diff options
111 files changed, 9324 insertions, 1516 deletions
@@ -399,7 +399,7 @@ endif $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \ done for d in $(TARGET_DIRS); do \ - $(MAKE) -C $$d $@ || exit 1 ; \ + $(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \ done # various test targets diff --git a/backends/baum.c b/backends/baum.c index 1132899026..665107fa9c 100644 --- a/backends/baum.c +++ b/backends/baum.c @@ -566,7 +566,7 @@ CharDriverState *chr_baum_init(void) BaumDriverState *baum; CharDriverState *chr; brlapi_handle_t *handle; -#ifdef CONFIG_SDL +#if defined(CONFIG_SDL) && SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) SDL_SysWMinfo info; #endif int tty; @@ -595,7 +595,7 @@ CharDriverState *chr_baum_init(void) goto fail; } -#ifdef CONFIG_SDL +#if defined(CONFIG_SDL) && SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) memset(&info, 0, sizeof(info)); SDL_VERSION(&info.version); if (SDL_GetWMInfo(&info)) @@ -14,13 +14,14 @@ fi TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c" TMPB="qemu-conf-${RANDOM}-$$-${RANDOM}" TMPO="${TMPDIR1}/${TMPB}.o" +TMPCXX="${TMPDIR1}/${TMPB}.cxx" TMPL="${TMPDIR1}/${TMPB}.lo" TMPA="${TMPDIR1}/lib${TMPB}.la" TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe" # NB: do not call "exit" in the trap handler; this is buggy with some shells; # see <1285349658-3122-1-git-send-email-loic.minier@linaro.org> -trap "rm -f $TMPC $TMPO $TMPE" EXIT INT QUIT TERM +trap "rm -f $TMPC $TMPO $TMPCXX $TMPE" EXIT INT QUIT TERM rm -f config.log # Print a helpful header at the top of config.log @@ -54,10 +55,13 @@ error_exit() { exit 1 } -do_cc() { - # Run the compiler, capturing its output to the log. - echo $cc "$@" >> config.log - $cc "$@" >> config.log 2>&1 || return $? +do_compiler() { + # Run the compiler, capturing its output to the log. First argument + # is compiler binary to execute. + local compiler="$1" + shift + echo $compiler "$@" >> config.log + $compiler "$@" >> config.log 2>&1 || return $? # Test passed. If this is an --enable-werror build, rerun # the test with -Werror and bail out if it fails. This # makes warning-generating-errors in configure test code @@ -71,14 +75,39 @@ do_cc() { return 0 ;; esac - echo $cc -Werror "$@" >> config.log - $cc -Werror "$@" >> config.log 2>&1 && return $? + echo $compiler -Werror "$@" >> config.log + $compiler -Werror "$@" >> config.log 2>&1 && return $? error_exit "configure test passed without -Werror but failed with -Werror." \ "This is probably a bug in the configure script. The failing command" \ "will be at the bottom of config.log." \ "You can run configure with --disable-werror to bypass this check." } +do_cc() { + do_compiler "$cc" "$@" +} + +do_cxx() { + do_compiler "$cxx" "$@" +} + +update_cxxflags() { + # Set QEMU_CXXFLAGS from QEMU_CFLAGS by filtering out those + # options which some versions of GCC's C++ compiler complain about + # because they only make sense for C programs. + QEMU_CXXFLAGS= + for arg in $QEMU_CFLAGS; do + case $arg in + -Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\ + -Wold-style-declaration|-Wold-style-definition|-Wredundant-decls) + ;; + *) + QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }$arg + ;; + esac + done +} + compile_object() { do_cc $QEMU_CFLAGS -c -o $TMPO $TMPC } @@ -207,6 +236,7 @@ fdt="" netmap="no" pixman="" sdl="" +sdlabi="1.2" virtfs="" vnc="yes" sparse="no" @@ -366,12 +396,13 @@ query_pkg_config() { } pkg_config=query_pkg_config sdl_config="${SDL_CONFIG-${cross_prefix}sdl-config}" +sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" # If the user hasn't specified ARFLAGS, default to 'rv', just as make does. ARFLAGS="${ARFLAGS-rv}" # default flags for all hosts -QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS" +QEMU_CFLAGS="-fno-strict-aliasing -fno-common $QEMU_CFLAGS" QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" @@ -775,6 +806,8 @@ for opt do ;; --enable-sdl) sdl="yes" ;; + --with-sdlabi=*) sdlabi="$optarg" + ;; --disable-qom-cast-debug) qom_cast_debug="no" ;; --enable-qom-cast-debug) qom_cast_debug="yes" @@ -1196,6 +1229,7 @@ Advanced options (experts only): --disable-werror disable compilation abort on warning --disable-sdl disable SDL --enable-sdl enable SDL + --with-sdlabi select preferred SDL ABI 1.2 or 2.0 --disable-gtk disable gtk UI --enable-gtk enable gtk UI --disable-virtfs disable VirtFS @@ -1335,6 +1369,19 @@ if test "$ARCH" = "unknown"; then fi fi +# Consult white-list to determine whether to enable werror +# by default. Only enable by default for git builds +z_version=`cut -f3 -d. $source_path/VERSION` + +if test -z "$werror" ; then + if test -d "$source_path/.git" -a \ + "$linux" = "yes" ; then + werror="yes" + else + werror="no" + fi +fi + # check that the C compiler works. cat > $TMPC <<EOF int main(void) { return 0; } @@ -1355,14 +1402,16 @@ EOF compile_object - cat > $TMPC <<EOF + cat > $TMPCXX <<EOF extern "C" { int c_function(void); } int c_function(void) { return 42; } EOF - if (cc=$cxx do_cc $QEMU_CFLAGS -o $TMPE $TMPC $TMPO $LDFLAGS); then + update_cxxflags + + if do_cxx $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $LDFLAGS; then # C++ compiler $cxx works ok with C compiler $cc : else @@ -1375,19 +1424,6 @@ else cxx= fi -# Consult white-list to determine whether to enable werror -# by default. Only enable by default for git builds -z_version=`cut -f3 -d. $source_path/VERSION` - -if test -z "$werror" ; then - if test -d "$source_path/.git" -a \ - "$linux" = "yes" ; then - werror="yes" - else - werror="no" - fi -fi - gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits" gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags" gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" @@ -1955,12 +1991,22 @@ fi # Look for sdl configuration program (pkg-config or sdl-config). Try # sdl-config even without cross prefix, and favour pkg-config over sdl-config. -if test "`basename $sdl_config`" != sdl-config && ! has ${sdl_config}; then - sdl_config=sdl-config + +if test $sdlabi = "2.0"; then + sdl_config=$sdl2_config + sdlname=sdl2 + sdlconfigname=sdl2_config +else + sdlname=sdl + sdlconfigname=sdl_config +fi + +if test "`basename $sdl_config`" != $sdlconfigname && ! has ${sdl_config}; then + sdl_config=$sdlconfigname fi -if $pkg_config sdl --exists; then - sdlconfig="$pkg_config sdl" +if $pkg_config $sdlname --exists; then + sdlconfig="$pkg_config $sdlname" _sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'` elif has ${sdl_config}; then sdlconfig="$sdl_config" diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index e5f9d36913..07c51ced1f 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -47,4 +47,5 @@ CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) # For PReP CONFIG_MC146818RTC=y +CONFIG_ETSEC=y CONFIG_ISA_TESTDEV=y diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index cce7127598..d10b5dbb49 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -630,7 +630,7 @@ static int musicpal_lcd_init(SysBusDevice *sbd) "musicpal-lcd", MP_LCD_SIZE); sysbus_init_mmio(sbd, &s->iomem); - s->con = graphic_console_init(dev, &musicpal_gfx_ops, s); + s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); qemu_console_resize(s->con, 128*3, 64*3); qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); diff --git a/hw/core/loader.c b/hw/core/loader.c index e1c3f3a860..b323c0c7b8 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -284,12 +284,30 @@ static void *load_at(int fd, int offset, int size) #define SZ 64 #include "hw/elf_ops.h" +const char *load_elf_strerror(int error) +{ + switch (error) { + case 0: + return "No error"; + case ELF_LOAD_FAILED: + return "Failed to load ELF"; + case ELF_LOAD_NOT_ELF: + return "The image is not ELF"; + case ELF_LOAD_WRONG_ARCH: + return "The image is from incompatible architecture"; + case ELF_LOAD_WRONG_ENDIAN: + return "The image has incorrect endianness"; + default: + return "Unknown error"; + } +} + /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb) { - int fd, data_order, target_data_order, must_swab, ret; + int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; fd = open(filename, O_RDONLY | O_BINARY); @@ -302,8 +320,10 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), if (e_ident[0] != ELFMAG0 || e_ident[1] != ELFMAG1 || e_ident[2] != ELFMAG2 || - e_ident[3] != ELFMAG3) + e_ident[3] != ELFMAG3) { + ret = ELF_LOAD_NOT_ELF; goto fail; + } #ifdef HOST_WORDS_BIGENDIAN data_order = ELFDATA2MSB; #else @@ -317,6 +337,7 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), } if (target_data_order != e_ident[EI_DATA]) { + ret = ELF_LOAD_WRONG_ENDIAN; goto fail; } @@ -329,12 +350,9 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), pentry, lowaddr, highaddr, elf_machine, clear_lsb); } - close(fd); - return ret; - fail: close(fd); - return -1; + return ret; } static void bswap_uboot_header(uboot_image_header_t *hdr) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index c0b857fbd4..380976a066 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -440,27 +440,33 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id) static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) { const char *typename = object_get_typename(OBJECT(bus)); + BusClass *bc; char *buf; - int i,len; + int i, len, bus_id; bus->parent = parent; if (name) { bus->name = g_strdup(name); } else if (bus->parent && bus->parent->id) { - /* parent device has id -> use it for bus name */ + /* parent device has id -> use it plus parent-bus-id for bus name */ + bus_id = bus->parent->num_child_bus; + len = strlen(bus->parent->id) + 16; buf = g_malloc(len); - snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus); + snprintf(buf, len, "%s.%d", bus->parent->id, bus_id); bus->name = buf; } else { - /* no id -> use lowercase bus type for bus name */ + /* no id -> use lowercase bus type plus global bus-id for bus name */ + bc = BUS_GET_CLASS(bus); + bus_id = bc->automatic_ids++; + len = strlen(typename) + 16; buf = g_malloc(len); - len = snprintf(buf, len, "%s.%d", typename, - bus->parent ? bus->parent->num_child_bus : 0); - for (i = 0; i < len; i++) + len = snprintf(buf, len, "%s.%d", typename, bus_id); + for (i = 0; i < len; i++) { buf[i] = qemu_tolower(buf[i]); + } bus->name = buf; } diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c index 4a466c8323..55c0ddf00b 100644 --- a/hw/display/blizzard.c +++ b/hw/display/blizzard.c @@ -956,7 +956,7 @@ void *s1d13745_init(qemu_irq gpio_int) s->fb = g_malloc(0x180000); - s->con = graphic_console_init(NULL, &blizzard_ops, s); + s->con = graphic_console_init(NULL, 0, &blizzard_ops, s); surface = qemu_console_surface(s->con); switch (surface_bits_per_pixel(surface)) { diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 6db8ca362a..a042b9ecbe 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -306,7 +306,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); - s->con = graphic_console_init(DEVICE(dev), &cg3_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &cg3_ops, s); qemu_console_resize(s->con, s->width, s->height); } diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 3a8fc0bf8e..0d3127da21 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -2917,7 +2917,7 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, isa_address_space(isadev), isa_address_space_io(isadev)); - s->con = graphic_console_init(dev, s->hw_ops, s); + s->con = graphic_console_init(dev, 0, s->hw_ops, s); rom_add_vga(VGABIOS_CIRRUS_FILENAME); /* XXX ISA-LFB support */ /* FIXME not qdev yet */ @@ -2963,7 +2963,7 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev) vga_common_init(&s->vga, OBJECT(dev)); cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev), pci_address_space_io(dev)); - s->vga.con = graphic_console_init(DEVICE(dev), s->vga.hw_ops, &s->vga); + s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga); /* setup PCI */ diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 65cca1d707..9750330c25 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1917,7 +1917,7 @@ static int exynos4210_fimd_init(SysBusDevice *dev) memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_fimd_mmio_ops, s, "exynos4210.fimd", FIMD_REGS_SIZE); sysbus_init_mmio(dev, &s->iomem); - s->console = graphic_console_init(DEVICE(dev), &exynos4210_fimd_ops, s); + s->console = graphic_console_init(DEVICE(dev), 0, &exynos4210_fimd_ops, s); return 0; } diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index bc909bb3de..5c6a2d3605 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -484,7 +484,7 @@ static void g364fb_init(DeviceState *dev, G364State *s) { s->vram = g_malloc0(s->vram_size); - s->con = graphic_console_init(dev, &g364fb_ops, s); + s->con = graphic_console_init(dev, 0, &g364fb_ops, s); memory_region_init_io(&s->mem_ctrl, NULL, &g364fb_ctrl_ops, s, "ctrl", 0x180000); memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram", diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c index 8407e6c2ef..f9e7d7c981 100644 --- a/hw/display/jazz_led.c +++ b/hw/display/jazz_led.c @@ -271,7 +271,7 @@ static int jazz_led_init(SysBusDevice *dev) memory_region_init_io(&s->iomem, OBJECT(s), &led_ops, s, "led", 1); sysbus_init_mmio(dev, &s->iomem); - s->con = graphic_console_init(DEVICE(dev), &jazz_led_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &jazz_led_ops, s); return 0; } diff --git a/hw/display/milkymist-vgafb.c b/hw/display/milkymist-vgafb.c index 5150cb48b7..603537aabb 100644 --- a/hw/display/milkymist-vgafb.c +++ b/hw/display/milkymist-vgafb.c @@ -290,7 +290,7 @@ static int milkymist_vgafb_init(SysBusDevice *dev) "milkymist-vgafb", R_MAX * 4); sysbus_init_mmio(dev, &s->regs_region); - s->con = graphic_console_init(DEVICE(dev), &vgafb_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &vgafb_ops, s); return 0; } diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c index c3b9b68971..fda81baff0 100644 --- a/hw/display/omap_lcdc.c +++ b/hw/display/omap_lcdc.c @@ -406,7 +406,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100); memory_region_add_subregion(sysmem, base, &s->iomem); - s->con = graphic_console_init(NULL, &omap_ops, s); + s->con = graphic_console_init(NULL, 0, &omap_ops, s); return s; } diff --git a/hw/display/pl110.c b/hw/display/pl110.c index ab689e9aae..c574cf1a81 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -464,7 +464,7 @@ static int pl110_initfn(SysBusDevice *sbd) sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); - s->con = graphic_console_init(dev, &pl110_gfx_ops, s); + s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); return 0; } diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c index 990931ae45..09cdf17ab9 100644 --- a/hw/display/pxa2xx_lcd.c +++ b/hw/display/pxa2xx_lcd.c @@ -1013,7 +1013,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, "pxa2xx-lcd-controller", 0x00100000); memory_region_add_subregion(sysmem, base, &s->iomem); - s->con = graphic_console_init(NULL, &pxa2xx_ops, s); + s->con = graphic_console_init(NULL, 0, &pxa2xx_ops, s); surface = qemu_console_surface(s->con); switch (surface_bits_per_pixel(surface)) { diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 2a559ebcc9..47bbf1f1fe 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2069,7 +2069,7 @@ static int qxl_init_primary(PCIDevice *dev) portio_list_set_flush_coalesced(qxl_vga_port_list); portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0); - vga->con = graphic_console_init(DEVICE(dev), &qxl_ops, qxl); + vga->con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); qemu_spice_display_init_common(&qxl->ssd); rc = qxl_init_common(qxl); @@ -2094,7 +2094,7 @@ static int qxl_init_secondary(PCIDevice *dev) qxl->vga.vram_size); vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev); qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); - qxl->vga.con = graphic_console_init(DEVICE(dev), &qxl_ops, qxl); + qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); return qxl_init_common(qxl); } diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 0b5f993594..eedf2d48e0 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1449,5 +1449,5 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base, } /* create qemu graphic console */ - s->con = graphic_console_init(DEVICE(dev), &sm501_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &sm501_ops, s); } diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index 89804e108b..c2eea04934 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -299,7 +299,7 @@ static int ssd0303_init(I2CSlave *i2c) { ssd0303_state *s = SSD0303(i2c); - s->con = graphic_console_init(DEVICE(i2c), &ssd0303_ops, s); + s->con = graphic_console_init(DEVICE(i2c), 0, &ssd0303_ops, s); qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY); return 0; } diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index c3231c6116..46c3b40c79 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -342,7 +342,7 @@ static int ssd0323_init(SSISlave *dev) s->col_end = 63; s->row_end = 79; - s->con = graphic_console_init(DEVICE(dev), &ssd0323_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &ssd0323_ops, s); qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY); qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1); diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 3dd9b98eca..f4011d2db0 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -587,7 +587,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); s->scr_width = 480; s->scr_height = 640; - s->con = graphic_console_init(NULL, &tc6393xb_gfx_ops, s); + s->con = graphic_console_init(NULL, 0, &tc6393xb_gfx_ops, s); return s; } diff --git a/hw/display/tcx.c b/hw/display/tcx.c index e60769c2c9..2b37ffac4c 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -602,14 +602,14 @@ static int tcx_init1(SysBusDevice *dev) &s->vram_mem, vram_offset, size); sysbus_init_mmio(dev, &s->vram_cplane); - s->con = graphic_console_init(DEVICE(dev), &tcx24_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s); } else { /* THC 8 bit (dummy) */ memory_region_init_io(&s->thc8, OBJECT(s), &dummy_ops, s, "tcx.thc8", TCX_THC_NREGS_8); sysbus_init_mmio(dev, &s->thc8); - s->con = graphic_console_init(DEVICE(dev), &tcx_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s); } qemu_console_resize(s->con, s->width, s->height); diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c index 8b514cc39d..afc46b8c9d 100644 --- a/hw/display/vga-isa-mm.c +++ b/hw/display/vga-isa-mm.c @@ -135,7 +135,7 @@ int isa_vga_mm_init(hwaddr vram_base, vga_common_init(&s->vga, NULL); vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space); - s->vga.con = graphic_console_init(NULL, s->vga.hw_ops, s); + s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s); vga_init_vbe(&s->vga, NULL, address_space); return 0; diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index c2a19ad6ba..1d9ea6b51d 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -67,7 +67,7 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) isa_mem_base + 0x000a0000, vga_io_memory, 1); memory_region_set_coalescing(vga_io_memory); - s->con = graphic_console_init(DEVICE(dev), s->hw_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); vga_init_vbe(s, OBJECT(dev), isa_address_space(isadev)); /* ROM BIOS */ diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index f74fc43aa6..574ea0e7f9 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -151,7 +151,7 @@ static int pci_std_vga_initfn(PCIDevice *dev) vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), true); - s->con = graphic_console_init(DEVICE(dev), s->hw_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* XXX: VGA_RAM_SIZE must be a power of two */ pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 334e71856e..bd2c108c42 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1199,7 +1199,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, s->scratch_size = SVGA_SCRATCH_SIZE; s->scratch = g_malloc(s->scratch_size * 4); - s->vga.con = graphic_console_init(dev, &vmsvga_ops, s); + s->vga.con = graphic_console_init(dev, 0, &vmsvga_ops, s); s->fifo_size = SVGA_FIFO_SIZE; memory_region_init_ram(&s->fifo_ram, NULL, "vmsvga.fifo", s->fifo_size); diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index cb9d456814..032eb7a9a5 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -992,7 +992,7 @@ wait_more: /* vfb */ fb = container_of(xfb, struct XenFB, c.xendev); - fb->c.con = graphic_console_init(NULL, &xenfb_ops, fb); + fb->c.con = graphic_console_init(NULL, 0, &xenfb_ops, fb); fb->have_console = 1; /* vkbd */ diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d5dc1ef336..ae1699d6db 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -221,10 +221,16 @@ static void pc_init1(QEMUMachineInitArgs *args, } else { for(i = 0; i < MAX_IDE_BUS; i++) { ISADevice *dev; + char busname[] = "ide.0"; dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - idebus[i] = qdev_get_child_bus(DEVICE(dev), "ide.0"); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); } } diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index c7f7b8406c..87fdb126cf 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -228,7 +228,7 @@ int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) encap.cap = KVM_CAP_IRQ_MPIC; encap.args[0] = opp->fd; - encap.args[1] = cs->cpu_index; + encap.args[1] = kvm_arch_vcpu_id(cs); return kvm_vcpu_ioctl(cs, KVM_ENABLE_CAP, &encap); } diff --git a/hw/intc/xics.c b/hw/intc/xics.c index b437563fb9..64aabe753d 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -33,6 +33,17 @@ #include "qemu/error-report.h" #include "qapi/visitor.h" +static int get_cpu_index_by_dt_id(int cpu_dt_id) +{ + PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); + + if (cpu) { + return cpu->parent_obj.cpu_index; + } + + return -1; +} + void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -659,7 +670,7 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { - target_ulong server = args[0]; + target_ulong server = get_cpu_index_by_dt_id(args[0]); target_ulong mfrr = args[1]; if (server >= spapr->icp->nr_servers) { @@ -728,7 +739,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, } nr = rtas_ld(args, 0); - server = rtas_ld(args, 1); + server = get_cpu_index_by_dt_id(rtas_ld(args, 1)); priority = rtas_ld(args, 2); if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index c203646bd6..a5bbc2406d 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -65,7 +65,7 @@ static void icp_get_kvm_state(ICPState *ss) ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, ®); if (ret != 0) { error_report("Unable to retrieve KVM interrupt controller state" - " for CPU %d: %s", ss->cs->cpu_index, strerror(errno)); + " for CPU %ld: %s", kvm_arch_vcpu_id(ss->cs), strerror(errno)); exit(1); } @@ -97,7 +97,7 @@ static int icp_set_kvm_state(ICPState *ss, int version_id) ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, ®); if (ret != 0) { error_report("Unable to restore KVM interrupt controller state (0x%" - PRIx64 ") for CPU %d: %s", state, ss->cs->cpu_index, + PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(ss->cs), strerror(errno)); return ret; } @@ -325,15 +325,15 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) struct kvm_enable_cap xics_enable_cap = { .cap = KVM_CAP_IRQ_XICS, .flags = 0, - .args = {icpkvm->kernel_xics_fd, cs->cpu_index, 0, 0}, + .args = {icpkvm->kernel_xics_fd, kvm_arch_vcpu_id(cs), 0, 0}, }; ss->cs = cs; ret = kvm_vcpu_ioctl(ss->cs, KVM_ENABLE_CAP, &xics_enable_cap); if (ret < 0) { - error_report("Unable to connect CPU%d to kernel XICS: %s", - cs->cpu_index, strerror(errno)); + error_report("Unable to connect CPU%ld to kernel XICS: %s", + kvm_arch_vcpu_id(cs), strerror(errno)); exit(1); } } diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index ef4f3a84d7..a87ca6ddcc 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -55,7 +55,7 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) &entry, &kernel_low, &kernel_high, 1, ELF_MACHINE, 0); - if (!kernel_size) { + if (kernel_size <= 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loader_params->kernel_filename); exit(1); diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 75e80c2c48..ea93293122 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -32,3 +32,6 @@ obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o obj-$(CONFIG_VIRTIO) += virtio-net.o obj-y += vhost_net.o + +obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ + fsl_etsec/rings.o fsl_etsec/miim.o diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c new file mode 100644 index 0000000000..d4b4429446 --- /dev/null +++ b/hw/net/fsl_etsec/etsec.c @@ -0,0 +1,465 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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. + */ + +/* + * This implementation doesn't include ring priority, TCP/IP Off-Load, QoS. + */ + +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "hw/ptimer.h" +#include "etsec.h" +#include "registers.h" + +/* #define HEX_DUMP */ +/* #define DEBUG_REGISTER */ + +#ifdef DEBUG_REGISTER +static const int debug_etsec = 1; +#else +static const int debug_etsec; +#endif + +#define DPRINTF(fmt, ...) do { \ + if (debug_etsec) { \ + qemu_log(fmt , ## __VA_ARGS__); \ + } \ + } while (0) + +static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size) +{ + eTSEC *etsec = opaque; + uint32_t reg_index = addr / 4; + eTSEC_Register *reg = NULL; + uint32_t ret = 0x0; + + assert(reg_index < ETSEC_REG_NUMBER); + + reg = &etsec->regs[reg_index]; + + + switch (reg->access) { + case ACC_WO: + ret = 0x00000000; + break; + + case ACC_RW: + case ACC_W1C: + case ACC_RO: + default: + ret = reg->value; + break; + } + + DPRINTF("Read 0x%08x @ 0x" TARGET_FMT_plx + " : %s (%s)\n", + ret, addr, reg->name, reg->desc); + + return ret; +} + +static void write_tstat(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + int i = 0; + + for (i = 0; i < 8; i++) { + /* Check THLTi flag in TSTAT */ + if (value & (1 << (31 - i))) { + etsec_walk_tx_ring(etsec, i); + } + } + + /* Write 1 to clear */ + reg->value &= ~value; +} + +static void write_rstat(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + int i = 0; + + for (i = 0; i < 8; i++) { + /* Check QHLTi flag in RSTAT */ + if (value & (1 << (23 - i)) && !(reg->value & (1 << (23 - i)))) { + etsec_walk_rx_ring(etsec, i); + } + } + + /* Write 1 to clear */ + reg->value &= ~value; +} + +static void write_tbasex(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value & ~0x7; + + /* Copy this value in the ring's TxBD pointer */ + etsec->regs[TBPTR0 + (reg_index - TBASE0)].value = value & ~0x7; +} + +static void write_rbasex(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value & ~0x7; + + /* Copy this value in the ring's RxBD pointer */ + etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7; +} + +static void write_ievent(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + /* Write 1 to clear */ + reg->value &= ~value; + + if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) { + qemu_irq_lower(etsec->tx_irq); + } + if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) { + qemu_irq_lower(etsec->rx_irq); + } + + if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | + IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | + IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | + IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | + IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | + IEVENT_MMRW))) { + qemu_irq_lower(etsec->err_irq); + } +} + +static void write_dmactrl(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value; + + if (value & DMACTRL_GRS) { + + if (etsec->rx_buffer_len != 0) { + /* Graceful receive stop delayed until end of frame */ + } else { + /* Graceful receive stop now */ + etsec->regs[IEVENT].value |= IEVENT_GRSC; + if (etsec->regs[IMASK].value & IMASK_GRSCEN) { + qemu_irq_raise(etsec->err_irq); + } + } + } + + if (value & DMACTRL_GTS) { + + if (etsec->tx_buffer_len != 0) { + /* Graceful transmit stop delayed until end of frame */ + } else { + /* Graceful transmit stop now */ + etsec->regs[IEVENT].value |= IEVENT_GTSC; + if (etsec->regs[IMASK].value & IMASK_GTSCEN) { + qemu_irq_raise(etsec->err_irq); + } + } + } + + if (!(value & DMACTRL_WOP)) { + /* Start polling */ + ptimer_stop(etsec->ptimer); + ptimer_set_count(etsec->ptimer, 1); + ptimer_run(etsec->ptimer, 1); + } +} + +static void etsec_write(void *opaque, + hwaddr addr, + uint64_t value, + unsigned size) +{ + eTSEC *etsec = opaque; + uint32_t reg_index = addr / 4; + eTSEC_Register *reg = NULL; + uint32_t before = 0x0; + + assert(reg_index < ETSEC_REG_NUMBER); + + reg = &etsec->regs[reg_index]; + before = reg->value; + + switch (reg_index) { + case IEVENT: + write_ievent(etsec, reg, reg_index, value); + break; + + case DMACTRL: + write_dmactrl(etsec, reg, reg_index, value); + break; + + case TSTAT: + write_tstat(etsec, reg, reg_index, value); + break; + + case RSTAT: + write_rstat(etsec, reg, reg_index, value); + break; + + case TBASE0 ... TBASE7: + write_tbasex(etsec, reg, reg_index, value); + break; + + case RBASE0 ... RBASE7: + write_rbasex(etsec, reg, reg_index, value); + break; + + case MIIMCFG ... MIIMIND: + etsec_write_miim(etsec, reg, reg_index, value); + break; + + default: + /* Default handling */ + switch (reg->access) { + + case ACC_RW: + case ACC_WO: + reg->value = value; + break; + + case ACC_W1C: + reg->value &= ~value; + break; + + case ACC_RO: + default: + /* Read Only or Unknown register */ + break; + } + } + + DPRINTF("Write 0x%08x @ 0x" TARGET_FMT_plx + " val:0x%08x->0x%08x : %s (%s)\n", + (unsigned int)value, addr, before, reg->value, + reg->name, reg->desc); +} + +static const MemoryRegionOps etsec_ops = { + .read = etsec_read, + .write = etsec_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void etsec_timer_hit(void *opaque) +{ + eTSEC *etsec = opaque; + + ptimer_stop(etsec->ptimer); + + if (!(etsec->regs[DMACTRL].value & DMACTRL_WOP)) { + + if (!(etsec->regs[DMACTRL].value & DMACTRL_GTS)) { + etsec_walk_tx_ring(etsec, 0); + } + ptimer_set_count(etsec->ptimer, 1); + ptimer_run(etsec->ptimer, 1); + } +} + +static void etsec_reset(DeviceState *d) +{ + eTSEC *etsec = ETSEC_COMMON(d); + int i = 0; + int reg_index = 0; + + /* Default value for all registers */ + for (i = 0; i < ETSEC_REG_NUMBER; i++) { + etsec->regs[i].name = "Reserved"; + etsec->regs[i].desc = ""; + etsec->regs[i].access = ACC_UNKNOWN; + etsec->regs[i].value = 0x00000000; + } + + /* Set-up known registers */ + for (i = 0; eTSEC_registers_def[i].name != NULL; i++) { + + reg_index = eTSEC_registers_def[i].offset / 4; + + etsec->regs[reg_index].name = eTSEC_registers_def[i].name; + etsec->regs[reg_index].desc = eTSEC_registers_def[i].desc; + etsec->regs[reg_index].access = eTSEC_registers_def[i].access; + etsec->regs[reg_index].value = eTSEC_registers_def[i].reset; + } + + etsec->tx_buffer = NULL; + etsec->tx_buffer_len = 0; + etsec->rx_buffer = NULL; + etsec->rx_buffer_len = 0; + + etsec->phy_status = + MII_SR_EXTENDED_CAPS | MII_SR_LINK_STATUS | MII_SR_AUTONEG_CAPS | + MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS | + MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS | + MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS | + MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; +} + +static void etsec_cleanup(NetClientState *nc) +{ + /* qemu_log("eTSEC cleanup\n"); */ +} + +static int etsec_can_receive(NetClientState *nc) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + + return etsec->rx_buffer_len == 0; +} + +static ssize_t etsec_receive(NetClientState *nc, + const uint8_t *buf, + size_t size) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + +#if defined(HEX_DUMP) + fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size); + qemu_hexdump(buf, stderr, "", size); +#endif + etsec_rx_ring_write(etsec, buf, size); + return size; +} + + +static void etsec_set_link_status(NetClientState *nc) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + + etsec_miim_link_status(etsec, nc); +} + +static NetClientInfo net_etsec_info = { + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = etsec_can_receive, + .receive = etsec_receive, + .cleanup = etsec_cleanup, + .link_status_changed = etsec_set_link_status, +}; + +static void etsec_realize(DeviceState *dev, Error **errp) +{ + eTSEC *etsec = ETSEC_COMMON(dev); + + etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf, + object_get_typename(OBJECT(dev)), dev->id, etsec); + qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a); + + + etsec->bh = qemu_bh_new(etsec_timer_hit, etsec); + etsec->ptimer = ptimer_init(etsec->bh); + ptimer_set_freq(etsec->ptimer, 100); +} + +static void etsec_instance_init(Object *obj) +{ + eTSEC *etsec = ETSEC_COMMON(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec, + "eTSEC", 0x1000); + sysbus_init_mmio(sbd, &etsec->io_area); + + sysbus_init_irq(sbd, &etsec->tx_irq); + sysbus_init_irq(sbd, &etsec->rx_irq); + sysbus_init_irq(sbd, &etsec->err_irq); +} + +static Property etsec_properties[] = { + DEFINE_NIC_PROPERTIES(eTSEC, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void etsec_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = etsec_realize; + dc->reset = etsec_reset; + dc->props = etsec_properties; +} + +static TypeInfo etsec_info = { + .name = "eTSEC", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(eTSEC), + .class_init = etsec_class_init, + .instance_init = etsec_instance_init, +}; + +static void etsec_register_types(void) +{ + type_register_static(&etsec_info); +} + +type_init(etsec_register_types) + +DeviceState *etsec_create(hwaddr base, + MemoryRegion * mr, + NICInfo * nd, + qemu_irq tx_irq, + qemu_irq rx_irq, + qemu_irq err_irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "eTSEC"); + qdev_set_nic_properties(dev, nd); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq); + + memory_region_add_subregion(mr, base, + SYS_BUS_DEVICE(dev)->mmio[0].memory); + + return dev; +} diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h new file mode 100644 index 0000000000..78d2c57ed3 --- /dev/null +++ b/hw/net/fsl_etsec/etsec.h @@ -0,0 +1,174 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 _ETSEC_H_ +#define _ETSEC_H_ + +#include "hw/qdev.h" +#include "hw/sysbus.h" +#include "net/net.h" +#include "hw/ptimer.h" + +/* Buffer Descriptors */ + +typedef struct eTSEC_rxtx_bd { + uint16_t flags; + uint16_t length; + uint32_t bufptr; +} eTSEC_rxtx_bd; + +#define BD_WRAP (1 << 13) +#define BD_INTERRUPT (1 << 12) +#define BD_LAST (1 << 11) + +#define BD_TX_READY (1 << 15) +#define BD_TX_PADCRC (1 << 14) +#define BD_TX_TC (1 << 10) +#define BD_TX_PREDEF (1 << 9) +#define BD_TX_HFELC (1 << 7) +#define BD_TX_CFRL (1 << 6) +#define BD_TX_RC_MASK 0xF +#define BD_TX_RC_OFFSET 0x2 +#define BD_TX_TOEUN (1 << 1) +#define BD_TX_TR (1 << 0) + +#define BD_RX_EMPTY (1 << 15) +#define BD_RX_RO1 (1 << 14) +#define BD_RX_FIRST (1 << 10) +#define BD_RX_MISS (1 << 8) +#define BD_RX_BROADCAST (1 << 7) +#define BD_RX_MULTICAST (1 << 6) +#define BD_RX_LG (1 << 5) +#define BD_RX_NO (1 << 4) +#define BD_RX_SH (1 << 3) +#define BD_RX_CR (1 << 2) +#define BD_RX_OV (1 << 1) +#define BD_RX_TR (1 << 0) + +/* Tx FCB flags */ +#define FCB_TX_VLN (1 << 7) +#define FCB_TX_IP (1 << 6) +#define FCB_TX_IP6 (1 << 5) +#define FCB_TX_TUP (1 << 4) +#define FCB_TX_UDP (1 << 3) +#define FCB_TX_CIP (1 << 2) +#define FCB_TX_CTU (1 << 1) +#define FCB_TX_NPH (1 << 0) + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* eTSEC */ + +/* Number of register in the device */ +#define ETSEC_REG_NUMBER 1024 + +typedef struct eTSEC_Register { + const char *name; + const char *desc; + uint32_t access; + uint32_t value; +} eTSEC_Register; + +typedef struct eTSEC { + SysBusDevice busdev; + + MemoryRegion io_area; + + eTSEC_Register regs[ETSEC_REG_NUMBER]; + + NICState *nic; + NICConf conf; + + /* Tx */ + + uint8_t *tx_buffer; + uint32_t tx_buffer_len; + eTSEC_rxtx_bd first_bd; + + /* Rx */ + + uint8_t *rx_buffer; + uint32_t rx_buffer_len; + uint32_t rx_remaining_data; + uint8_t rx_first_in_frame; + uint8_t rx_fcb_size; + eTSEC_rxtx_bd rx_first_bd; + uint8_t rx_fcb[10]; + uint32_t rx_padding; + + /* IRQs */ + qemu_irq tx_irq; + qemu_irq rx_irq; + qemu_irq err_irq; + + + uint16_t phy_status; + uint16_t phy_control; + + /* Polling */ + QEMUBH *bh; + struct ptimer_state *ptimer; + +} eTSEC; + +#define TYPE_ETSEC_COMMON "eTSEC" +#define ETSEC_COMMON(obj) \ + OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON) + +#define eTSEC_TRANSMIT 1 +#define eTSEC_RECEIVE 2 + +DeviceState *etsec_create(hwaddr base, + MemoryRegion *mr, + NICInfo *nd, + qemu_irq tx_irq, + qemu_irq rx_irq, + qemu_irq err_irq); + +void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr); +void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr); +void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size); + +void etsec_write_miim(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value); + +void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc); + +#endif /* ! _ETSEC_H_ */ diff --git a/hw/net/fsl_etsec/miim.c b/hw/net/fsl_etsec/miim.c new file mode 100644 index 0000000000..1931b74e6c --- /dev/null +++ b/hw/net/fsl_etsec/miim.c @@ -0,0 +1,146 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 "etsec.h" +#include "registers.h" + +/* #define DEBUG_MIIM */ + +#define MIIM_CONTROL 0 +#define MIIM_STATUS 1 +#define MIIM_PHY_ID_1 2 +#define MIIM_PHY_ID_2 3 +#define MIIM_T2_STATUS 10 +#define MIIM_EXT_STATUS 15 + +static void miim_read_cycle(eTSEC *etsec) +{ + uint8_t phy; + uint8_t addr; + uint16_t value; + + phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F; + (void)phy; /* Unreferenced */ + addr = etsec->regs[MIIMADD].value & 0x1F; + + switch (addr) { + case MIIM_CONTROL: + value = etsec->phy_control; + break; + case MIIM_STATUS: + value = etsec->phy_status; + break; + case MIIM_T2_STATUS: + value = 0x1800; /* Local and remote receivers OK */ + break; + default: + value = 0x0; + break; + }; + +#ifdef DEBUG_MIIM + qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); +#endif + + etsec->regs[MIIMSTAT].value = value; +} + +static void miim_write_cycle(eTSEC *etsec) +{ + uint8_t phy; + uint8_t addr; + uint16_t value; + + phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F; + (void)phy; /* Unreferenced */ + addr = etsec->regs[MIIMADD].value & 0x1F; + value = etsec->regs[MIIMCON].value & 0xffff; + +#ifdef DEBUG_MIIM + qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); +#endif + + switch (addr) { + case MIIM_CONTROL: + etsec->phy_control = value & ~(0x8100); + break; + default: + break; + }; +} + +void etsec_write_miim(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + + switch (reg_index) { + + case MIIMCOM: + /* Read and scan cycle */ + + if ((!(reg->value & MIIMCOM_READ)) && (value & MIIMCOM_READ)) { + /* Read */ + miim_read_cycle(etsec); + } + reg->value = value; + break; + + case MIIMCON: + reg->value = value & 0xffff; + miim_write_cycle(etsec); + break; + + default: + /* Default handling */ + switch (reg->access) { + + case ACC_RW: + case ACC_WO: + reg->value = value; + break; + + case ACC_W1C: + reg->value &= ~value; + break; + + case ACC_RO: + default: + /* Read Only or Unknown register */ + break; + } + } + +} + +void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc) +{ + /* Set link status */ + if (nc->link_down) { + etsec->phy_status &= ~MII_SR_LINK_STATUS; + } else { + etsec->phy_status |= MII_SR_LINK_STATUS; + } +} diff --git a/hw/net/fsl_etsec/registers.c b/hw/net/fsl_etsec/registers.c new file mode 100644 index 0000000000..a7bbfa113f --- /dev/null +++ b/hw/net/fsl_etsec/registers.c @@ -0,0 +1,295 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 "registers.h" + +const eTSEC_Register_Definition eTSEC_registers_def[] = { +{0x000, "TSEC_ID", "Controller ID register", ACC_RO, 0x01240000}, +{0x004, "TSEC_ID2", "Controller ID register 2", ACC_RO, 0x003000F0}, +{0x010, "IEVENT", "Interrupt event register", ACC_W1C, 0x00000000}, +{0x014, "IMASK", "Interrupt mask register", ACC_RW, 0x00000000}, +{0x018, "EDIS", "Error disabled register", ACC_RW, 0x00000000}, +{0x020, "ECNTRL", "Ethernet control register", ACC_RW, 0x00000040}, +{0x028, "PTV", "Pause time value register", ACC_RW, 0x00000000}, +{0x02C, "DMACTRL", "DMA control register", ACC_RW, 0x00000000}, +{0x030, "TBIPA", "TBI PHY address register", ACC_RW, 0x00000000}, + +/* eTSEC FIFO Control and Status Registers */ + +{0x058, "FIFO_RX_ALARM", "FIFO receive alarm start threshold register", ACC_RW, 0x00000040}, +{0x05C, "FIFO_RX_ALARM_SHUTOFF", "FIFO receive alarm shut-off threshold register", ACC_RW, 0x00000080}, +{0x08C, "FIFO_TX_THR", "FIFO transmit threshold register", ACC_RW, 0x00000080}, +{0x098, "FIFO_TX_STARVE", "FIFO transmit starve register", ACC_RW, 0x00000040}, +{0x09C, "FIFO_TX_STARVE_SHUTOFF", "FIFO transmit starve shut-off register", ACC_RW, 0x00000080}, + +/* eTSEC Transmit Control and Status Registers */ + +{0x100, "TCTRL", "Transmit control register", ACC_RW, 0x00000000}, +{0x104, "TSTAT", "Transmit status register", ACC_W1C, 0x00000000}, +{0x108, "DFVLAN", "Default VLAN control word", ACC_RW, 0x81000000}, +{0x110, "TXIC", "Transmit interrupt coalescing register", ACC_RW, 0x00000000}, +{0x114, "TQUEUE", "Transmit queue control register", ACC_RW, 0x00008000}, +{0x140, "TR03WT", "TxBD Rings 0-3 round-robin weightings", ACC_RW, 0x00000000}, +{0x144, "TR47WT", "TxBD Rings 4-7 round-robin weightings", ACC_RW, 0x00000000}, +{0x180, "TBDBPH", "Tx data buffer pointer high bits", ACC_RW, 0x00000000}, +{0x184, "TBPTR0", "TxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0x18C, "TBPTR1", "TxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0x194, "TBPTR2", "TxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0x19C, "TBPTR3", "TxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0x1A4, "TBPTR4", "TxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0x1AC, "TBPTR5", "TxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0x1B4, "TBPTR6", "TxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0x1BC, "TBPTR7", "TxBD pointer for ring 7", ACC_RW, 0x00000000}, +{0x200, "TBASEH", "TxBD base address high bits", ACC_RW, 0x00000000}, +{0x204, "TBASE0", "TxBD base address of ring 0", ACC_RW, 0x00000000}, +{0x20C, "TBASE1", "TxBD base address of ring 1", ACC_RW, 0x00000000}, +{0x214, "TBASE2", "TxBD base address of ring 2", ACC_RW, 0x00000000}, +{0x21C, "TBASE3", "TxBD base address of ring 3", ACC_RW, 0x00000000}, +{0x224, "TBASE4", "TxBD base address of ring 4", ACC_RW, 0x00000000}, +{0x22C, "TBASE5", "TxBD base address of ring 5", ACC_RW, 0x00000000}, +{0x234, "TBASE6", "TxBD base address of ring 6", ACC_RW, 0x00000000}, +{0x23C, "TBASE7", "TxBD base address of ring 7", ACC_RW, 0x00000000}, +{0x280, "TMR_TXTS1_ID", "Tx time stamp identification tag (set 1)", ACC_RO, 0x00000000}, +{0x284, "TMR_TXTS2_ID", "Tx time stamp identification tag (set 2)", ACC_RO, 0x00000000}, +{0x2C0, "TMR_TXTS1_H", "Tx time stamp high (set 1)", ACC_RO, 0x00000000}, +{0x2C4, "TMR_TXTS1_L", "Tx time stamp high (set 1)", ACC_RO, 0x00000000}, +{0x2C8, "TMR_TXTS2_H", "Tx time stamp high (set 2)", ACC_RO, 0x00000000}, +{0x2CC, "TMR_TXTS2_L", "Tx time stamp high (set 2)", ACC_RO, 0x00000000}, + +/* eTSEC Receive Control and Status Registers */ + +{0x300, "RCTRL", "Receive control register", ACC_RW, 0x00000000}, +{0x304, "RSTAT", "Receive status register", ACC_W1C, 0x00000000}, +{0x310, "RXIC", "Receive interrupt coalescing register", ACC_RW, 0x00000000}, +{0x314, "RQUEUE", "Receive queue control register.", ACC_RW, 0x00800080}, +{0x330, "RBIFX", "Receive bit field extract control register", ACC_RW, 0x00000000}, +{0x334, "RQFAR", "Receive queue filing table address register", ACC_RW, 0x00000000}, +{0x338, "RQFCR", "Receive queue filing table control register", ACC_RW, 0x00000000}, +{0x33C, "RQFPR", "Receive queue filing table property register", ACC_RW, 0x00000000}, +{0x340, "MRBLR", "Maximum receive buffer length register", ACC_RW, 0x00000000}, +{0x380, "RBDBPH", "Rx data buffer pointer high bits", ACC_RW, 0x00000000}, +{0x384, "RBPTR0", "RxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0x38C, "RBPTR1", "RxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0x394, "RBPTR2", "RxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0x39C, "RBPTR3", "RxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0x3A4, "RBPTR4", "RxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0x3AC, "RBPTR5", "RxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0x3B4, "RBPTR6", "RxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0x3BC, "RBPTR7", "RxBD pointer for ring 7", ACC_RW, 0x00000000}, +{0x400, "RBASEH", "RxBD base address high bits", ACC_RW, 0x00000000}, +{0x404, "RBASE0", "RxBD base address of ring 0", ACC_RW, 0x00000000}, +{0x40C, "RBASE1", "RxBD base address of ring 1", ACC_RW, 0x00000000}, +{0x414, "RBASE2", "RxBD base address of ring 2", ACC_RW, 0x00000000}, +{0x41C, "RBASE3", "RxBD base address of ring 3", ACC_RW, 0x00000000}, +{0x424, "RBASE4", "RxBD base address of ring 4", ACC_RW, 0x00000000}, +{0x42C, "RBASE5", "RxBD base address of ring 5", ACC_RW, 0x00000000}, +{0x434, "RBASE6", "RxBD base address of ring 6", ACC_RW, 0x00000000}, +{0x43C, "RBASE7", "RxBD base address of ring 7", ACC_RW, 0x00000000}, +{0x4C0, "TMR_RXTS_H", "Rx timer time stamp register high", ACC_RW, 0x00000000}, +{0x4C4, "TMR_RXTS_L", "Rx timer time stamp register low", ACC_RW, 0x00000000}, + +/* eTSEC MAC Registers */ + +{0x500, "MACCFG1", "MAC configuration register 1", ACC_RW, 0x00000000}, +{0x504, "MACCFG2", "MAC configuration register 2", ACC_RW, 0x00007000}, +{0x508, "IPGIFG", "Inter-packet/inter-frame gap register", ACC_RW, 0x40605060}, +{0x50C, "HAFDUP", "Half-duplex control", ACC_RW, 0x00A1F037}, +{0x510, "MAXFRM", "Maximum frame length", ACC_RW, 0x00000600}, +{0x520, "MIIMCFG", "MII management configuration", ACC_RW, 0x00000007}, +{0x524, "MIIMCOM", "MII management command", ACC_RW, 0x00000000}, +{0x528, "MIIMADD", "MII management address", ACC_RW, 0x00000000}, +{0x52C, "MIIMCON", "MII management control", ACC_WO, 0x00000000}, +{0x530, "MIIMSTAT", "MII management status", ACC_RO, 0x00000000}, +{0x534, "MIIMIND", "MII management indicator", ACC_RO, 0x00000000}, +{0x53C, "IFSTAT", "Interface status", ACC_RO, 0x00000000}, +{0x540, "MACSTNADDR1", "MAC station address register 1", ACC_RW, 0x00000000}, +{0x544, "MACSTNADDR2", "MAC station address register 2", ACC_RW, 0x00000000}, +{0x548, "MAC01ADDR1", "MAC exact match address 1, part 1", ACC_RW, 0x00000000}, +{0x54C, "MAC01ADDR2", "MAC exact match address 1, part 2", ACC_RW, 0x00000000}, +{0x550, "MAC02ADDR1", "MAC exact match address 2, part 1", ACC_RW, 0x00000000}, +{0x554, "MAC02ADDR2", "MAC exact match address 2, part 2", ACC_RW, 0x00000000}, +{0x558, "MAC03ADDR1", "MAC exact match address 3, part 1", ACC_RW, 0x00000000}, +{0x55C, "MAC03ADDR2", "MAC exact match address 3, part 2", ACC_RW, 0x00000000}, +{0x560, "MAC04ADDR1", "MAC exact match address 4, part 1", ACC_RW, 0x00000000}, +{0x564, "MAC04ADDR2", "MAC exact match address 4, part 2", ACC_RW, 0x00000000}, +{0x568, "MAC05ADDR1", "MAC exact match address 5, part 1", ACC_RW, 0x00000000}, +{0x56C, "MAC05ADDR2", "MAC exact match address 5, part 2", ACC_RW, 0x00000000}, +{0x570, "MAC06ADDR1", "MAC exact match address 6, part 1", ACC_RW, 0x00000000}, +{0x574, "MAC06ADDR2", "MAC exact match address 6, part 2", ACC_RW, 0x00000000}, +{0x578, "MAC07ADDR1", "MAC exact match address 7, part 1", ACC_RW, 0x00000000}, +{0x57C, "MAC07ADDR2", "MAC exact match address 7, part 2", ACC_RW, 0x00000000}, +{0x580, "MAC08ADDR1", "MAC exact match address 8, part 1", ACC_RW, 0x00000000}, +{0x584, "MAC08ADDR2", "MAC exact match address 8, part 2", ACC_RW, 0x00000000}, +{0x588, "MAC09ADDR1", "MAC exact match address 9, part 1", ACC_RW, 0x00000000}, +{0x58C, "MAC09ADDR2", "MAC exact match address 9, part 2", ACC_RW, 0x00000000}, +{0x590, "MAC10ADDR1", "MAC exact match address 10, part 1", ACC_RW, 0x00000000}, +{0x594, "MAC10ADDR2", "MAC exact match address 10, part 2", ACC_RW, 0x00000000}, +{0x598, "MAC11ADDR1", "MAC exact match address 11, part 1", ACC_RW, 0x00000000}, +{0x59C, "MAC11ADDR2", "MAC exact match address 11, part 2", ACC_RW, 0x00000000}, +{0x5A0, "MAC12ADDR1", "MAC exact match address 12, part 1", ACC_RW, 0x00000000}, +{0x5A4, "MAC12ADDR2", "MAC exact match address 12, part 2", ACC_RW, 0x00000000}, +{0x5A8, "MAC13ADDR1", "MAC exact match address 13, part 1", ACC_RW, 0x00000000}, +{0x5AC, "MAC13ADDR2", "MAC exact match address 13, part 2", ACC_RW, 0x00000000}, +{0x5B0, "MAC14ADDR1", "MAC exact match address 14, part 1", ACC_RW, 0x00000000}, +{0x5B4, "MAC14ADDR2", "MAC exact match address 14, part 2", ACC_RW, 0x00000000}, +{0x5B8, "MAC15ADDR1", "MAC exact match address 15, part 1", ACC_RW, 0x00000000}, +{0x5BC, "MAC15ADDR2", "MAC exact match address 15, part 2", ACC_RW, 0x00000000}, + +/* eTSEC, "Transmit", "and", Receive, Counters */ + +{0x680, "TR64", "Transmit and receive 64-byte frame counter ", ACC_RW, 0x00000000}, +{0x684, "TR127", "Transmit and receive 65- to 127-byte frame counter", ACC_RW, 0x00000000}, +{0x688, "TR255", "Transmit and receive 128- to 255-byte frame counter", ACC_RW, 0x00000000}, +{0x68C, "TR511", "Transmit and receive 256- to 511-byte frame counter", ACC_RW, 0x00000000}, +{0x690, "TR1K", "Transmit and receive 512- to 1023-byte frame counter", ACC_RW, 0x00000000}, +{0x694, "TRMAX", "Transmit and receive 1024- to 1518-byte frame counter", ACC_RW, 0x00000000}, +{0x698, "TRMGV", "Transmit and receive 1519- to 1522-byte good VLAN frame count", ACC_RW, 0x00000000}, + +/* eTSEC Receive Counters */ + +{0x69C, "RBYT", "Receive byte counter", ACC_RW, 0x00000000}, +{0x6A0, "RPKT", "Receive packet counter", ACC_RW, 0x00000000}, +{0x6A4, "RFCS", "Receive FCS error counter", ACC_RW, 0x00000000}, +{0x6A8, "RMCA", "Receive multicast packet counter", ACC_RW, 0x00000000}, +{0x6AC, "RBCA", "Receive broadcast packet counter", ACC_RW, 0x00000000}, +{0x6B0, "RXCF", "Receive control frame packet counter ", ACC_RW, 0x00000000}, +{0x6B4, "RXPF", "Receive PAUSE frame packet counter", ACC_RW, 0x00000000}, +{0x6B8, "RXUO", "Receive unknown OP code counter ", ACC_RW, 0x00000000}, +{0x6BC, "RALN", "Receive alignment error counter ", ACC_RW, 0x00000000}, +{0x6C0, "RFLR", "Receive frame length error counter ", ACC_RW, 0x00000000}, +{0x6C4, "RCDE", "Receive code error counter ", ACC_RW, 0x00000000}, +{0x6C8, "RCSE", "Receive carrier sense error counter", ACC_RW, 0x00000000}, +{0x6CC, "RUND", "Receive undersize packet counter", ACC_RW, 0x00000000}, +{0x6D0, "ROVR", "Receive oversize packet counter ", ACC_RW, 0x00000000}, +{0x6D4, "RFRG", "Receive fragments counter", ACC_RW, 0x00000000}, +{0x6D8, "RJBR", "Receive jabber counter ", ACC_RW, 0x00000000}, +{0x6DC, "RDRP", "Receive drop counter", ACC_RW, 0x00000000}, + +/* eTSEC Transmit Counters */ + +{0x6E0, "TBYT", "Transmit byte counter", ACC_RW, 0x00000000}, +{0x6E4, "TPKT", "Transmit packet counter", ACC_RW, 0x00000000}, +{0x6E8, "TMCA", "Transmit multicast packet counter ", ACC_RW, 0x00000000}, +{0x6EC, "TBCA", "Transmit broadcast packet counter ", ACC_RW, 0x00000000}, +{0x6F0, "TXPF", "Transmit PAUSE control frame counter ", ACC_RW, 0x00000000}, +{0x6F4, "TDFR", "Transmit deferral packet counter ", ACC_RW, 0x00000000}, +{0x6F8, "TEDF", "Transmit excessive deferral packet counter ", ACC_RW, 0x00000000}, +{0x6FC, "TSCL", "Transmit single collision packet counter", ACC_RW, 0x00000000}, +{0x700, "TMCL", "Transmit multiple collision packet counter", ACC_RW, 0x00000000}, +{0x704, "TLCL", "Transmit late collision packet counter", ACC_RW, 0x00000000}, +{0x708, "TXCL", "Transmit excessive collision packet counter", ACC_RW, 0x00000000}, +{0x70C, "TNCL", "Transmit total collision counter ", ACC_RW, 0x00000000}, +{0x714, "TDRP", "Transmit drop frame counter", ACC_RW, 0x00000000}, +{0x718, "TJBR", "Transmit jabber frame counter ", ACC_RW, 0x00000000}, +{0x71C, "TFCS", "Transmit FCS error counter", ACC_RW, 0x00000000}, +{0x720, "TXCF", "Transmit control frame counter ", ACC_RW, 0x00000000}, +{0x724, "TOVR", "Transmit oversize frame counter", ACC_RW, 0x00000000}, +{0x728, "TUND", "Transmit undersize frame counter ", ACC_RW, 0x00000000}, +{0x72C, "TFRG", "Transmit fragments frame counter ", ACC_RW, 0x00000000}, + +/* eTSEC Counter Control and TOE Statistics Registers */ + +{0x730, "CAR1", "Carry register one register", ACC_W1C, 0x00000000}, +{0x734, "CAR2", "Carry register two register ", ACC_W1C, 0x00000000}, +{0x738, "CAM1", "Carry register one mask register ", ACC_RW, 0xFE03FFFF}, +{0x73C, "CAM2", "Carry register two mask register ", ACC_RW, 0x000FFFFD}, +{0x740, "RREJ", "Receive filer rejected packet counter", ACC_RW, 0x00000000}, + +/* Hash Function Registers */ + +{0x800, "IGADDR0", "Individual/group address register 0", ACC_RW, 0x00000000}, +{0x804, "IGADDR1", "Individual/group address register 1", ACC_RW, 0x00000000}, +{0x808, "IGADDR2", "Individual/group address register 2", ACC_RW, 0x00000000}, +{0x80C, "IGADDR3", "Individual/group address register 3", ACC_RW, 0x00000000}, +{0x810, "IGADDR4", "Individual/group address register 4", ACC_RW, 0x00000000}, +{0x814, "IGADDR5", "Individual/group address register 5", ACC_RW, 0x00000000}, +{0x818, "IGADDR6", "Individual/group address register 6", ACC_RW, 0x00000000}, +{0x81C, "IGADDR7", "Individual/group address register 7", ACC_RW, 0x00000000}, +{0x880, "GADDR0", "Group address register 0", ACC_RW, 0x00000000}, +{0x884, "GADDR1", "Group address register 1", ACC_RW, 0x00000000}, +{0x888, "GADDR2", "Group address register 2", ACC_RW, 0x00000000}, +{0x88C, "GADDR3", "Group address register 3", ACC_RW, 0x00000000}, +{0x890, "GADDR4", "Group address register 4", ACC_RW, 0x00000000}, +{0x894, "GADDR5", "Group address register 5", ACC_RW, 0x00000000}, +{0x898, "GADDR6", "Group address register 6", ACC_RW, 0x00000000}, +{0x89C, "GADDR7", "Group address register 7", ACC_RW, 0x00000000}, + +/* eTSEC DMA Attribute Registers */ + +{0xBF8, "ATTR", "Attribute register", ACC_RW, 0x00000000}, +{0xBFC, "ATTRELI", "Attribute extract length and extract index register", ACC_RW, 0x00000000}, + + +/* eTSEC Lossless Flow Control Registers */ + +{0xC00, "RQPRM0", "Receive Queue Parameters register 0 ", ACC_RW, 0x00000000}, +{0xC04, "RQPRM1", "Receive Queue Parameters register 1 ", ACC_RW, 0x00000000}, +{0xC08, "RQPRM2", "Receive Queue Parameters register 2 ", ACC_RW, 0x00000000}, +{0xC0C, "RQPRM3", "Receive Queue Parameters register 3 ", ACC_RW, 0x00000000}, +{0xC10, "RQPRM4", "Receive Queue Parameters register 4 ", ACC_RW, 0x00000000}, +{0xC14, "RQPRM5", "Receive Queue Parameters register 5 ", ACC_RW, 0x00000000}, +{0xC18, "RQPRM6", "Receive Queue Parameters register 6 ", ACC_RW, 0x00000000}, +{0xC1C, "RQPRM7", "Receive Queue Parameters register 7 ", ACC_RW, 0x00000000}, +{0xC44, "RFBPTR0", "Last Free RxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0xC4C, "RFBPTR1", "Last Free RxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0xC54, "RFBPTR2", "Last Free RxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0xC5C, "RFBPTR3", "Last Free RxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0xC64, "RFBPTR4", "Last Free RxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0xC6C, "RFBPTR5", "Last Free RxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0xC74, "RFBPTR6", "Last Free RxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0xC7C, "RFBPTR7", "Last Free RxBD pointer for ring 7", ACC_RW, 0x00000000}, + +/* eTSEC Future Expansion Space */ + +/* Reserved*/ + +/* eTSEC IEEE 1588 Registers */ + +{0xE00, "TMR_CTRL", "Timer control register", ACC_RW, 0x00010001}, +{0xE04, "TMR_TEVENT", "time stamp event register", ACC_W1C, 0x00000000}, +{0xE08, "TMR_TEMASK", "Timer event mask register", ACC_RW, 0x00000000}, +{0xE0C, "TMR_PEVENT", "time stamp event register", ACC_RW, 0x00000000}, +{0xE10, "TMR_PEMASK", "Timer event mask register", ACC_RW, 0x00000000}, +{0xE14, "TMR_STAT", "time stamp status register", ACC_RW, 0x00000000}, +{0xE18, "TMR_CNT_H", "timer counter high register", ACC_RW, 0x00000000}, +{0xE1C, "TMR_CNT_L", "timer counter low register", ACC_RW, 0x00000000}, +{0xE20, "TMR_ADD", "Timer drift compensation addend register", ACC_RW, 0x00000000}, +{0xE24, "TMR_ACC", "Timer accumulator register", ACC_RW, 0x00000000}, +{0xE28, "TMR_PRSC", "Timer prescale", ACC_RW, 0x00000002}, +{0xE30, "TMROFF_H", "Timer offset high", ACC_RW, 0x00000000}, +{0xE34, "TMROFF_L", "Timer offset low", ACC_RW, 0x00000000}, +{0xE40, "TMR_ALARM1_H", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF}, +{0xE44, "TMR_ALARM1_L", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF}, +{0xE48, "TMR_ALARM2_H", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF}, +{0xE4C, "TMR_ALARM2_L", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF}, +{0xE80, "TMR_FIPER1", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xE84, "TMR_FIPER2", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xE88, "TMR_FIPER3", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xEA0, "TMR_ETTS1_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000}, +{0xEA4, "TMR_ETTS1_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000}, +{0xEA8, "TMR_ETTS2_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000}, +{0xEAC, "TMR_ETTS2_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000}, + +/* End Of Table */ +{0x0, 0x0, 0x0, 0x0, 0x0} +}; diff --git a/hw/net/fsl_etsec/registers.h b/hw/net/fsl_etsec/registers.h new file mode 100644 index 0000000000..7ad7686470 --- /dev/null +++ b/hw/net/fsl_etsec/registers.h @@ -0,0 +1,320 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 _ETSEC_REGISTERS_H_ +#define _ETSEC_REGISTERS_H_ + +#include <stdint.h> + +enum eTSEC_Register_Access_Type { + ACC_RW = 1, /* Read/Write */ + ACC_RO = 2, /* Read Only */ + ACC_WO = 3, /* Write Only */ + ACC_W1C = 4, /* Write 1 to clear */ + ACC_UNKNOWN = 5 /* Unknown register*/ +}; + +typedef struct eTSEC_Register_Definition { + uint32_t offset; + const char *name; + const char *desc; + enum eTSEC_Register_Access_Type access; + uint32_t reset; +} eTSEC_Register_Definition; + +extern const eTSEC_Register_Definition eTSEC_registers_def[]; + +#define DMACTRL_LE (1 << 15) +#define DMACTRL_GRS (1 << 4) +#define DMACTRL_GTS (1 << 3) +#define DMACTRL_WOP (1 << 0) + +#define IEVENT_PERR (1 << 0) +#define IEVENT_DPE (1 << 1) +#define IEVENT_FIQ (1 << 2) +#define IEVENT_FIR (1 << 3) +#define IEVENT_FGPI (1 << 4) +#define IEVENT_RXF (1 << 7) +#define IEVENT_GRSC (1 << 8) +#define IEVENT_MMRW (1 << 9) +#define IEVENT_MMRD (1 << 10) +#define IEVENT_MAG (1 << 11) +#define IEVENT_RXB (1 << 15) +#define IEVENT_XFUN (1 << 16) +#define IEVENT_CRL (1 << 17) +#define IEVENT_LC (1 << 18) +#define IEVENT_TXF (1 << 20) +#define IEVENT_TXB (1 << 21) +#define IEVENT_TXE (1 << 22) +#define IEVENT_TXC (1 << 23) +#define IEVENT_BABT (1 << 24) +#define IEVENT_GTSC (1 << 25) +#define IEVENT_MSRO (1 << 26) +#define IEVENT_EBERR (1 << 28) +#define IEVENT_BSY (1 << 29) +#define IEVENT_RXC (1 << 30) +#define IEVENT_BABR (1 << 31) + +#define IMASK_RXFEN (1 << 7) +#define IMASK_GRSCEN (1 << 8) +#define IMASK_RXBEN (1 << 15) +#define IMASK_TXFEN (1 << 20) +#define IMASK_TXBEN (1 << 21) +#define IMASK_GTSCEN (1 << 25) + +#define MACCFG1_TX_EN (1 << 0) +#define MACCFG1_RX_EN (1 << 2) + +#define MACCFG2_CRC_EN (1 << 1) +#define MACCFG2_PADCRC (1 << 2) + +#define MIIMCOM_READ (1 << 0) +#define MIIMCOM_SCAN (1 << 1) + +#define RCTRL_PRSDEP_MASK (0x3) +#define RCTRL_PRSDEP_OFFSET (6) +#define RCTRL_RSF (1 << 2) + +/* Index of each register */ + +#define TSEC_ID (0x000 / 4) +#define TSEC_ID2 (0x004 / 4) +#define IEVENT (0x010 / 4) +#define IMASK (0x014 / 4) +#define EDIS (0x018 / 4) +#define ECNTRL (0x020 / 4) +#define PTV (0x028 / 4) +#define DMACTRL (0x02C / 4) +#define TBIPA (0x030 / 4) +#define TCTRL (0x100 / 4) +#define TSTAT (0x104 / 4) +#define DFVLAN (0x108 / 4) +#define TXIC (0x110 / 4) +#define TQUEUE (0x114 / 4) +#define TR03WT (0x140 / 4) +#define TR47WT (0x144 / 4) +#define TBDBPH (0x180 / 4) +#define TBPTR0 (0x184 / 4) +#define TBPTR1 (0x18C / 4) +#define TBPTR2 (0x194 / 4) +#define TBPTR3 (0x19C / 4) +#define TBPTR4 (0x1A4 / 4) +#define TBPTR5 (0x1AC / 4) +#define TBPTR6 (0x1B4 / 4) +#define TBPTR7 (0x1BC / 4) +#define TBASEH (0x200 / 4) +#define TBASE0 (0x204 / 4) +#define TBASE1 (0x20C / 4) +#define TBASE2 (0x214 / 4) +#define TBASE3 (0x21C / 4) +#define TBASE4 (0x224 / 4) +#define TBASE5 (0x22C / 4) +#define TBASE6 (0x234 / 4) +#define TBASE7 (0x23C / 4) +#define TMR_TXTS1_ID (0x280 / 4) +#define TMR_TXTS2_ID (0x284 / 4) +#define TMR_TXTS1_H (0x2C0 / 4) +#define TMR_TXTS1_L (0x2C4 / 4) +#define TMR_TXTS2_H (0x2C8 / 4) +#define TMR_TXTS2_L (0x2CC / 4) +#define RCTRL (0x300 / 4) +#define RSTAT (0x304 / 4) +#define RXIC (0x310 / 4) +#define RQUEUE (0x314 / 4) +#define RBIFX (0x330 / 4) +#define RQFAR (0x334 / 4) +#define RQFCR (0x338 / 4) +#define RQFPR (0x33C / 4) +#define MRBLR (0x340 / 4) +#define RBDBPH (0x380 / 4) +#define RBPTR0 (0x384 / 4) +#define RBPTR1 (0x38C / 4) +#define RBPTR2 (0x394 / 4) +#define RBPTR3 (0x39C / 4) +#define RBPTR4 (0x3A4 / 4) +#define RBPTR5 (0x3AC / 4) +#define RBPTR6 (0x3B4 / 4) +#define RBPTR7 (0x3BC / 4) +#define RBASEH (0x400 / 4) +#define RBASE0 (0x404 / 4) +#define RBASE1 (0x40C / 4) +#define RBASE2 (0x414 / 4) +#define RBASE3 (0x41C / 4) +#define RBASE4 (0x424 / 4) +#define RBASE5 (0x42C / 4) +#define RBASE6 (0x434 / 4) +#define RBASE7 (0x43C / 4) +#define TMR_RXTS_H (0x4C0 / 4) +#define TMR_RXTS_L (0x4C4 / 4) +#define MACCFG1 (0x500 / 4) +#define MACCFG2 (0x504 / 4) +#define IPGIFG (0x508 / 4) +#define HAFDUP (0x50C / 4) +#define MAXFRM (0x510 / 4) +#define MIIMCFG (0x520 / 4) +#define MIIMCOM (0x524 / 4) +#define MIIMADD (0x528 / 4) +#define MIIMCON (0x52C / 4) +#define MIIMSTAT (0x530 / 4) +#define MIIMIND (0x534 / 4) +#define IFSTAT (0x53C / 4) +#define MACSTNADDR1 (0x540 / 4) +#define MACSTNADDR2 (0x544 / 4) +#define MAC01ADDR1 (0x548 / 4) +#define MAC01ADDR2 (0x54C / 4) +#define MAC02ADDR1 (0x550 / 4) +#define MAC02ADDR2 (0x554 / 4) +#define MAC03ADDR1 (0x558 / 4) +#define MAC03ADDR2 (0x55C / 4) +#define MAC04ADDR1 (0x560 / 4) +#define MAC04ADDR2 (0x564 / 4) +#define MAC05ADDR1 (0x568 / 4) +#define MAC05ADDR2 (0x56C / 4) +#define MAC06ADDR1 (0x570 / 4) +#define MAC06ADDR2 (0x574 / 4) +#define MAC07ADDR1 (0x578 / 4) +#define MAC07ADDR2 (0x57C / 4) +#define MAC08ADDR1 (0x580 / 4) +#define MAC08ADDR2 (0x584 / 4) +#define MAC09ADDR1 (0x588 / 4) +#define MAC09ADDR2 (0x58C / 4) +#define MAC10ADDR1 (0x590 / 4) +#define MAC10ADDR2 (0x594 / 4) +#define MAC11ADDR1 (0x598 / 4) +#define MAC11ADDR2 (0x59C / 4) +#define MAC12ADDR1 (0x5A0 / 4) +#define MAC12ADDR2 (0x5A4 / 4) +#define MAC13ADDR1 (0x5A8 / 4) +#define MAC13ADDR2 (0x5AC / 4) +#define MAC14ADDR1 (0x5B0 / 4) +#define MAC14ADDR2 (0x5B4 / 4) +#define MAC15ADDR1 (0x5B8 / 4) +#define MAC15ADDR2 (0x5BC / 4) +#define TR64 (0x680 / 4) +#define TR127 (0x684 / 4) +#define TR255 (0x688 / 4) +#define TR511 (0x68C / 4) +#define TR1K (0x690 / 4) +#define TRMAX (0x694 / 4) +#define TRMGV (0x698 / 4) +#define RBYT (0x69C / 4) +#define RPKT (0x6A0 / 4) +#define RFCS (0x6A4 / 4) +#define RMCA (0x6A8 / 4) +#define RBCA (0x6AC / 4) +#define RXCF (0x6B0 / 4) +#define RXPF (0x6B4 / 4) +#define RXUO (0x6B8 / 4) +#define RALN (0x6BC / 4) +#define RFLR (0x6C0 / 4) +#define RCDE (0x6C4 / 4) +#define RCSE (0x6C8 / 4) +#define RUND (0x6CC / 4) +#define ROVR (0x6D0 / 4) +#define RFRG (0x6D4 / 4) +#define RJBR (0x6D8 / 4) +#define RDRP (0x6DC / 4) +#define TBYT (0x6E0 / 4) +#define TPKT (0x6E4 / 4) +#define TMCA (0x6E8 / 4) +#define TBCA (0x6EC / 4) +#define TXPF (0x6F0 / 4) +#define TDFR (0x6F4 / 4) +#define TEDF (0x6F8 / 4) +#define TSCL (0x6FC / 4) +#define TMCL (0x700 / 4) +#define TLCL (0x704 / 4) +#define TXCL (0x708 / 4) +#define TNCL (0x70C / 4) +#define TDRP (0x714 / 4) +#define TJBR (0x718 / 4) +#define TFCS (0x71C / 4) +#define TXCF (0x720 / 4) +#define TOVR (0x724 / 4) +#define TUND (0x728 / 4) +#define TFRG (0x72C / 4) +#define CAR1 (0x730 / 4) +#define CAR2 (0x734 / 4) +#define CAM1 (0x738 / 4) +#define CAM2 (0x73C / 4) +#define RREJ (0x740 / 4) +#define IGADDR0 (0x800 / 4) +#define IGADDR1 (0x804 / 4) +#define IGADDR2 (0x808 / 4) +#define IGADDR3 (0x80C / 4) +#define IGADDR4 (0x810 / 4) +#define IGADDR5 (0x814 / 4) +#define IGADDR6 (0x818 / 4) +#define IGADDR7 (0x81C / 4) +#define GADDR0 (0x880 / 4) +#define GADDR1 (0x884 / 4) +#define GADDR2 (0x888 / 4) +#define GADDR3 (0x88C / 4) +#define GADDR4 (0x890 / 4) +#define GADDR5 (0x894 / 4) +#define GADDR6 (0x898 / 4) +#define GADDR7 (0x89C / 4) +#define ATTR (0xBF8 / 4) +#define ATTRELI (0xBFC / 4) +#define RQPRM0 (0xC00 / 4) +#define RQPRM1 (0xC04 / 4) +#define RQPRM2 (0xC08 / 4) +#define RQPRM3 (0xC0C / 4) +#define RQPRM4 (0xC10 / 4) +#define RQPRM5 (0xC14 / 4) +#define RQPRM6 (0xC18 / 4) +#define RQPRM7 (0xC1C / 4) +#define RFBPTR0 (0xC44 / 4) +#define RFBPTR1 (0xC4C / 4) +#define RFBPTR2 (0xC54 / 4) +#define RFBPTR3 (0xC5C / 4) +#define RFBPTR4 (0xC64 / 4) +#define RFBPTR5 (0xC6C / 4) +#define RFBPTR6 (0xC74 / 4) +#define RFBPTR7 (0xC7C / 4) +#define TMR_CTRL (0xE00 / 4) +#define TMR_TEVENT (0xE04 / 4) +#define TMR_TEMASK (0xE08 / 4) +#define TMR_PEVENT (0xE0C / 4) +#define TMR_PEMASK (0xE10 / 4) +#define TMR_STAT (0xE14 / 4) +#define TMR_CNT_H (0xE18 / 4) +#define TMR_CNT_L (0xE1C / 4) +#define TMR_ADD (0xE20 / 4) +#define TMR_ACC (0xE24 / 4) +#define TMR_PRSC (0xE28 / 4) +#define TMROFF_H (0xE30 / 4) +#define TMROFF_L (0xE34 / 4) +#define TMR_ALARM1_H (0xE40 / 4) +#define TMR_ALARM1_L (0xE44 / 4) +#define TMR_ALARM2_H (0xE48 / 4) +#define TMR_ALARM2_L (0xE4C / 4) +#define TMR_FIPER1 (0xE80 / 4) +#define TMR_FIPER2 (0xE84 / 4) +#define TMR_FIPER3 (0xE88 / 4) +#define TMR_ETTS1_H (0xEA0 / 4) +#define TMR_ETTS1_L (0xEA4 / 4) +#define TMR_ETTS2_H (0xEA8 / 4) +#define TMR_ETTS2_L (0xEAC / 4) + +#endif /* ! _ETSEC_REGISTERS_H_ */ diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c new file mode 100644 index 0000000000..77602722b3 --- /dev/null +++ b/hw/net/fsl_etsec/rings.c @@ -0,0 +1,650 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 "net/checksum.h" + +#include "etsec.h" +#include "registers.h" + +/* #define ETSEC_RING_DEBUG */ +/* #define HEX_DUMP */ +/* #define DEBUG_BD */ + +#ifdef ETSEC_RING_DEBUG +static const int debug_etsec = 1; +#else +static const int debug_etsec; +#endif + +#define RING_DEBUG(fmt, ...) do { \ + if (debug_etsec) { \ + qemu_log(fmt , ## __VA_ARGS__); \ + } \ + } while (0) + +#ifdef DEBUG_BD + +static void print_tx_bd_flags(uint16_t flags) +{ + qemu_log(" Ready: %d\n", !!(flags & BD_TX_READY)); + qemu_log(" PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC)); + qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP)); + qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT)); + qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST)); + qemu_log(" Tx CRC: %d\n", !!(flags & BD_TX_TC)); + qemu_log(" User-defined preamble / defer: %d\n", + !!(flags & BD_TX_PREDEF)); + qemu_log(" Huge frame enable / Late collision: %d\n", + !!(flags & BD_TX_HFELC)); + qemu_log(" Control frame / Retransmission Limit: %d\n", + !!(flags & BD_TX_CFRL)); + qemu_log(" Retry count: %d\n", + (flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK); + qemu_log(" Underrun / TCP/IP off-load enable: %d\n", + !!(flags & BD_TX_TOEUN)); + qemu_log(" Truncation: %d\n", !!(flags & BD_TX_TR)); +} + +static void print_rx_bd_flags(uint16_t flags) +{ + qemu_log(" Empty: %d\n", !!(flags & BD_RX_EMPTY)); + qemu_log(" Receive software ownership: %d\n", !!(flags & BD_RX_RO1)); + qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP)); + qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT)); + qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST)); + qemu_log(" First in frame: %d\n", !!(flags & BD_RX_FIRST)); + qemu_log(" Miss: %d\n", !!(flags & BD_RX_MISS)); + qemu_log(" Broadcast: %d\n", !!(flags & BD_RX_BROADCAST)); + qemu_log(" Multicast: %d\n", !!(flags & BD_RX_MULTICAST)); + qemu_log(" Rx frame length violation: %d\n", !!(flags & BD_RX_LG)); + qemu_log(" Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO)); + qemu_log(" Short frame: %d\n", !!(flags & BD_RX_SH)); + qemu_log(" Rx CRC Error: %d\n", !!(flags & BD_RX_CR)); + qemu_log(" Overrun: %d\n", !!(flags & BD_RX_OV)); + qemu_log(" Truncation: %d\n", !!(flags & BD_RX_TR)); +} + + +static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index) +{ + qemu_log("eTSEC %s Data Buffer Descriptor (%u)\n", + mode == eTSEC_TRANSMIT ? "Transmit" : "Receive", + index); + qemu_log(" Flags : 0x%04x\n", bd.flags); + if (mode == eTSEC_TRANSMIT) { + print_tx_bd_flags(bd.flags); + } else { + print_rx_bd_flags(bd.flags); + } + qemu_log(" Length : 0x%04x\n", bd.length); + qemu_log(" Pointer : 0x%08x\n", bd.bufptr); +} + +#endif /* DEBUG_BD */ + +static void read_buffer_descriptor(eTSEC *etsec, + hwaddr addr, + eTSEC_rxtx_bd *bd) +{ + assert(bd != NULL); + + RING_DEBUG("READ Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); + cpu_physical_memory_read(addr, + bd, + sizeof(eTSEC_rxtx_bd)); + + if (etsec->regs[DMACTRL].value & DMACTRL_LE) { + bd->flags = lduw_le_p(&bd->flags); + bd->length = lduw_le_p(&bd->length); + bd->bufptr = ldl_le_p(&bd->bufptr); + } else { + bd->flags = lduw_be_p(&bd->flags); + bd->length = lduw_be_p(&bd->length); + bd->bufptr = ldl_be_p(&bd->bufptr); + } +} + +static void write_buffer_descriptor(eTSEC *etsec, + hwaddr addr, + eTSEC_rxtx_bd *bd) +{ + assert(bd != NULL); + + if (etsec->regs[DMACTRL].value & DMACTRL_LE) { + stw_le_p(&bd->flags, bd->flags); + stw_le_p(&bd->length, bd->length); + stl_le_p(&bd->bufptr, bd->bufptr); + } else { + stw_be_p(&bd->flags, bd->flags); + stw_be_p(&bd->length, bd->length); + stl_be_p(&bd->bufptr, bd->bufptr); + } + + RING_DEBUG("Write Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); + cpu_physical_memory_write(addr, + bd, + sizeof(eTSEC_rxtx_bd)); +} + +static void ievent_set(eTSEC *etsec, + uint32_t flags) +{ + etsec->regs[IEVENT].value |= flags; + + if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN) + || (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) { + qemu_irq_raise(etsec->tx_irq); + RING_DEBUG("%s Raise Tx IRQ\n", __func__); + } + + if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN) + || (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) { + qemu_irq_pulse(etsec->rx_irq); + RING_DEBUG("%s Raise Rx IRQ\n", __func__); + } +} + +static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len) +{ + int add = min_frame_len - etsec->tx_buffer_len; + + /* Padding */ + if (add > 0) { + RING_DEBUG("pad:%u\n", add); + etsec->tx_buffer = g_realloc(etsec->tx_buffer, + etsec->tx_buffer_len + add); + + memset(etsec->tx_buffer + etsec->tx_buffer_len, 0x0, add); + etsec->tx_buffer_len += add; + } + + /* Never add CRC in QEMU */ +} + +static void process_tx_fcb(eTSEC *etsec) +{ + uint8_t flags = (uint8_t)(*etsec->tx_buffer); + /* L3 header offset from start of frame */ + uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3); + /* L4 header offset from start of L3 header */ + uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2); + /* L3 header */ + uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset; + /* L4 header */ + uint8_t *l4_header = l3_header + l4_header_offset; + + /* if packet is IP4 and IP checksum is requested */ + if (flags & FCB_TX_IP && flags & FCB_TX_CIP) { + /* do IP4 checksum (TODO This funtion does TCP/UDP checksum but not sure + * if it also does IP4 checksum. */ + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } + /* TODO Check the correct usage of the PHCS field of the FCB in case the NPH + * flag is on */ + + /* if packet is IP4 and TCP or UDP */ + if (flags & FCB_TX_IP && flags & FCB_TX_TUP) { + /* if UDP */ + if (flags & FCB_TX_UDP) { + /* if checksum is requested */ + if (flags & FCB_TX_CTU) { + /* do UDP checksum */ + + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } else { + /* set checksum field to 0 */ + l4_header[6] = 0; + l4_header[7] = 0; + } + } else if (flags & FCB_TX_CTU) { /* if TCP and checksum is requested */ + /* do TCP checksum */ + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } + } +} + +static void process_tx_bd(eTSEC *etsec, + eTSEC_rxtx_bd *bd) +{ + uint8_t *tmp_buff = NULL; + hwaddr tbdbth = (hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32; + + if (bd->length == 0) { + /* ERROR */ + return; + } + + if (etsec->tx_buffer_len == 0) { + /* It's the first BD */ + etsec->first_bd = *bd; + } + + /* TODO: if TxBD[TOE/UN] skip the Tx Frame Control Block*/ + + /* Load this Data Buffer */ + etsec->tx_buffer = g_realloc(etsec->tx_buffer, + etsec->tx_buffer_len + bd->length); + tmp_buff = etsec->tx_buffer + etsec->tx_buffer_len; + cpu_physical_memory_read(bd->bufptr + tbdbth, tmp_buff, bd->length); + + /* Update buffer length */ + etsec->tx_buffer_len += bd->length; + + + if (etsec->tx_buffer_len != 0 && (bd->flags & BD_LAST)) { + if (etsec->regs[MACCFG1].value & MACCFG1_TX_EN) { + /* MAC Transmit enabled */ + + /* Process offload Tx FCB */ + if (etsec->first_bd.flags & BD_TX_TOEUN) { + process_tx_fcb(etsec); + } + + if (etsec->first_bd.flags & BD_TX_PADCRC + || etsec->regs[MACCFG2].value & MACCFG2_PADCRC) { + + /* Padding and CRC (Padding implies CRC) */ + tx_padding_and_crc(etsec, 64); + + } else if (etsec->first_bd.flags & BD_TX_TC + || etsec->regs[MACCFG2].value & MACCFG2_CRC_EN) { + + /* Only CRC */ + /* Never add CRC in QEMU */ + } + +#if defined(HEX_DUMP) + qemu_log("eTSEC Send packet size:%d\n", etsec->tx_buffer_len); + qemu_hexdump(etsec->tx_buffer, stderr, "", etsec->tx_buffer_len); +#endif /* ETSEC_RING_DEBUG */ + + if (etsec->first_bd.flags & BD_TX_TOEUN) { + qemu_send_packet(qemu_get_queue(etsec->nic), + etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } else { + qemu_send_packet(qemu_get_queue(etsec->nic), + etsec->tx_buffer, + etsec->tx_buffer_len); + } + + } + + etsec->tx_buffer_len = 0; + + if (bd->flags & BD_INTERRUPT) { + ievent_set(etsec, IEVENT_TXF); + } + } else { + if (bd->flags & BD_INTERRUPT) { + ievent_set(etsec, IEVENT_TXB); + } + } + + /* Update DB flags */ + + /* Clear Ready */ + bd->flags &= ~BD_TX_READY; + + /* Clear Defer */ + bd->flags &= ~BD_TX_PREDEF; + + /* Clear Late Collision */ + bd->flags &= ~BD_TX_HFELC; + + /* Clear Retransmission Limit */ + bd->flags &= ~BD_TX_CFRL; + + /* Clear Retry Count */ + bd->flags &= ~(BD_TX_RC_MASK << BD_TX_RC_OFFSET); + + /* Clear Underrun */ + bd->flags &= ~BD_TX_TOEUN; + + /* Clear Truncation */ + bd->flags &= ~BD_TX_TR; +} + +void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr) +{ + hwaddr ring_base = 0; + hwaddr bd_addr = 0; + eTSEC_rxtx_bd bd; + uint16_t bd_flags; + + if (!(etsec->regs[MACCFG1].value & MACCFG1_TX_EN)) { + RING_DEBUG("%s: MAC Transmit not enabled\n", __func__); + return; + } + + ring_base = (hwaddr)(etsec->regs[TBASEH].value & 0xF) << 32; + ring_base += etsec->regs[TBASE0 + ring_nbr].value & ~0x7; + bd_addr = etsec->regs[TBPTR0 + ring_nbr].value & ~0x7; + + do { + read_buffer_descriptor(etsec, bd_addr, &bd); + +#ifdef DEBUG_BD + print_bd(bd, + eTSEC_TRANSMIT, + (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); + +#endif /* DEBUG_BD */ + + /* Save flags before BD update */ + bd_flags = bd.flags; + + if (bd_flags & BD_TX_READY) { + process_tx_bd(etsec, &bd); + + /* Write back BD after update */ + write_buffer_descriptor(etsec, bd_addr, &bd); + } + + /* Wrap or next BD */ + if (bd_flags & BD_WRAP) { + bd_addr = ring_base; + } else { + bd_addr += sizeof(eTSEC_rxtx_bd); + } + + } while (bd_addr != ring_base); + + bd_addr = ring_base; + + /* Save the Buffer Descriptor Pointers to current bd */ + etsec->regs[TBPTR0 + ring_nbr].value = bd_addr; + + /* Set transmit halt THLTx */ + etsec->regs[TSTAT].value |= 1 << (31 - ring_nbr); +} + +static void fill_rx_bd(eTSEC *etsec, + eTSEC_rxtx_bd *bd, + const uint8_t **buf, + size_t *size) +{ + uint16_t to_write; + hwaddr bufptr = bd->bufptr + + ((hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32); + uint8_t padd[etsec->rx_padding]; + uint8_t rem; + + RING_DEBUG("eTSEC fill Rx buffer @ 0x%016" HWADDR_PRIx + " size:%zu(padding + crc:%u) + fcb:%u\n", + bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size); + + bd->length = 0; + + /* This operation will only write FCB */ + if (etsec->rx_fcb_size != 0) { + + cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size); + + bufptr += etsec->rx_fcb_size; + bd->length += etsec->rx_fcb_size; + etsec->rx_fcb_size = 0; + + } + + /* We remove padding from the computation of to_write because it is not + * allocated in the buffer. + */ + to_write = MIN(*size - etsec->rx_padding, + etsec->regs[MRBLR].value - etsec->rx_fcb_size); + + /* This operation can only write packet data and no padding */ + if (to_write > 0) { + cpu_physical_memory_write(bufptr, *buf, to_write); + + *buf += to_write; + bufptr += to_write; + *size -= to_write; + + bd->flags &= ~BD_RX_EMPTY; + bd->length += to_write; + } + + if (*size == etsec->rx_padding) { + /* The remaining bytes are only for padding which is not actually + * allocated in the data buffer. + */ + + rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding); + + if (rem > 0) { + memset(padd, 0x0, sizeof(padd)); + etsec->rx_padding -= rem; + *size -= rem; + bd->length += rem; + cpu_physical_memory_write(bufptr, padd, rem); + } + } +} + +static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size) +{ + uint32_t fcb_size = 0; + uint8_t prsdep = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET) + & RCTRL_PRSDEP_MASK; + + if (prsdep != 0) { + /* Prepend FCB (FCB size + RCTRL[PAL]) */ + fcb_size = 8 + ((etsec->regs[RCTRL].value >> 16) & 0x1F); + + etsec->rx_fcb_size = fcb_size; + + /* TODO: fill_FCB(etsec); */ + memset(etsec->rx_fcb, 0x0, sizeof(etsec->rx_fcb)); + + } else { + etsec->rx_fcb_size = 0; + } + + if (etsec->rx_buffer != NULL) { + g_free(etsec->rx_buffer); + } + + /* Do not copy the frame for now */ + etsec->rx_buffer = (uint8_t *)buf; + etsec->rx_buffer_len = size; + + /* CRC padding (We don't have to compute the CRC) */ + etsec->rx_padding = 4; + + etsec->rx_first_in_frame = 1; + etsec->rx_remaining_data = etsec->rx_buffer_len; + RING_DEBUG("%s: rx_buffer_len:%u rx_padding+crc:%u\n", __func__, + etsec->rx_buffer_len, etsec->rx_padding); +} + +void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size) +{ + int ring_nbr = 0; /* Always use ring0 (no filer) */ + + if (etsec->rx_buffer_len != 0) { + RING_DEBUG("%s: We can't receive now," + " a buffer is already in the pipe\n", __func__); + return; + } + + if (etsec->regs[RSTAT].value & 1 << (23 - ring_nbr)) { + RING_DEBUG("%s: The ring is halted\n", __func__); + return; + } + + if (etsec->regs[DMACTRL].value & DMACTRL_GRS) { + RING_DEBUG("%s: Graceful receive stop\n", __func__); + return; + } + + if (!(etsec->regs[MACCFG1].value & MACCFG1_RX_EN)) { + RING_DEBUG("%s: MAC Receive not enabled\n", __func__); + return; + } + + if ((etsec->regs[RCTRL].value & RCTRL_RSF) && (size < 60)) { + /* CRC is not in the packet yet, so short frame is below 60 bytes */ + RING_DEBUG("%s: Drop short frame\n", __func__); + return; + } + + rx_init_frame(etsec, buf, size); + + etsec_walk_rx_ring(etsec, ring_nbr); +} + +void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr) +{ + hwaddr ring_base = 0; + hwaddr bd_addr = 0; + hwaddr start_bd_addr = 0; + eTSEC_rxtx_bd bd; + uint16_t bd_flags; + size_t remaining_data; + const uint8_t *buf; + uint8_t *tmp_buf; + size_t size; + + if (etsec->rx_buffer_len == 0) { + /* No frame to send */ + RING_DEBUG("No frame to send\n"); + return; + } + + remaining_data = etsec->rx_remaining_data + etsec->rx_padding; + buf = etsec->rx_buffer + + (etsec->rx_buffer_len - etsec->rx_remaining_data); + size = etsec->rx_buffer_len + etsec->rx_padding; + + ring_base = (hwaddr)(etsec->regs[RBASEH].value & 0xF) << 32; + ring_base += etsec->regs[RBASE0 + ring_nbr].value & ~0x7; + start_bd_addr = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7; + + do { + read_buffer_descriptor(etsec, bd_addr, &bd); + +#ifdef DEBUG_BD + print_bd(bd, + eTSEC_RECEIVE, + (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); + +#endif /* DEBUG_BD */ + + /* Save flags before BD update */ + bd_flags = bd.flags; + + if (bd_flags & BD_RX_EMPTY) { + fill_rx_bd(etsec, &bd, &buf, &remaining_data); + + if (etsec->rx_first_in_frame) { + bd.flags |= BD_RX_FIRST; + etsec->rx_first_in_frame = 0; + etsec->rx_first_bd = bd; + } + + /* Last in frame */ + if (remaining_data == 0) { + + /* Clear flags */ + + bd.flags &= ~0x7ff; + + bd.flags |= BD_LAST; + + /* NOTE: non-octet aligned frame is impossible in qemu */ + + if (size >= etsec->regs[MAXFRM].value) { + /* frame length violation */ + qemu_log("%s frame length violation: size:%zu MAXFRM:%d\n", + __func__, size, etsec->regs[MAXFRM].value); + + bd.flags |= BD_RX_LG; + } + + if (size < 64) { + /* Short frame */ + bd.flags |= BD_RX_SH; + } + + /* TODO: Broadcast and Multicast */ + + if (bd.flags | BD_INTERRUPT) { + /* Set RXFx */ + etsec->regs[RSTAT].value |= 1 << (7 - ring_nbr); + + /* Set IEVENT */ + ievent_set(etsec, IEVENT_RXF); + } + + } else { + if (bd.flags | BD_INTERRUPT) { + /* Set IEVENT */ + ievent_set(etsec, IEVENT_RXB); + } + } + + /* Write back BD after update */ + write_buffer_descriptor(etsec, bd_addr, &bd); + } + + /* Wrap or next BD */ + if (bd_flags & BD_WRAP) { + bd_addr = ring_base; + } else { + bd_addr += sizeof(eTSEC_rxtx_bd); + } + } while (remaining_data != 0 + && (bd_flags & BD_RX_EMPTY) + && bd_addr != start_bd_addr); + + /* Reset ring ptr */ + etsec->regs[RBPTR0 + ring_nbr].value = bd_addr; + + /* The frame is too large to fit in the Rx ring */ + if (remaining_data > 0) { + + /* Set RSTAT[QHLTx] */ + etsec->regs[RSTAT].value |= 1 << (23 - ring_nbr); + + /* Save remaining data to send the end of the frame when the ring will + * be restarted + */ + etsec->rx_remaining_data = remaining_data; + + /* Copy the frame */ + tmp_buf = g_malloc(size); + memcpy(tmp_buf, etsec->rx_buffer, size); + etsec->rx_buffer = tmp_buf; + + RING_DEBUG("no empty RxBD available any more\n"); + } else { + etsec->rx_buffer_len = 0; + etsec->rx_buffer = NULL; + } + + RING_DEBUG("eTSEC End of ring_write: remaining_data:%zu\n", remaining_data); +} diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 1bd6f50aaa..f6fbcb56bf 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -405,6 +405,8 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, dev->rx_bufs++; + qemu_flush_queued_packets(qemu_get_queue(dev->nic)); + DPRINTF("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d" " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs, (unsigned long long)buf); diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index b37ce9d633..8a08752613 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -238,6 +238,7 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { CPUState *cpu; + PowerPCCPU *pcpu; char cpu_name[128]; uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); @@ -246,14 +247,16 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, continue; } env = cpu->env_ptr; + pcpu = POWERPC_CPU(cpu); snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", - cpu->cpu_index); + ppc_get_vcpu_dt_id(pcpu)); qemu_fdt_add_subnode(fdt, cpu_name); qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); - qemu_fdt_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index); + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", + ppc_get_vcpu_dt_id(pcpu)); qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size", env->dcache_line_size); qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size", diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 114be64480..0e82719b69 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -26,6 +26,7 @@ #include "hw/ppc/ppc_e500.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/cpus.h" #include "hw/timer/m48t59.h" #include "qemu/log.h" #include "hw/loader.h" @@ -1362,3 +1363,24 @@ int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size, return 0; } + +/* CPU device-tree ID helpers */ +int ppc_get_vcpu_dt_id(PowerPCCPU *cpu) +{ + return cpu->cpu_dt_id; +} + +PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + if (cpu->cpu_dt_id == cpu_dt_id) { + return cpu; + } + } + + return NULL; +} diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 93d02c1e50..bf46c380ec 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -49,6 +49,7 @@ #include "exec/address-spaces.h" #include "hw/usb.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include <libfdt.h> @@ -206,19 +207,20 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) CPU_FOREACH(cpu) { DeviceClass *dc = DEVICE_GET_CLASS(cpu); + int index = ppc_get_vcpu_dt_id(POWERPC_CPU(cpu)); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(cpu->numa_node), - cpu_to_be32(cpu->cpu_index)}; + cpu_to_be32(index)}; - if ((cpu->cpu_index % smt) != 0) { + if ((index % smt) != 0) { continue; } snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name, - cpu->cpu_index); + index); offset = fdt_path_offset(fdt, cpu_model); if (offset < 0) { @@ -367,7 +369,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, CPUPPCState *env = &cpu->env; DeviceClass *dc = DEVICE_GET_CLASS(cs); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - int index = cs->cpu_index; + int index = ppc_get_vcpu_dt_id(cpu); uint32_t servers_prop[smp_threads]; uint32_t gservers_prop[smp_threads * 2]; char *nodename; @@ -685,6 +687,7 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) if (shift > 0) { /* Kernel handles htab, we don't need to allocate one */ spapr->htab_shift = shift; + kvmppc_kern_htab = true; } else { if (!spapr->htab) { /* Allocate an htab if we don't yet have one */ @@ -740,8 +743,21 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; env->external_htab = (uint8_t *)spapr->htab; + if (kvm_enabled() && !env->external_htab) { + /* + * HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte* + * functions do the right thing. + */ + env->external_htab = (void *)1; + } env->htab_base = -1; - env->htab_mask = HTAB_SIZE(spapr) - 1; + /* + * htab_mask is the mask used to normalize hash value to PTEG index. + * htab_shift is log2 of hash table size. + * We have 8 hpte per group, and each hpte is 16 bytes. + * ie have 128 bytes per hpte entry. + */ + env->htab_mask = (1ULL << ((spapr)->htab_shift - 7)) - 1; env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab | (spapr->htab_shift - 18); } @@ -1305,20 +1321,15 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); - if (kernel_size < 0) { + if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0); kernel_le = kernel_size > 0; } if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - load_limit - KERNEL_LOAD_ADDR); - } - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); + fprintf(stderr, "qemu: error loading %s: %s\n", + kernel_filename, load_elf_strerror(kernel_size)); exit(1); } @@ -1366,6 +1377,24 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) assert(spapr->fdt_skel != NULL); } +static int spapr_kvm_type(const char *vm_type) +{ + if (!vm_type) { + return 0; + } + + if (!strcmp(vm_type, "HV")) { + return 1; + } + + if (!strcmp(vm_type, "PR")) { + return 2; + } + + error_report("Unknown kvm-type specified '%s'", vm_type); + exit(1); +} + static QEMUMachine spapr_machine = { .name = "pseries", .desc = "pSeries Logical Partition (PAPR compliant)", @@ -1376,6 +1405,7 @@ static QEMUMachine spapr_machine = { .max_cpus = MAX_CPUS, .no_parallel = 1, .default_boot_order = NULL, + .kvm_type = spapr_kvm_type, }; static void spapr_machine_init(void) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 3ffcc65f03..d918780746 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -40,6 +40,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, return rb; } +static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) +{ + /* + * hash value/pteg group index is normalized by htab_mask + */ + if (((pte_index & ~7ULL) / HPTES_PER_GROUP) & ~env->htab_mask) { + return false; + } + return true; +} + static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -50,8 +61,8 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong ptel = args[3]; target_ulong page_shift = 12; target_ulong raddr; - target_ulong i; - hwaddr hpte; + target_ulong index; + uint64_t token; /* only handle 4k and 16M pages for now */ if (pteh & HPTE64_V_LARGE) { @@ -91,33 +102,37 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, pteh &= ~0x60ULL; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } + + index = 0; if (likely((flags & H_EXACT) == 0)) { pte_index &= ~7ULL; - hpte = pte_index * HASH_PTE_SIZE_64; - for (i = 0; ; ++i) { - if (i == 8) { + token = ppc_hash64_start_access(cpu, pte_index); + do { + if (index == 8) { + ppc_hash64_stop_access(token); return H_PTEG_FULL; } - if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) { + if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) { break; } - hpte += HASH_PTE_SIZE_64; - } + } while (index++); + ppc_hash64_stop_access(token); } else { - i = 0; - hpte = pte_index * HASH_PTE_SIZE_64; - if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) { + token = ppc_hash64_start_access(cpu, pte_index); + if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) { + ppc_hash64_stop_access(token); return H_PTEG_FULL; } + ppc_hash64_stop_access(token); } - ppc_hash64_store_hpte1(env, hpte, ptel); - /* eieio(); FIXME: need some sort of barrier for smp? */ - ppc_hash64_store_hpte0(env, hpte, pteh | HPTE64_V_HPTE_DIRTY); - args[0] = pte_index + i; + ppc_hash64_store_hpte(env, pte_index + index, + pteh | HPTE64_V_HPTE_DIRTY, ptel); + + args[0] = pte_index + index; return H_SUCCESS; } @@ -133,17 +148,17 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, target_ulong flags, target_ulong *vp, target_ulong *rp) { - hwaddr hpte; + uint64_t token; target_ulong v, r, rb; - if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, ptex)) { return REMOVE_PARM; } - hpte = ptex * HASH_PTE_SIZE_64; - - v = ppc_hash64_load_hpte0(env, hpte); - r = ppc_hash64_load_hpte1(env, hpte); + token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex); + v = ppc_hash64_load_hpte0(env, token, 0); + r = ppc_hash64_load_hpte1(env, token, 0); + ppc_hash64_stop_access(token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || @@ -152,7 +167,7 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, } *vp = v; *rp = r; - ppc_hash64_store_hpte0(env, hpte, HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0); rb = compute_tlbie_rb(v, r, ptex); ppc_tlb_invalidate_one(env, rb); return REMOVE_SUCCESS; @@ -259,17 +274,17 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; - hwaddr hpte; + uint64_t token; target_ulong v, r, rb; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } - hpte = pte_index * HASH_PTE_SIZE_64; - - v = ppc_hash64_load_hpte0(env, hpte); - r = ppc_hash64_load_hpte1(env, hpte); + token = ppc_hash64_start_access(cpu, pte_index); + v = ppc_hash64_load_hpte0(env, token, 0); + r = ppc_hash64_load_hpte1(env, token, 0); + ppc_hash64_stop_access(token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { @@ -282,11 +297,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, r |= (flags << 48) & HPTE64_R_KEY_HI; r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); rb = compute_tlbie_rb(v, r, pte_index); - ppc_hash64_store_hpte0(env, hpte, (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, pte_index, + (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); ppc_tlb_invalidate_one(env, rb); - ppc_hash64_store_hpte1(env, hpte, r); /* Don't need a memory barrier, due to qemu's global lock */ - ppc_hash64_store_hpte0(env, hpte, v | HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r); return H_SUCCESS; } @@ -299,7 +314,7 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint8_t *hpte; int i, ridx, n_entries = 1; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } @@ -467,13 +482,13 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; CPUPPCState *tenv; - CPUState *tcpu; + PowerPCCPU *tcpu; - tcpu = qemu_get_cpu(procno); + tcpu = ppc_get_vcpu_by_dt_id(procno); if (!tcpu) { return H_PARAMETER; } - tenv = tcpu->env_ptr; + tenv = &tcpu->env; switch (flags) { case FLAGS_REGISTER_VPA: diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index ef45f4f0cc..d9fe946818 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -243,6 +243,42 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, return ret; } +static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, + target_ulong *tce) +{ + if (ioba >= tcet->window_size) { + hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x" + TARGET_FMT_lx "\n", ioba); + return H_PARAMETER; + } + + *tce = tcet->table[ioba >> SPAPR_TCE_PAGE_SHIFT]; + + return H_SUCCESS; +} + +static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong liobn = args[0]; + target_ulong ioba = args[1]; + target_ulong tce = 0; + target_ulong ret = H_PARAMETER; + sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + + ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1); + + if (tcet) { + ret = get_tce_emu(tcet, ioba, &tce); + if (!ret) { + args[0] = tce; + } + } + trace_spapr_iommu_get(liobn, ioba, ret, tce); + + return ret; +} + int spapr_dma_dt(void *fdt, int node_off, const char *propname, uint32_t liobn, uint64_t window, uint32_t size) { @@ -295,6 +331,7 @@ static void spapr_tce_table_class_init(ObjectClass *klass, void *data) /* hcall-tce */ spapr_register_hypercall(H_PUT_TCE, h_put_tce); + spapr_register_hypercall(H_GET_TCE, h_get_tce); } static TypeInfo spapr_tce_table_info = { diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 4c7c3aec12..cea9469872 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -469,6 +469,8 @@ static const MemoryRegionOps spapr_msi_ops = { void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr) { + uint64_t window_size = 4096; + /* * As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, * we need to allocate some memory to catch those writes coming @@ -476,10 +478,19 @@ void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr) * As MSIMessage:addr is going to be the same and MSIMessage:data * is going to be a VIRQ number, 4 bytes of the MSI MR will only * be used. + * + * For KVM we want to ensure that this memory is a full page so that + * our memory slot is of page size granularity. */ +#ifdef CONFIG_KVM + if (kvm_enabled()) { + window_size = getpagesize(); + } +#endif + spapr->msi_win_addr = addr; memory_region_init_io(&spapr->msiwindow, NULL, &spapr_msi_ops, spapr, - "msi", getpagesize()); + "msi", window_size); memory_region_add_subregion(get_system_memory(), spapr->msi_win_addr, &spapr->msiwindow); } @@ -728,6 +739,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) dc->props = spapr_phb_properties; dc->reset = spapr_phb_reset; dc->vmsd = &vmstate_spapr_pci; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->cannot_instantiate_with_device_add_yet = false; } static const TypeInfo spapr_phb_info = { diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 1cb276de05..73860d0486 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -131,7 +131,7 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, uint32_t nret, target_ulong rets) { target_ulong id; - CPUState *cpu; + PowerPCCPU *cpu; if (nargs != 1 || nret != 2) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -139,9 +139,9 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, } id = rtas_ld(args, 0); - cpu = qemu_get_cpu(id); + cpu = ppc_get_vcpu_by_dt_id(id); if (cpu != NULL) { - if (cpu->halted) { + if (CPU(cpu)->halted) { rtas_st(rets, 1, 0); } else { rtas_st(rets, 1, 2); @@ -161,7 +161,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { target_ulong id, start, r3; - CPUState *cs; + PowerPCCPU *cpu; if (nargs != 3 || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -172,9 +172,9 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, start = rtas_ld(args, 1); r3 = rtas_ld(args, 2); - cs = qemu_get_cpu(id); - if (cs != NULL) { - PowerPCCPU *cpu = POWERPC_CPU(cs); + cpu = ppc_get_vcpu_by_dt_id(id); + if (cpu != NULL) { + CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; if (!cs->halted) { diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 85a0e537b9..ce8ea91e8b 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -174,6 +174,19 @@ static int xilinx_load_device_tree(hwaddr addr, if (!fdt) { return 0; } + + r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (r < 0) { + error_report("couldn't set /chosen/linux,initrd-start"); + } + + r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (r < 0) { + error_report("couldn't set /chosen/linux,initrd-end"); + } + r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); if (r < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -187,6 +200,8 @@ static void virtex_init(QEMUMachineInitArgs *args) const char *cpu_model = args->cpu_model; const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; + hwaddr initrd_base = 0; + int initrd_size = 0; MemoryRegion *address_space_mem = get_system_memory(); DeviceState *dev; PowerPCCPU *cpu; @@ -259,10 +274,27 @@ static void virtex_init(QEMUMachineInitArgs *args) boot_info.ima_size = kernel_size; + /* Load initrd. */ + if (args->initrd_filename) { + initrd_base = high = ROUND_UP(high, 4); + initrd_size = load_image_targphys(args->initrd_filename, + high, ram_size - high); + + if (initrd_size < 0) { + error_report("couldn't load ram disk '%s'", + args->initrd_filename); + exit(1); + } + high = ROUND_UP(high + initrd_size, 4); + } + /* Provide a device-tree. */ boot_info.fdt = high + (8192 * 2); boot_info.fdt &= ~8191; - xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline); + + xilinx_load_device_tree(boot_info.fdt, ram_size, + initrd_base, initrd_size, + kernel_cmdline); } env->load_info = &boot_info; } diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 75b04b45af..7074d2b3d5 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -116,6 +116,15 @@ void css_conditional_io_interrupt(SubchDev *sch) } } +void css_adapter_interrupt(uint8_t isc) +{ + S390CPU *cpu = s390_cpu_addr2state(0); + uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; + + trace_css_adapter_interrupt(isc); + s390_io_interrupt(cpu, 0, 0, 0, io_int_word); +} + static void sch_handle_clear_func(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; @@ -1259,6 +1268,7 @@ void css_reset_sch(SubchDev *sch) sch->channel_prog = 0x0; sch->last_cmd_valid = false; sch->orb = NULL; + sch->thinint_active = false; } void css_reset(void) diff --git a/hw/s390x/css.h b/hw/s390x/css.h index b536ab5957..e9b440540d 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -77,6 +77,7 @@ struct SubchDev { CCW1 last_cmd; bool last_cmd_valid; ORB *orb; + bool thinint_active; /* transport-provided data: */ int (*ccw_cb) (SubchDev *, CCW1); SenseId id; @@ -97,4 +98,5 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, int hotplugged, int add); void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); +void css_adapter_interrupt(uint8_t isc); #endif diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 04fb1a8e05..32d38a08f6 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -98,10 +98,10 @@ static int s390_ipl_init(SysBusDevice *dev) uint64_t pentry = KERN_IMAGE_START; kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL, NULL, 1, ELF_MACHINE, 0); - if (kernel_size == -1) { + if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); } - if (kernel_size == -1) { + if (kernel_size < 0) { fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel); return -1; } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index f6e0e3e4ae..a01801e342 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1,7 +1,7 @@ /* * virtio ccw target implementation * - * Copyright 2012 IBM Corp. + * Copyright 2012,2014 IBM Corp. * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> * * This work is licensed under the terms of the GNU GPL, version 2 or (at @@ -188,6 +188,13 @@ typedef struct VirtioFeatDesc { uint8_t index; } QEMU_PACKED VirtioFeatDesc; +typedef struct VirtioThinintInfo { + hwaddr summary_indicator; + hwaddr device_indicator; + uint64_t ind_bit; + uint8_t isc; +} QEMU_PACKED VirtioThinintInfo; + /* Specify where the virtqueues for the subchannel are in guest memory. */ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, uint16_t index, uint16_t num) @@ -237,6 +244,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) bool check_len; int len; hwaddr hw_len; + VirtioThinintInfo *thinint; if (!dev) { return -EINVAL; @@ -428,6 +436,11 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EINVAL; break; } + if (sch->thinint_active) { + /* Trigger a command reject. */ + ret = -ENOSYS; + break; + } if (!ccw.cda) { ret = -EFAULT; } else { @@ -480,6 +493,42 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = 0; } break; + case CCW_CMD_SET_IND_ADAPTER: + if (check_len) { + if (ccw.count != sizeof(*thinint)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(*thinint)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + len = sizeof(*thinint); + hw_len = len; + if (!ccw.cda) { + ret = -EFAULT; + } else if (dev->indicators && !sch->thinint_active) { + /* Trigger a command reject. */ + ret = -ENOSYS; + } else { + thinint = cpu_physical_memory_map(ccw.cda, &hw_len, 0); + if (!thinint) { + ret = -EFAULT; + } else { + len = hw_len; + dev->summary_indicator = thinint->summary_indicator; + dev->indicators = thinint->device_indicator; + dev->thinint_isc = thinint->isc; + dev->ind_bit = thinint->ind_bit; + cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); + sch->thinint_active = ((dev->indicators != 0) && + (dev->summary_indicator != 0)); + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + } + } + break; default: ret = -ENOSYS; break; @@ -511,6 +560,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) sch->channel_prog = 0x0; sch->last_cmd_valid = false; sch->orb = NULL; + sch->thinint_active = false; /* * Use a device number if provided. Otherwise, fall back to subchannel * number. @@ -858,6 +908,28 @@ static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) return container_of(d, VirtioCcwDevice, parent_obj); } +static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, + uint8_t to_be_set) +{ + uint8_t ind_old, ind_new; + hwaddr len = 1; + uint8_t *ind_addr; + + ind_addr = cpu_physical_memory_map(ind_loc, &len, 1); + if (!ind_addr) { + error_report("%s(%x.%x.%04x): unable to access indicator", + __func__, sch->cssid, sch->ssid, sch->schid); + return -1; + } + do { + ind_old = *ind_addr; + ind_new = ind_old | to_be_set; + } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); + cpu_physical_memory_unmap(ind_addr, len, 1, len); + + return ind_old; +} + static void virtio_ccw_notify(DeviceState *d, uint16_t vector) { VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); @@ -872,9 +944,26 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) if (!dev->indicators) { return; } - indicators = ldq_phys(&address_space_memory, dev->indicators); - indicators |= 1ULL << vector; - stq_phys(&address_space_memory, dev->indicators, indicators); + if (sch->thinint_active) { + /* + * In the adapter interrupt case, indicators points to a + * memory area that may be (way) larger than 64 bit and + * ind_bit indicates the start of the indicators in a big + * endian notation. + */ + virtio_set_ind_atomic(sch, dev->indicators + + (dev->ind_bit + vector) / 8, + 0x80 >> ((dev->ind_bit + vector) % 8)); + if (!virtio_set_ind_atomic(sch, dev->summary_indicator, + 0x01)) { + css_adapter_interrupt(dev->thinint_isc); + } + } else { + indicators = ldq_phys(&address_space_memory, dev->indicators); + indicators |= 1ULL << vector; + stq_phys(&address_space_memory, dev->indicators, indicators); + css_conditional_io_interrupt(sch); + } } else { if (!dev->indicators2) { return; @@ -883,10 +972,8 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) indicators = ldq_phys(&address_space_memory, dev->indicators2); indicators |= 1ULL << vector; stq_phys(&address_space_memory, dev->indicators2, indicators); + css_conditional_io_interrupt(sch); } - - css_conditional_io_interrupt(sch); - } static unsigned virtio_ccw_get_features(DeviceState *d) @@ -907,6 +994,7 @@ static void virtio_ccw_reset(DeviceState *d) css_reset_sch(dev->sch); dev->indicators = 0; dev->indicators2 = 0; + dev->summary_indicator = 0; } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 00932c746d..4393e44814 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -38,6 +38,7 @@ #define CCW_CMD_SET_IND 0x43 #define CCW_CMD_SET_CONF_IND 0x53 #define CCW_CMD_READ_VQ_CONF 0x32 +#define CCW_CMD_SET_IND_ADAPTER 0x73 #define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" #define VIRTIO_CCW_DEVICE(obj) \ @@ -83,9 +84,12 @@ struct VirtioCcwDevice { bool ioeventfd_started; bool ioeventfd_disabled; uint32_t flags; + uint8_t thinint_isc; /* Guest provided values: */ hwaddr indicators; hwaddr indicators2; + hwaddr summary_indicator; + uint64_t ind_bit; }; /* virtual css bus type */ diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index e8bca390dd..b3835c821d 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -62,6 +62,8 @@ #define SRP_RSP_SENSE_DATA_LEN 18 +#define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL + typedef union vscsi_crq { struct viosrp_crq s; uint8_t raw[16]; @@ -719,12 +721,70 @@ static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) } } +static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) +{ + BusChild *kid; + int i, len, n, rc; + uint8_t *resp_data; + bool found_lun0; + + n = 0; + found_lun0 = false; + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *dev = SCSI_DEVICE(kid->child); + + n += 8; + if (dev->channel == 0 && dev->id == 0 && dev->lun == 0) { + found_lun0 = true; + } + } + if (!found_lun0) { + n += 8; + } + len = n+8; + + resp_data = g_malloc0(len); + memset(resp_data, 0, len); + stl_be_p(resp_data, n); + i = found_lun0 ? 8 : 16; + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + DeviceState *qdev = kid->child; + SCSIDevice *dev = SCSI_DEVICE(qdev); + + if (dev->id == 0 && dev->channel == 0) { + resp_data[i] = 0; /* Use simple LUN for 0 (SAM5 4.7.7.1) */ + } else { + resp_data[i] = (2 << 6); /* Otherwise LUN addressing (4.7.7.4) */ + } + resp_data[i] |= dev->id; + resp_data[i+1] = (dev->channel << 5); + resp_data[i+1] |= dev->lun; + i += 8; + } + + vscsi_preprocess_desc(req); + rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len); + g_free(resp_data); + if (rc < 0) { + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + } else { + vscsi_send_rsp(s, req, 0, len - rc, 0); + } +} + static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) { union srp_iu *srp = &req->iu.srp; SCSIDevice *sdev; int n, lun; + if ((srp->cmd.lun == 0 || be64_to_cpu(srp->cmd.lun) == SRP_REPORT_LUNS_WLUN) + && srp->cmd.cdb[0] == REPORT_LUNS) { + vscsi_report_luns(s, req); + return 0; + } + sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun); if (!sdev) { DPRINTF("VSCSI: Command for lun %08" PRIx64 " with no drive\n", diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index e05cbc131e..42913b6a5a 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -98,7 +98,7 @@ static void puv3_load_kernel(const char *kernel_filename) } /* cheat curses that we have a graphic console, only under ocd console */ - graphic_console_init(NULL, &no_ops, NULL); + graphic_console_init(NULL, 0, &no_ops, NULL); } static void puv3_init(QEMUMachineInitArgs *args) diff --git a/include/hw/boards.h b/include/hw/boards.h index 2151460f9e..c2096e6ba2 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -4,10 +4,9 @@ #define HW_BOARDS_H #include "sysemu/blockdev.h" +#include "sysemu/qemumachine.h" #include "hw/qdev.h" -typedef struct QEMUMachine QEMUMachine; - typedef struct QEMUMachineInitArgs { const QEMUMachine *machine; ram_addr_t ram_size; @@ -24,6 +23,8 @@ typedef void QEMUMachineResetFunc(void); typedef void QEMUMachineHotAddCPUFunc(const int64_t id, Error **errp); +typedef int QEMUMachineGetKvmtypeFunc(const char *arg); + struct QEMUMachine { const char *name; const char *alias; @@ -31,6 +32,7 @@ struct QEMUMachine { QEMUMachineInitFunc *init; QEMUMachineResetFunc *reset; QEMUMachineHotAddCPUFunc *hot_add_cpu; + QEMUMachineGetKvmtypeFunc *kvm_type; BlockInterfaceType block_default_type; int max_cpus; unsigned int no_serial:1, diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index acc701e3a4..c6b5129bab 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -201,6 +201,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, uint64_t addr, low = (uint64_t)-1, high = 0; uint8_t *data = NULL; char label[128]; + int ret = ELF_LOAD_FAILED; if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) goto fail; @@ -211,22 +212,30 @@ static int glue(load_elf, SZ)(const char *name, int fd, switch (elf_machine) { case EM_PPC64: if (EM_PPC64 != ehdr.e_machine) - if (EM_PPC != ehdr.e_machine) + if (EM_PPC != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; goto fail; + } break; case EM_X86_64: if (EM_X86_64 != ehdr.e_machine) - if (EM_386 != ehdr.e_machine) + if (EM_386 != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; goto fail; + } break; case EM_MICROBLAZE: if (EM_MICROBLAZE != ehdr.e_machine) - if (EM_MICROBLAZE_OLD != ehdr.e_machine) + if (EM_MICROBLAZE_OLD != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; goto fail; + } break; default: - if (elf_machine != ehdr.e_machine) + if (elf_machine != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; goto fail; + } } if (pentry) @@ -305,5 +314,5 @@ static int glue(load_elf, SZ)(const char *name, int fd, fail: g_free(data); g_free(phdr); - return -1; + return ret; } diff --git a/include/hw/loader.h b/include/hw/loader.h index 91b01224a3..aaf08c377e 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -15,6 +15,12 @@ int get_image_size(const char *filename); int load_image(const char *filename, uint8_t *addr); /* deprecated */ int load_image_targphys(const char *filename, hwaddr, uint64_t max_sz); + +#define ELF_LOAD_FAILED -1 +#define ELF_LOAD_NOT_ELF -2 +#define ELF_LOAD_WRONG_ARCH -3 +#define ELF_LOAD_WRONG_ENDIAN -4 +const char *load_elf_strerror(int error); int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 276b336c09..1ed0691716 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -176,6 +176,8 @@ struct BusClass { void (*reset)(BusState *bus); /* maximum devices allowed on the bus, 0: no limit. */ int max_dev; + /* number of automatically allocated bus ids (e.g. ide.0) */ + int automatic_ids; }; typedef struct BusChild { diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index e1f88bf9cf..e1818213b2 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -10,6 +10,7 @@ #include "hw/irq.h" #include "qemu-common.h" +#include "sysemu/qemumachine.h" /* xen-machine.c */ enum xen_mode { @@ -36,7 +37,7 @@ void xen_cmos_set_s3_resume(void *opaque, int irq, int level); qemu_irq *xen_interrupt_controller_init(void); -int xen_init(void); +int xen_init(QEMUMachine *machine); int xen_hvm_init(MemoryRegion **ram_memory); void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 285c5fbab8..d4f21c947f 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -44,9 +44,37 @@ static inline void muls64(uint64_t *plow, uint64_t *phigh, *plow = r; *phigh = r >> 64; } + +static inline int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) +{ + if (divisor == 0) { + return 1; + } else { + __uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow; + __uint128_t result = dividend / divisor; + *plow = result; + *phigh = dividend % divisor; + return result > UINT64_MAX; + } +} + +static inline int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) +{ + if (divisor == 0) { + return 1; + } else { + __int128_t dividend = ((__int128_t)*phigh << 64) | *plow; + __int128_t result = dividend / divisor; + *plow = result; + *phigh = dividend % divisor; + return result != *plow; + } +} #else void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b); void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b); +int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); +int divs128(int64_t *plow, int64_t *phigh, int64_t divisor); #endif /** diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index a02d67cd5a..ed01998aa8 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -18,6 +18,7 @@ #include "config-host.h" #include "qemu/queue.h" #include "qom/cpu.h" +#include "sysemu/qemumachine.h" #ifdef CONFIG_KVM #include <linux/kvm.h> @@ -152,7 +153,7 @@ extern KVMState *kvm_state; /* external API */ -int kvm_init(void); +int kvm_init(QEMUMachine *machine); int kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); diff --git a/include/sysemu/qemumachine.h b/include/sysemu/qemumachine.h new file mode 100644 index 0000000000..4cefd56b67 --- /dev/null +++ b/include/sysemu/qemumachine.h @@ -0,0 +1,16 @@ +/* + * QEMU Machine typedef + * + * Copyright Alexander Graf <agraf@suse.de> + * + * 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 QEMUMACHINE_H +#define QEMUMACHINE_H + +typedef struct QEMUMachine QEMUMachine; + +#endif /* !QEMUMACHINE_H */ diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 28f4875112..e62281d4bf 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -16,6 +16,7 @@ #include "qemu-common.h" #include "qapi/error.h" +#include "sysemu/qemumachine.h" extern bool qtest_allowed; @@ -26,7 +27,7 @@ static inline bool qtest_enabled(void) bool qtest_driver(void); -int qtest_init_accel(void); +int qtest_init_accel(QEMUMachine *machine); void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp); static inline int qtest_available(void) diff --git a/include/ui/console.h b/include/ui/console.h index 4156a876e1..08a38eab13 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -14,6 +14,8 @@ #define MOUSE_EVENT_LBUTTON 0x01 #define MOUSE_EVENT_RBUTTON 0x02 #define MOUSE_EVENT_MBUTTON 0x04 +#define MOUSE_EVENT_WHEELUP 0x08 +#define MOUSE_EVENT_WHEELDN 0x10 /* identical to the ps/2 keyboard bits */ #define QEMU_SCROLL_LOCK_LED (1 << 0) @@ -44,17 +46,7 @@ void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry); QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, void *opaque); void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry); -void kbd_put_keycode(int keycode); void kbd_put_ledstate(int ledstate); -void kbd_mouse_event(int dx, int dy, int dz, int buttons_state); - -/* Does the current mouse generate absolute events */ -int kbd_mouse_is_absolute(void); -void qemu_add_mouse_mode_change_notifier(Notifier *notify); -void qemu_remove_mouse_mode_change_notifier(Notifier *notify); - -/* Of all the mice, is there one that generates absolute events */ -int kbd_mouse_has_absolute(void); struct MouseTransformInfo { /* Touchscreen resolution */ @@ -128,6 +120,14 @@ struct DisplaySurface { struct PixelFormat pf; }; +typedef struct QemuUIInfo { + /* geometry */ + int xoff; + int yoff; + uint32_t width; + uint32_t height; +} QemuUIInfo; + /* cursor data format is 32bit RGBA */ typedef struct QEMUCursor { int width, height; @@ -212,6 +212,8 @@ void update_displaychangelistener(DisplayChangeListener *dcl, uint64_t interval); void unregister_displaychangelistener(DisplayChangeListener *dcl); +int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info); + void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h); void dpy_gfx_replace_surface(QemuConsole *con, DisplaySurface *surface); @@ -274,9 +276,10 @@ typedef struct GraphicHwOps { void (*gfx_update)(void *opaque); void (*text_update)(void *opaque, console_ch_t *text); void (*update_interval)(void *opaque, uint64_t interval); + int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info); } GraphicHwOps; -QemuConsole *graphic_console_init(DeviceState *dev, +QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, const GraphicHwOps *ops, void *opaque); @@ -285,10 +288,15 @@ void graphic_hw_invalidate(QemuConsole *con); void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata); QemuConsole *qemu_console_lookup_by_index(unsigned int index); -QemuConsole *qemu_console_lookup_by_device(DeviceState *dev); +QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head); bool qemu_console_is_visible(QemuConsole *con); bool qemu_console_is_graphic(QemuConsole *con); bool qemu_console_is_fixedsize(QemuConsole *con); +int qemu_console_get_index(QemuConsole *con); +uint32_t qemu_console_get_head(QemuConsole *con); +QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con); +int qemu_console_get_width(QemuConsole *con, int fallback); +int qemu_console_get_height(QemuConsole *con, int fallback); void text_consoles_set_display(DisplayState *ds); void console_select(unsigned int index); @@ -334,7 +342,6 @@ void curses_display_init(DisplayState *ds, int full_screen); /* input.c */ int index_from_key(const char *key); -int index_from_keycode(int code); /* gtk.c */ void early_gtk_display_init(void); diff --git a/include/ui/input.h b/include/ui/input.h new file mode 100644 index 0000000000..4976f3da2c --- /dev/null +++ b/include/ui/input.h @@ -0,0 +1,56 @@ +#ifndef INPUT_H +#define INPUT_H + +#include "qapi-types.h" + +#define INPUT_EVENT_MASK_KEY (1<<INPUT_EVENT_KIND_KEY) +#define INPUT_EVENT_MASK_BTN (1<<INPUT_EVENT_KIND_BTN) +#define INPUT_EVENT_MASK_REL (1<<INPUT_EVENT_KIND_REL) +#define INPUT_EVENT_MASK_ABS (1<<INPUT_EVENT_KIND_ABS) + +#define INPUT_EVENT_ABS_SIZE 0x8000 + +typedef struct QemuInputHandler QemuInputHandler; +typedef struct QemuInputHandlerState QemuInputHandlerState; + +typedef void (*QemuInputHandlerEvent)(DeviceState *dev, QemuConsole *src, + InputEvent *evt); +typedef void (*QemuInputHandlerSync)(DeviceState *dev); + +struct QemuInputHandler { + const char *name; + uint32_t mask; + QemuInputHandlerEvent event; + QemuInputHandlerSync sync; +}; + +QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, + QemuInputHandler *handler); +void qemu_input_handler_activate(QemuInputHandlerState *s); +void qemu_input_handler_unregister(QemuInputHandlerState *s); +void qemu_input_event_send(QemuConsole *src, InputEvent *evt); +void qemu_input_event_sync(void); + +InputEvent *qemu_input_event_new_key(KeyValue *key, bool down); +void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); +void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down); +void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down); + +InputEvent *qemu_input_event_new_btn(InputButton btn, bool down); +void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down); +void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, + uint32_t button_old, uint32_t button_new); + +bool qemu_input_is_absolute(void); +int qemu_input_scale_axis(int value, int size_in, int size_out); +InputEvent *qemu_input_event_new_move(InputEventKind kind, + InputAxis axis, int value); +void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value); +void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, + int value, int size); + +void qemu_input_check_mode_change(void); +void qemu_add_mouse_mode_change_notifier(Notifier *notify); +void qemu_remove_mouse_mode_change_notifier(Notifier *notify); + +#endif /* INPUT_H */ @@ -36,6 +36,8 @@ #include "qemu/event_notifier.h" #include "trace.h" +#include "hw/boards.h" + /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD #include <sys/eventfd.h> @@ -1339,7 +1341,7 @@ static int kvm_max_vcpus(KVMState *s) return (ret) ? ret : kvm_recommended_vcpus(s); } -int kvm_init(void) +int kvm_init(QEMUMachine *machine) { static const char upgrade_note[] = "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" @@ -1356,7 +1358,8 @@ int kvm_init(void) KVMState *s; const KVMCapabilityInfo *missing_cap; int ret; - int i; + int i, type = 0; + const char *kvm_type; s = g_malloc0(sizeof(KVMState)); @@ -1430,8 +1433,16 @@ int kvm_init(void) nc++; } + kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); + if (machine->kvm_type) { + type = machine->kvm_type(kvm_type); + } else if (kvm_type) { + fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); + goto err; + } + do { - ret = kvm_ioctl(s, KVM_CREATE_VM, 0); + ret = kvm_ioctl(s, KVM_CREATE_VM, type); } while (ret == -EINTR); if (ret < 0) { diff --git a/kvm-stub.c b/kvm-stub.c index e979f76d07..4ef084e84a 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -14,6 +14,7 @@ #include "hw/hw.h" #include "cpu.h" #include "sysemu/kvm.h" +#include "sysemu/qemumachine.h" #ifndef CONFIG_USER_ONLY #include "hw/pci/msi.h" @@ -34,7 +35,7 @@ int kvm_init_vcpu(CPUState *cpu) return -ENOSYS; } -int kvm_init(void) +int kvm_init(QEMUMachine *machine) { return -ENOSYS; } diff --git a/linux-user/main.c b/linux-user/main.c index 919297736c..be9491bc7d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1492,7 +1492,7 @@ static int do_store_exclusive(CPUPPCState *env) { target_ulong addr; target_ulong page_addr; - target_ulong val; + target_ulong val, val2 __attribute__((unused)); int flags; int segv = 0; @@ -1515,6 +1515,13 @@ static int do_store_exclusive(CPUPPCState *env) case 4: segv = get_user_u32(val, addr); break; #if defined(TARGET_PPC64) case 8: segv = get_user_u64(val, addr); break; + case 16: { + segv = get_user_u64(val, addr); + if (!segv) { + segv = get_user_u64(val2, addr + 8); + } + break; + } #endif default: abort(); } @@ -1526,6 +1533,15 @@ static int do_store_exclusive(CPUPPCState *env) case 4: segv = put_user_u32(val, addr); break; #if defined(TARGET_PPC64) case 8: segv = put_user_u64(val, addr); break; + case 16: { + if (val2 == env->reserve_val2) { + segv = put_user_u64(val, addr); + if (!segv) { + segv = put_user_u64(val2, addr + 8); + } + } + break; + } #endif default: abort(); } @@ -39,6 +39,7 @@ #include "monitor/monitor.h" #include "qemu/readline.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/blockdev.h" #include "audio/audio.h" #include "disas/disas.h" @@ -1463,23 +1464,43 @@ static int mouse_button_state; static void do_mouse_move(Monitor *mon, const QDict *qdict) { - int dx, dy, dz; + int dx, dy, dz, button; const char *dx_str = qdict_get_str(qdict, "dx_str"); const char *dy_str = qdict_get_str(qdict, "dy_str"); const char *dz_str = qdict_get_try_str(qdict, "dz_str"); + dx = strtol(dx_str, NULL, 0); dy = strtol(dy_str, NULL, 0); - dz = 0; - if (dz_str) + qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); + qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); + + if (dz_str) { dz = strtol(dz_str, NULL, 0); - kbd_mouse_event(dx, dy, dz, mouse_button_state); + if (dz != 0) { + button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; + qemu_input_queue_btn(NULL, button, true); + qemu_input_event_sync(); + qemu_input_queue_btn(NULL, button, false); + } + } + qemu_input_event_sync(); } static void do_mouse_button(Monitor *mon, const QDict *qdict) { + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, + [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, + [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, + }; int button_state = qdict_get_int(qdict, "button_state"); + + if (mouse_button_state == button_state) { + return; + } + qemu_input_update_buttons(NULL, bmap, mouse_button_state, button_state); + qemu_input_event_sync(); mouse_button_state = button_state; - kbd_mouse_event(0, 0, 0, mouse_button_state); } static void do_ioport_read(Monitor *mon, const QDict *qdict) diff --git a/qapi-schema.json b/qapi-schema.json index 193e7e4d3f..6c381b7306 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3555,9 +3555,12 @@ # This is used by the send-key command. # # Since: 1.3.0 +# +# 'unmapped' and 'pause' since 2.0 ## { 'enum': 'QKeyCode', - 'data': [ 'shift', 'shift_r', 'alt', 'alt_r', 'altgr', 'altgr_r', 'ctrl', + 'data': [ 'unmapped', + 'shift', 'shift_r', 'alt', 'alt_r', 'altgr', 'altgr_r', 'ctrl', 'ctrl_r', 'menu', 'esc', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'minus', 'equal', 'backspace', 'tab', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'bracket_left', 'bracket_right', @@ -3571,7 +3574,7 @@ 'kp_9', 'less', 'f11', 'f12', 'print', 'home', 'pgup', 'pgdn', 'end', 'left', 'up', 'down', 'right', 'insert', 'delete', 'stop', 'again', 'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut', - 'lf', 'help', 'meta_l', 'meta_r', 'compose' ] } + 'lf', 'help', 'meta_l', 'meta_r', 'compose', 'pause' ] } ## # @KeyValue @@ -4566,3 +4569,79 @@ # Since: 1.7 ## { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } } + +## +# @InputButton +# +# Button of a pointer input device (mouse, tablet). +# +# Since: 2.0 +## +{ 'enum' : 'InputButton', + 'data' : [ 'Left', 'Middle', 'Right', 'WheelUp', 'WheelDown' ] } + +## +# @InputButton +# +# Position axis of a pointer input device (mouse, tablet). +# +# Since: 2.0 +## +{ 'enum' : 'InputAxis', + 'data' : [ 'X', 'Y' ] } + +## +# @InputKeyEvent +# +# Keyboard input event. +# +# @key: Which key this event is for. +# @down: True for key-down and false for key-up events. +# +# Since: 2.0 +## +{ 'type' : 'InputKeyEvent', + 'data' : { 'key' : 'KeyValue', + 'down' : 'bool' } } + +## +# @InputBtnEvent +# +# Pointer button input event. +# +# @button: Which button this event is for. +# @down: True for key-down and false for key-up events. +# +# Since: 2.0 +## +{ 'type' : 'InputBtnEvent', + 'data' : { 'button' : 'InputButton', + 'down' : 'bool' } } + +## +# @InputMoveEvent +# +# Pointer motion input event. +# +# @axis: Which axis is referenced by @value. +# @value: Pointer position. For absolute coordinates the +# valid range is 0 -> 0x7ffff +# +# Since: 2.0 +## +{ 'type' : 'InputMoveEvent', + 'data' : { 'axis' : 'InputAxis', + 'value' : 'int' } } + +## +# @InputEvent +# +# Input event union. +# +# Since: 2.0 +## +{ 'union' : 'InputEvent', + 'data' : { 'key' : 'InputKeyEvent', + 'btn' : 'InputBtnEvent', + 'rel' : 'InputMoveEvent', + 'abs' : 'InputMoveEvent' } } @@ -500,7 +500,7 @@ static void qtest_event(void *opaque, int event) } } -int qtest_init_accel(void) +int qtest_init_accel(QEMUMachine *machine) { configure_icount("0"); diff --git a/target-ppc/STATUS b/target-ppc/STATUS index c8e9018bfc..a4d48a7ca2 100644 --- a/target-ppc/STATUS +++ b/target-ppc/STATUS @@ -377,15 +377,6 @@ MMU OK EXCP KO partially implemented Remarks: Should be able to boot but there is no hw platform currently emulated. -PowerPC 970GX: -INSN KO Altivec missing and more -SPR KO -MSR ? -IRQ OK -MMU OK -EXCP KO partially implemented -Remarks: Should be able to boot but there is no hw platform currently emulated. - PowerPC Cell: INSN KO Altivec missing and more SPR KO diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c index 7c9466fc07..f6c9b3ab01 100644 --- a/target-ppc/cpu-models.c +++ b/target-ppc/cpu-models.c @@ -1156,8 +1156,6 @@ "PowerPC 970FX v3.0 (G5)") POWERPC_DEF("970fx_v3.1", CPU_POWERPC_970FX_v31, 970FX, "PowerPC 970FX v3.1 (G5)") - POWERPC_DEF("970gx", CPU_POWERPC_970GX, 970GX, - "PowerPC 970GX (G5)") POWERPC_DEF("970mp_v1.0", CPU_POWERPC_970MP_v10, 970MP, "PowerPC 970MP v1.0") POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970MP, diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h index 49ba4a4522..644a126459 100644 --- a/target-ppc/cpu-models.h +++ b/target-ppc/cpu-models.h @@ -570,7 +570,6 @@ enum { CPU_POWERPC_970FX_v21 = 0x003C0201, CPU_POWERPC_970FX_v30 = 0x003C0300, CPU_POWERPC_970FX_v31 = 0x003C0301, - CPU_POWERPC_970GX = 0x00450000, CPU_POWERPC_970MP_v10 = 0x00440100, CPU_POWERPC_970MP_v11 = 0x00440101, #define CPU_POWERPC_CELL CPU_POWERPC_CELL_v32 diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index 72b22329b0..b17c024543 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -79,6 +79,7 @@ typedef struct PowerPCCPUClass { /** * PowerPCCPU: * @env: #CPUPPCState + * @cpu_dt_id: CPU index used in the device tree. KVM uses this index too * * A PowerPC CPU. */ @@ -88,6 +89,7 @@ typedef struct PowerPCCPU { /*< public >*/ CPUPPCState env; + int cpu_dt_id; } PowerPCCPU; static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index bb847676a5..afab267485 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -352,6 +352,10 @@ union ppc_avr_t { int16_t s16[8]; int32_t s32[4]; uint64_t u64[2]; + int64_t s64[2]; +#ifdef CONFIG_INT128 + __uint128_t u128; +#endif }; #if !defined(CONFIG_USER_ONLY) @@ -926,6 +930,7 @@ struct CPUPPCState { target_ulong reserve_addr; /* Reservation value */ target_ulong reserve_val; + target_ulong reserve_val2; /* Reservation store address */ target_ulong reserve_ea; /* Reserved store source register and size */ @@ -961,6 +966,7 @@ struct CPUPPCState { #endif /* segment registers */ hwaddr htab_base; + /* mask used to normalize hash value to PTEG index */ hwaddr htab_mask; target_ulong sr[32]; /* externally stored hash table */ @@ -1250,7 +1256,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_MPC_EIE (0x050) #define SPR_MPC_EID (0x051) #define SPR_MPC_NRI (0x052) -#define SPR_CTRL (0x088) +#define SPR_UCTRL (0x088) #define SPR_MPC_CMPA (0x090) #define SPR_MPC_CMPB (0x091) #define SPR_MPC_CMPC (0x092) @@ -1259,7 +1265,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_MPC_DER (0x095) #define SPR_MPC_COUNTA (0x096) #define SPR_MPC_COUNTB (0x097) -#define SPR_UCTRL (0x098) +#define SPR_CTRL (0x098) #define SPR_MPC_CMPE (0x098) #define SPR_MPC_CMPF (0x099) #define SPR_MPC_CMPG (0x09A) @@ -1322,12 +1328,12 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_BOOKE_IAC3 (0x13A) #define SPR_HSRR1 (0x13B) #define SPR_BOOKE_IAC4 (0x13B) -#define SPR_LPCR (0x13C) #define SPR_BOOKE_DAC1 (0x13C) #define SPR_LPIDR (0x13D) #define SPR_DABR2 (0x13D) #define SPR_BOOKE_DAC2 (0x13D) #define SPR_BOOKE_DVC1 (0x13E) +#define SPR_LPCR (0x13E) #define SPR_BOOKE_DVC2 (0x13F) #define SPR_BOOKE_TSR (0x150) #define SPR_BOOKE_TCR (0x154) @@ -1508,6 +1514,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_RCPU_L2U_RA2 (0x32A) #define SPR_MPC_MD_DBRAM1 (0x32A) #define SPR_RCPU_L2U_RA3 (0x32B) +#define SPR_TAR (0x32F) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) #define SPR_440_INV2 (0x372) @@ -1875,9 +1882,31 @@ enum { PPC2_DBRX = 0x0000000000000010ULL, /* Book I 2.05 PowerPC specification */ PPC2_ISA205 = 0x0000000000000020ULL, + /* VSX additions in ISA 2.07 */ + PPC2_VSX207 = 0x0000000000000040ULL, + /* ISA 2.06B bpermd */ + PPC2_PERM_ISA206 = 0x0000000000000080ULL, + /* ISA 2.06B divide extended variants */ + PPC2_DIVE_ISA206 = 0x0000000000000100ULL, + /* ISA 2.06B larx/stcx. instructions */ + PPC2_ATOMIC_ISA206 = 0x0000000000000200ULL, + /* ISA 2.06B floating point integer conversion */ + PPC2_FP_CVT_ISA206 = 0x0000000000000400ULL, + /* ISA 2.06B floating point test instructions */ + PPC2_FP_TST_ISA206 = 0x0000000000000800ULL, + /* ISA 2.07 bctar instruction */ + PPC2_BCTAR_ISA207 = 0x0000000000001000ULL, + /* ISA 2.07 load/store quadword */ + PPC2_LSQ_ISA207 = 0x0000000000002000ULL, + /* ISA 2.07 Altivec */ + PPC2_ALTIVEC_207 = 0x0000000000004000ULL, #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ - PPC2_ISA205) + PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \ + PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | \ + PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \ + PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \ + PPC2_ALTIVEC_207) }; /*****************************************************************************/ @@ -2154,4 +2183,22 @@ static inline bool cpu_has_work(CPUState *cpu) void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); +/** + * ppc_get_vcpu_dt_id: + * @cs: a PowerPCCPU struct. + * + * Returns a device-tree ID for a CPU. + */ +int ppc_get_vcpu_dt_id(PowerPCCPU *cpu); + +/** + * ppc_get_vcpu_by_dt_id: + * @cpu_dt_id: a device tree id + * + * Searches for a CPU by @cpu_dt_id. + * + * Returns: a PowerPCCPU struct + */ +PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id); + #endif /* !defined (__CPU_PPC_H__) */ diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c index 4f6021835f..e7f329566d 100644 --- a/target-ppc/fpu_helper.c +++ b/target-ppc/fpu_helper.c @@ -50,6 +50,16 @@ static inline int isden(float64 d) return ((u.ll >> 52) & 0x7FF) == 0; } +static inline int ppc_float32_get_unbiased_exp(float32 f) +{ + return ((f >> 23) & 0xFF) - 127; +} + +static inline int ppc_float64_get_unbiased_exp(float64 f) +{ + return ((f >> 52) & 0x7FF) - 1023; +} + uint32_t helper_compute_fprf(CPUPPCState *env, uint64_t arg, uint32_t set_fprf) { CPU_DoubleU farg; @@ -106,7 +116,8 @@ uint32_t helper_compute_fprf(CPUPPCState *env, uint64_t arg, uint32_t set_fprf) } /* Floating-point invalid operations exception */ -static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) +static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op, + int set_fpcc) { uint64_t ret = 0; int ve; @@ -138,8 +149,10 @@ static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) case POWERPC_EXCP_FP_VXVC: /* Ordered comparison of NaN */ env->fpscr |= 1 << FPSCR_VXVC; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } /* We must update the target FPR before raising the exception */ if (ve != 0) { env->exception_index = POWERPC_EXCP_PROGRAM; @@ -158,8 +171,10 @@ static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) if (ve == 0) { /* Set the result to quiet NaN */ ret = 0x7FF8000000000000ULL; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } } break; case POWERPC_EXCP_FP_VXCVI: @@ -169,8 +184,10 @@ static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) if (ve == 0) { /* Set the result to quiet NaN */ ret = 0x7FF8000000000000ULL; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } } break; } @@ -505,12 +522,12 @@ uint64_t helper_fadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2) if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN addition */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); } @@ -529,12 +546,12 @@ uint64_t helper_fsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2) if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN subtraction */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); } @@ -553,12 +570,12 @@ uint64_t helper_fmul(CPUPPCState *env, uint64_t arg1, uint64_t arg2) if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN multiplication */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); } @@ -577,15 +594,15 @@ uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) { /* Division of infinity by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1); } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { /* Division of zero by zero */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN division */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); } @@ -593,107 +610,63 @@ uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) return farg1.ll; } -/* fctiw - fctiw. */ -uint64_t helper_fctiw(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int32(farg.d, &env->fp_status); - /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PowerPC 750 - */ - farg.ll |= 0xFFF80000ULL << 32; - } - return farg.ll; -} - -/* fctiwz - fctiwz. */ -uint64_t helper_fctiwz(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int32_round_to_zero(farg.d, &env->fp_status); - /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PowerPC 750 - */ - farg.ll |= 0xFFF80000ULL << 32; - } - return farg.ll; -} +#define FPU_FCTI(op, cvt, nanval) \ +uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ +{ \ + CPU_DoubleU farg; \ + \ + farg.ll = arg; \ + farg.ll = float64_to_##cvt(farg.d, &env->fp_status); \ + \ + if (unlikely(env->fp_status.float_exception_flags)) { \ + if (float64_is_any_nan(arg)) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 1); \ + if (float64_is_signaling_nan(arg)) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); \ + } \ + farg.ll = nanval; \ + } else if (env->fp_status.float_exception_flags & \ + float_flag_invalid) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 1); \ + } \ + helper_float_check_status(env); \ + } \ + return farg.ll; \ + } + +FPU_FCTI(fctiw, int32, 0x80000000U) +FPU_FCTI(fctiwz, int32_round_to_zero, 0x80000000U) +FPU_FCTI(fctiwu, uint32, 0x00000000U) +FPU_FCTI(fctiwuz, uint32_round_to_zero, 0x00000000U) #if defined(TARGET_PPC64) -/* fcfid - fcfid. */ -uint64_t helper_fcfid(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - - farg.d = int64_to_float64(arg, &env->fp_status); - return farg.ll; -} - -/* fctid - fctid. */ -uint64_t helper_fctid(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int64(farg.d, &env->fp_status); - } - return farg.ll; -} - -/* fctidz - fctidz. */ -uint64_t helper_fctidz(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; +FPU_FCTI(fctid, int64, 0x8000000000000000ULL) +FPU_FCTI(fctidz, int64_round_to_zero, 0x8000000000000000ULL) +FPU_FCTI(fctidu, uint64, 0x0000000000000000ULL) +FPU_FCTI(fctiduz, uint64_round_to_zero, 0x0000000000000000ULL) +#endif - farg.ll = arg; +#if defined(TARGET_PPC64) - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int64_round_to_zero(farg.d, &env->fp_status); - } - return farg.ll; -} +#define FPU_FCFI(op, cvtr, is_single) \ +uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ +{ \ + CPU_DoubleU farg; \ + \ + if (is_single) { \ + float32 tmp = cvtr(arg, &env->fp_status); \ + farg.d = float32_to_float64(tmp, &env->fp_status); \ + } else { \ + farg.d = cvtr(arg, &env->fp_status); \ + } \ + helper_float_check_status(env); \ + return farg.ll; \ +} + +FPU_FCFI(fcfid, int64_to_float64, 0) +FPU_FCFI(fcfids, int64_to_float32, 1) +FPU_FCFI(fcfidu, uint64_to_float64, 0) +FPU_FCFI(fcfidus, uint64_to_float32, 1) #endif @@ -706,24 +679,28 @@ static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg, if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN round */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity round */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + farg.ll = arg | 0x0008000000000000ULL; } else { + int inexact = get_float_exception_flags(&env->fp_status) & + float_flag_inexact; set_float_rounding_mode(rounding_mode, &env->fp_status); farg.ll = float64_round_to_int(farg.d, &env->fp_status); /* Restore rounding mode from FPSCR */ fpscr_set_rounding_mode(env); + + /* fri* does not set FPSCR[XX] */ + if (!inexact) { + env->fp_status.float_exception_flags &= ~float_flag_inexact; + } } + helper_float_check_status(env); return farg.ll; } uint64_t helper_frin(CPUPPCState *env, uint64_t arg) { - return do_fri(env, arg, float_round_nearest_even); + return do_fri(env, arg, float_round_ties_away); } uint64_t helper_friz(CPUPPCState *env, uint64_t arg) @@ -754,13 +731,13 @@ uint64_t helper_fmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -772,7 +749,7 @@ uint64_t helper_fmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); @@ -797,13 +774,13 @@ uint64_t helper_fmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -815,7 +792,7 @@ uint64_t helper_fmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); @@ -838,13 +815,13 @@ uint64_t helper_fnmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -856,7 +833,7 @@ uint64_t helper_fnmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); @@ -883,13 +860,13 @@ uint64_t helper_fnmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -901,7 +878,7 @@ uint64_t helper_fnmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); @@ -924,7 +901,7 @@ uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN square root */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } f32 = float64_to_float32(farg.d, &env->fp_status); farg.d = float32_to_float64(f32, &env->fp_status); @@ -941,11 +918,11 @@ uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Square root of a negative nonzero number */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT); + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); } else { if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN square root */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg.d = float64_sqrt(farg.d, &env->fp_status); } @@ -961,7 +938,7 @@ uint64_t helper_fre(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg.d = float64_div(float64_one, farg.d, &env->fp_status); return farg.d; @@ -977,7 +954,7 @@ uint64_t helper_fres(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg.d = float64_div(float64_one, farg.d, &env->fp_status); f32 = float64_to_float32(farg.d, &env->fp_status); @@ -996,11 +973,11 @@ uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Reciprocal square root of a negative nonzero number */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT); + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); } else { if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal square root */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg.d = float64_sqrt(farg.d, &env->fp_status); farg.d = float64_div(float64_one, farg.d, &env->fp_status); @@ -1026,6 +1003,73 @@ uint64_t helper_fsel(CPUPPCState *env, uint64_t arg1, uint64_t arg2, } } +uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) +{ + int fe_flag = 0; + int fg_flag = 0; + + if (unlikely(float64_is_infinity(fra) || + float64_is_infinity(frb) || + float64_is_zero(frb))) { + fe_flag = 1; + fg_flag = 1; + } else { + int e_a = ppc_float64_get_unbiased_exp(fra); + int e_b = ppc_float64_get_unbiased_exp(frb); + + if (unlikely(float64_is_any_nan(fra) || + float64_is_any_nan(frb))) { + fe_flag = 1; + } else if ((e_b <= -1022) || (e_b >= 1021)) { + fe_flag = 1; + } else if (!float64_is_zero(fra) && + (((e_a - e_b) >= 1023) || + ((e_a - e_b) <= -1021) || + (e_a <= -970))) { + fe_flag = 1; + } + + if (unlikely(float64_is_zero_or_denormal(frb))) { + /* XB is not zero because of the above check and */ + /* so must be denormalized. */ + fg_flag = 1; + } + } + + return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); +} + +uint32_t helper_ftsqrt(uint64_t frb) +{ + int fe_flag = 0; + int fg_flag = 0; + + if (unlikely(float64_is_infinity(frb) || float64_is_zero(frb))) { + fe_flag = 1; + fg_flag = 1; + } else { + int e_b = ppc_float64_get_unbiased_exp(frb); + + if (unlikely(float64_is_any_nan(frb))) { + fe_flag = 1; + } else if (unlikely(float64_is_zero(frb))) { + fe_flag = 1; + } else if (unlikely(float64_is_neg(frb))) { + fe_flag = 1; + } else if (!float64_is_zero(frb) && (e_b <= (-1022+52))) { + fe_flag = 1; + } + + if (unlikely(float64_is_zero_or_denormal(frb))) { + /* XB is not zero because of the above check and */ + /* therefore must be denormalized. */ + fg_flag = 1; + } + } + + return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); +} + void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint32_t crfD) { @@ -1053,7 +1097,7 @@ void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, && (float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d)))) { /* sNaN comparison */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } } @@ -1085,10 +1129,10 @@ void helper_fcmpo(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_signaling_nan(farg2.d)) { /* sNaN comparison */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXVC); + POWERPC_EXCP_FP_VXVC, 1); } else { /* qNaN comparison */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 1); } } } @@ -1710,3 +1754,969 @@ uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2) /* XXX: TODO: test special values (NaN, infinites, ...) */ return helper_efdtsteq(env, op1, op2); } + +#define DECODE_SPLIT(opcode, shift1, nb1, shift2, nb2) \ + (((((opcode) >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \ + (((opcode) >> (shift2)) & ((1 << (nb2)) - 1))) + +#define xT(opcode) DECODE_SPLIT(opcode, 0, 1, 21, 5) +#define xA(opcode) DECODE_SPLIT(opcode, 2, 1, 16, 5) +#define xB(opcode) DECODE_SPLIT(opcode, 1, 1, 11, 5) +#define xC(opcode) DECODE_SPLIT(opcode, 3, 1, 6, 5) +#define BF(opcode) (((opcode) >> (31-8)) & 7) + +typedef union _ppc_vsr_t { + uint64_t u64[2]; + uint32_t u32[4]; + float32 f32[4]; + float64 f64[2]; +} ppc_vsr_t; + +static void getVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) +{ + if (n < 32) { + vsr->f64[0] = env->fpr[n]; + vsr->u64[1] = env->vsr[n]; + } else { + vsr->u64[0] = env->avr[n-32].u64[0]; + vsr->u64[1] = env->avr[n-32].u64[1]; + } +} + +static void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) +{ + if (n < 32) { + env->fpr[n] = vsr->f64[0]; + env->vsr[n] = vsr->u64[1]; + } else { + env->avr[n-32].u64[0] = vsr->u64[0]; + env->avr[n-32].u64[1] = vsr->u64[1]; + } +} + +#define float64_to_float64(x, env) x + + +/* VSX_ADD_SUB - VSX floating point add/subract + * name - instruction mnemonic + * op - operation (add or sub) + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_ADD_SUB(name, op, nels, tp, fld, sfprf, r2sp) \ +void helper_##name(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_##op(xa.fld[i], xb.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_infinity(xa.fld[i]) && tp##_is_infinity(xb.fld[i])) {\ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_ADD_SUB(xsadddp, add, 1, float64, f64, 1, 0) +VSX_ADD_SUB(xsaddsp, add, 1, float64, f64, 1, 1) +VSX_ADD_SUB(xvadddp, add, 2, float64, f64, 0, 0) +VSX_ADD_SUB(xvaddsp, add, 4, float32, f32, 0, 0) +VSX_ADD_SUB(xssubdp, sub, 1, float64, f64, 1, 0) +VSX_ADD_SUB(xssubsp, sub, 1, float64, f64, 1, 1) +VSX_ADD_SUB(xvsubdp, sub, 2, float64, f64, 0, 0) +VSX_ADD_SUB(xvsubsp, sub, 4, float32, f32, 0, 0) + +/* VSX_MUL - VSX floating point multiply + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_MUL(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_mul(xa.fld[i], xb.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if ((tp##_is_infinity(xa.fld[i]) && tp##_is_zero(xb.fld[i])) || \ + (tp##_is_infinity(xb.fld[i]) && tp##_is_zero(xa.fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_MUL(xsmuldp, 1, float64, f64, 1, 0) +VSX_MUL(xsmulsp, 1, float64, f64, 1, 1) +VSX_MUL(xvmuldp, 2, float64, f64, 0, 0) +VSX_MUL(xvmulsp, 4, float32, f32, 0, 0) + +/* VSX_DIV - VSX floating point divide + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_DIV(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_div(xa.fld[i], xb.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_infinity(xa.fld[i]) && tp##_is_infinity(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, sfprf); \ + } else if (tp##_is_zero(xa.fld[i]) && \ + tp##_is_zero(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_DIV(xsdivdp, 1, float64, f64, 1, 0) +VSX_DIV(xsdivsp, 1, float64, f64, 1, 1) +VSX_DIV(xvdivdp, 2, float64, f64, 0, 0) +VSX_DIV(xvdivsp, 4, float32, f32, 0, 0) + +/* VSX_RE - VSX floating point reciprocal estimate + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_RE(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_signaling_nan(xb.fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + xt.fld[i] = tp##_div(tp##_one, xb.fld[i], &env->fp_status); \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[0], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_RE(xsredp, 1, float64, f64, 1, 0) +VSX_RE(xsresp, 1, float64, f64, 1, 1) +VSX_RE(xvredp, 2, float64, f64, 0, 0) +VSX_RE(xvresp, 4, float32, f32, 0, 0) + +/* VSX_SQRT - VSX floating point square root + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_SQRT(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_sqrt(xb.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_neg(xb.fld[i]) && !tp##_is_zero(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, sfprf); \ + } else if (tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_SQRT(xssqrtdp, 1, float64, f64, 1, 0) +VSX_SQRT(xssqrtsp, 1, float64, f64, 1, 1) +VSX_SQRT(xvsqrtdp, 2, float64, f64, 0, 0) +VSX_SQRT(xvsqrtsp, 4, float32, f32, 0, 0) + +/* VSX_RSQRTE - VSX floating point reciprocal square root estimate + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_RSQRTE(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_sqrt(xb.fld[i], &tstat); \ + xt.fld[i] = tp##_div(tp##_one, xt.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_neg(xb.fld[i]) && !tp##_is_zero(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, sfprf); \ + } else if (tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_RSQRTE(xsrsqrtedp, 1, float64, f64, 1, 0) +VSX_RSQRTE(xsrsqrtesp, 1, float64, f64, 1, 1) +VSX_RSQRTE(xvrsqrtedp, 2, float64, f64, 0, 0) +VSX_RSQRTE(xvrsqrtesp, 4, float32, f32, 0, 0) + +/* VSX_TDIV - VSX floating point test for divide + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * emin - minimum unbiased exponent + * emax - maximum unbiased exponent + * nbits - number of fraction bits + */ +#define VSX_TDIV(op, nels, tp, fld, emin, emax, nbits) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + int i; \ + int fe_flag = 0; \ + int fg_flag = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_infinity(xa.fld[i]) || \ + tp##_is_infinity(xb.fld[i]) || \ + tp##_is_zero(xb.fld[i]))) { \ + fe_flag = 1; \ + fg_flag = 1; \ + } else { \ + int e_a = ppc_##tp##_get_unbiased_exp(xa.fld[i]); \ + int e_b = ppc_##tp##_get_unbiased_exp(xb.fld[i]); \ + \ + if (unlikely(tp##_is_any_nan(xa.fld[i]) || \ + tp##_is_any_nan(xb.fld[i]))) { \ + fe_flag = 1; \ + } else if ((e_b <= emin) || (e_b >= (emax-2))) { \ + fe_flag = 1; \ + } else if (!tp##_is_zero(xa.fld[i]) && \ + (((e_a - e_b) >= emax) || \ + ((e_a - e_b) <= (emin+1)) || \ + (e_a <= (emin+nbits)))) { \ + fe_flag = 1; \ + } \ + \ + if (unlikely(tp##_is_zero_or_denormal(xb.fld[i]))) { \ + /* XB is not zero because of the above check and */ \ + /* so must be denormalized. */ \ + fg_flag = 1; \ + } \ + } \ + } \ + \ + env->crf[BF(opcode)] = 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); \ +} + +VSX_TDIV(xstdivdp, 1, float64, f64, -1022, 1023, 52) +VSX_TDIV(xvtdivdp, 2, float64, f64, -1022, 1023, 52) +VSX_TDIV(xvtdivsp, 4, float32, f32, -126, 127, 23) + +/* VSX_TSQRT - VSX floating point test for square root + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * emin - minimum unbiased exponent + * emax - maximum unbiased exponent + * nbits - number of fraction bits + */ +#define VSX_TSQRT(op, nels, tp, fld, emin, nbits) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + int i; \ + int fe_flag = 0; \ + int fg_flag = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_infinity(xb.fld[i]) || \ + tp##_is_zero(xb.fld[i]))) { \ + fe_flag = 1; \ + fg_flag = 1; \ + } else { \ + int e_b = ppc_##tp##_get_unbiased_exp(xb.fld[i]); \ + \ + if (unlikely(tp##_is_any_nan(xb.fld[i]))) { \ + fe_flag = 1; \ + } else if (unlikely(tp##_is_zero(xb.fld[i]))) { \ + fe_flag = 1; \ + } else if (unlikely(tp##_is_neg(xb.fld[i]))) { \ + fe_flag = 1; \ + } else if (!tp##_is_zero(xb.fld[i]) && \ + (e_b <= (emin+nbits))) { \ + fe_flag = 1; \ + } \ + \ + if (unlikely(tp##_is_zero_or_denormal(xb.fld[i]))) { \ + /* XB is not zero because of the above check and */ \ + /* therefore must be denormalized. */ \ + fg_flag = 1; \ + } \ + } \ + } \ + \ + env->crf[BF(opcode)] = 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); \ +} + +VSX_TSQRT(xstsqrtdp, 1, float64, f64, -1022, 52) +VSX_TSQRT(xvtsqrtdp, 2, float64, f64, -1022, 52) +VSX_TSQRT(xvtsqrtsp, 4, float32, f32, -126, 23) + +/* VSX_MADD - VSX floating point muliply/add variations + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * maddflgs - flags for the float*muladd routine that control the + * various forms (madd, msub, nmadd, nmsub) + * afrm - A form (1=A, 0=M) + * sfprf - set FPRF + */ +#define VSX_MADD(op, nels, tp, fld, maddflgs, afrm, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt_in, xa, xb, xt_out; \ + ppc_vsr_t *b, *c; \ + int i; \ + \ + if (afrm) { /* AxB + T */ \ + b = &xb; \ + c = &xt_in; \ + } else { /* AxT + B */ \ + b = &xt_in; \ + c = &xb; \ + } \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt_in, env); \ + \ + xt_out = xt_in; \ + \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + if (r2sp && (tstat.float_rounding_mode == float_round_nearest_even)) {\ + /* Avoid double rounding errors by rounding the intermediate */ \ + /* result to odd. */ \ + set_float_rounding_mode(float_round_to_zero, &tstat); \ + xt_out.fld[i] = tp##_muladd(xa.fld[i], b->fld[i], c->fld[i], \ + maddflgs, &tstat); \ + xt_out.fld[i] |= (get_float_exception_flags(&tstat) & \ + float_flag_inexact) != 0; \ + } else { \ + xt_out.fld[i] = tp##_muladd(xa.fld[i], b->fld[i], c->fld[i], \ + maddflgs, &tstat); \ + } \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(b->fld[i]) || \ + tp##_is_signaling_nan(c->fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + tstat.float_exception_flags &= ~float_flag_invalid; \ + } \ + if ((tp##_is_infinity(xa.fld[i]) && tp##_is_zero(b->fld[i])) || \ + (tp##_is_zero(xa.fld[i]) && tp##_is_infinity(b->fld[i]))) { \ + xt_out.fld[i] = float64_to_##tp(fload_invalid_op_excp(env, \ + POWERPC_EXCP_FP_VXIMZ, sfprf), &env->fp_status); \ + tstat.float_exception_flags &= ~float_flag_invalid; \ + } \ + if ((tstat.float_exception_flags & float_flag_invalid) && \ + ((tp##_is_infinity(xa.fld[i]) || \ + tp##_is_infinity(b->fld[i])) && \ + tp##_is_infinity(c->fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt_out.fld[i] = helper_frsp(env, xt_out.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt_out.fld[i], sfprf); \ + } \ + } \ + putVSR(xT(opcode), &xt_out, env); \ + helper_float_check_status(env); \ +} + +#define MADD_FLGS 0 +#define MSUB_FLGS float_muladd_negate_c +#define NMADD_FLGS float_muladd_negate_result +#define NMSUB_FLGS (float_muladd_negate_c | float_muladd_negate_result) + +VSX_MADD(xsmaddadp, 1, float64, f64, MADD_FLGS, 1, 1, 0) +VSX_MADD(xsmaddmdp, 1, float64, f64, MADD_FLGS, 0, 1, 0) +VSX_MADD(xsmsubadp, 1, float64, f64, MSUB_FLGS, 1, 1, 0) +VSX_MADD(xsmsubmdp, 1, float64, f64, MSUB_FLGS, 0, 1, 0) +VSX_MADD(xsnmaddadp, 1, float64, f64, NMADD_FLGS, 1, 1, 0) +VSX_MADD(xsnmaddmdp, 1, float64, f64, NMADD_FLGS, 0, 1, 0) +VSX_MADD(xsnmsubadp, 1, float64, f64, NMSUB_FLGS, 1, 1, 0) +VSX_MADD(xsnmsubmdp, 1, float64, f64, NMSUB_FLGS, 0, 1, 0) + +VSX_MADD(xsmaddasp, 1, float64, f64, MADD_FLGS, 1, 1, 1) +VSX_MADD(xsmaddmsp, 1, float64, f64, MADD_FLGS, 0, 1, 1) +VSX_MADD(xsmsubasp, 1, float64, f64, MSUB_FLGS, 1, 1, 1) +VSX_MADD(xsmsubmsp, 1, float64, f64, MSUB_FLGS, 0, 1, 1) +VSX_MADD(xsnmaddasp, 1, float64, f64, NMADD_FLGS, 1, 1, 1) +VSX_MADD(xsnmaddmsp, 1, float64, f64, NMADD_FLGS, 0, 1, 1) +VSX_MADD(xsnmsubasp, 1, float64, f64, NMSUB_FLGS, 1, 1, 1) +VSX_MADD(xsnmsubmsp, 1, float64, f64, NMSUB_FLGS, 0, 1, 1) + +VSX_MADD(xvmaddadp, 2, float64, f64, MADD_FLGS, 1, 0, 0) +VSX_MADD(xvmaddmdp, 2, float64, f64, MADD_FLGS, 0, 0, 0) +VSX_MADD(xvmsubadp, 2, float64, f64, MSUB_FLGS, 1, 0, 0) +VSX_MADD(xvmsubmdp, 2, float64, f64, MSUB_FLGS, 0, 0, 0) +VSX_MADD(xvnmaddadp, 2, float64, f64, NMADD_FLGS, 1, 0, 0) +VSX_MADD(xvnmaddmdp, 2, float64, f64, NMADD_FLGS, 0, 0, 0) +VSX_MADD(xvnmsubadp, 2, float64, f64, NMSUB_FLGS, 1, 0, 0) +VSX_MADD(xvnmsubmdp, 2, float64, f64, NMSUB_FLGS, 0, 0, 0) + +VSX_MADD(xvmaddasp, 4, float32, f32, MADD_FLGS, 1, 0, 0) +VSX_MADD(xvmaddmsp, 4, float32, f32, MADD_FLGS, 0, 0, 0) +VSX_MADD(xvmsubasp, 4, float32, f32, MSUB_FLGS, 1, 0, 0) +VSX_MADD(xvmsubmsp, 4, float32, f32, MSUB_FLGS, 0, 0, 0) +VSX_MADD(xvnmaddasp, 4, float32, f32, NMADD_FLGS, 1, 0, 0) +VSX_MADD(xvnmaddmsp, 4, float32, f32, NMADD_FLGS, 0, 0, 0) +VSX_MADD(xvnmsubasp, 4, float32, f32, NMSUB_FLGS, 1, 0, 0) +VSX_MADD(xvnmsubmsp, 4, float32, f32, NMSUB_FLGS, 0, 0, 0) + +#define VSX_SCALAR_CMP(op, ordered) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + uint32_t cc = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + if (unlikely(float64_is_any_nan(xa.f64[0]) || \ + float64_is_any_nan(xb.f64[0]))) { \ + if (float64_is_signaling_nan(xa.f64[0]) || \ + float64_is_signaling_nan(xb.f64[0])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (ordered) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + cc = 1; \ + } else { \ + if (float64_lt(xa.f64[0], xb.f64[0], &env->fp_status)) { \ + cc = 8; \ + } else if (!float64_le(xa.f64[0], xb.f64[0], &env->fp_status)) { \ + cc = 4; \ + } else { \ + cc = 2; \ + } \ + } \ + \ + env->fpscr &= ~(0x0F << FPSCR_FPRF); \ + env->fpscr |= cc << FPSCR_FPRF; \ + env->crf[BF(opcode)] = cc; \ + \ + helper_float_check_status(env); \ +} + +VSX_SCALAR_CMP(xscmpodp, 1) +VSX_SCALAR_CMP(xscmpudp, 0) + +#define float64_snan_to_qnan(x) ((x) | 0x0008000000000000ULL) +#define float32_snan_to_qnan(x) ((x) | 0x00400000) + +/* VSX_MAX_MIN - VSX floating point maximum/minimum + * name - instruction mnemonic + * op - operation (max or min) + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + */ +#define VSX_MAX_MIN(name, op, nels, tp, fld) \ +void helper_##name(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + xt.fld[i] = tp##_##op(xa.fld[i], xb.fld[i], &env->fp_status); \ + if (unlikely(tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_MAX_MIN(xsmaxdp, maxnum, 1, float64, f64) +VSX_MAX_MIN(xvmaxdp, maxnum, 2, float64, f64) +VSX_MAX_MIN(xvmaxsp, maxnum, 4, float32, f32) +VSX_MAX_MIN(xsmindp, minnum, 1, float64, f64) +VSX_MAX_MIN(xvmindp, minnum, 2, float64, f64) +VSX_MAX_MIN(xvminsp, minnum, 4, float32, f32) + +/* VSX_CMP - VSX floating point compare + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * cmp - comparison operation + * svxvc - set VXVC bit + */ +#define VSX_CMP(op, nels, tp, fld, cmp, svxvc) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + int all_true = 1; \ + int all_false = 1; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_any_nan(xa.fld[i]) || \ + tp##_is_any_nan(xb.fld[i]))) { \ + if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (svxvc) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + xt.fld[i] = 0; \ + all_true = 0; \ + } else { \ + if (tp##_##cmp(xb.fld[i], xa.fld[i], &env->fp_status) == 1) { \ + xt.fld[i] = -1; \ + all_false = 0; \ + } else { \ + xt.fld[i] = 0; \ + all_true = 0; \ + } \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + if ((opcode >> (31-21)) & 1) { \ + env->crf[6] = (all_true ? 0x8 : 0) | (all_false ? 0x2 : 0); \ + } \ + helper_float_check_status(env); \ + } + +VSX_CMP(xvcmpeqdp, 2, float64, f64, eq, 0) +VSX_CMP(xvcmpgedp, 2, float64, f64, le, 1) +VSX_CMP(xvcmpgtdp, 2, float64, f64, lt, 1) +VSX_CMP(xvcmpeqsp, 4, float32, f32, eq, 0) +VSX_CMP(xvcmpgesp, 4, float32, f32, le, 1) +VSX_CMP(xvcmpgtsp, 4, float32, f32, lt, 1) + +#if defined(HOST_WORDS_BIGENDIAN) +#define JOFFSET 0 +#else +#define JOFFSET 1 +#endif + +/* VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (float32 or float64) + * ttp - target type (float32 or float64) + * sfld - source vsr_t field + * tfld - target vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_CVT_FP_TO_FP(op, nels, stp, ttp, sfld, tfld, sfprf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + int j = 2*i + JOFFSET; \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (unlikely(stp##_is_signaling_nan(xb.sfld))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, ttp##_to_float64(xt.tfld, \ + &env->fp_status), sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_CVT_FP_TO_FP(xscvdpsp, 1, float64, float32, f64[i], f32[j], 1) +VSX_CVT_FP_TO_FP(xscvspdp, 1, float32, float64, f32[j], f64[i], 1) +VSX_CVT_FP_TO_FP(xvcvdpsp, 2, float64, float32, f64[i], f32[j], 0) +VSX_CVT_FP_TO_FP(xvcvspdp, 2, float32, float64, f32[j], f64[i], 0) + +uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb) +{ + float_status tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + + return (uint64_t)float64_to_float32(xb, &tstat) << 32; +} + +uint64_t helper_xscvspdpn(CPUPPCState *env, uint64_t xb) +{ + float_status tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + + return float32_to_float64(xb >> 32, &tstat); +} + +/* VSX_CVT_FP_TO_INT - VSX floating point to integer conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (float32 or float64) + * ttp - target type (int32, uint32, int64 or uint64) + * sfld - source vsr_t field + * tfld - target vsr_t field + * jdef - definition of the j index (i or 2*i) + * rnan - resulting NaN + */ +#define VSX_CVT_FP_TO_INT(op, nels, stp, ttp, sfld, tfld, jdef, rnan) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + int j = jdef; \ + if (unlikely(stp##_is_any_nan(xb.sfld))) { \ + if (stp##_is_signaling_nan(xb.sfld)) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + xt.tfld = rnan; \ + } else { \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (env->fp_status.float_exception_flags & float_flag_invalid) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + } \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_CVT_FP_TO_INT(xscvdpsxds, 1, float64, int64, f64[j], u64[i], i, \ + 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xscvdpsxws, 1, float64, int32, f64[i], u32[j], \ + 2*i + JOFFSET, 0x80000000U) +VSX_CVT_FP_TO_INT(xscvdpuxds, 1, float64, uint64, f64[j], u64[i], i, 0ULL) +VSX_CVT_FP_TO_INT(xscvdpuxws, 1, float64, uint32, f64[i], u32[j], \ + 2*i + JOFFSET, 0U) +VSX_CVT_FP_TO_INT(xvcvdpsxds, 2, float64, int64, f64[j], u64[i], i, \ + 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xvcvdpsxws, 2, float64, int32, f64[i], u32[j], \ + 2*i + JOFFSET, 0x80000000U) +VSX_CVT_FP_TO_INT(xvcvdpuxds, 2, float64, uint64, f64[j], u64[i], i, 0ULL) +VSX_CVT_FP_TO_INT(xvcvdpuxws, 2, float64, uint32, f64[i], u32[j], \ + 2*i + JOFFSET, 0U) +VSX_CVT_FP_TO_INT(xvcvspsxds, 2, float32, int64, f32[j], u64[i], \ + 2*i + JOFFSET, 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xvcvspsxws, 4, float32, int32, f32[j], u32[j], i, \ + 0x80000000U) +VSX_CVT_FP_TO_INT(xvcvspuxds, 2, float32, uint64, f32[j], u64[i], \ + 2*i + JOFFSET, 0ULL) +VSX_CVT_FP_TO_INT(xvcvspuxws, 4, float32, uint32, f32[j], u32[i], i, 0U) + +/* VSX_CVT_INT_TO_FP - VSX integer to floating point conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (int32, uint32, int64 or uint64) + * ttp - target type (float32 or float64) + * sfld - source vsr_t field + * tfld - target vsr_t field + * jdef - definition of the j index (i or 2*i) + * sfprf - set FPRF + */ +#define VSX_CVT_INT_TO_FP(op, nels, stp, ttp, sfld, tfld, jdef, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + int j = jdef; \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (r2sp) { \ + xt.tfld = helper_frsp(env, xt.tfld); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, xt.tfld, sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_CVT_INT_TO_FP(xscvsxddp, 1, int64, float64, u64[j], f64[i], i, 1, 0) +VSX_CVT_INT_TO_FP(xscvuxddp, 1, uint64, float64, u64[j], f64[i], i, 1, 0) +VSX_CVT_INT_TO_FP(xscvsxdsp, 1, int64, float64, u64[j], f64[i], i, 1, 1) +VSX_CVT_INT_TO_FP(xscvuxdsp, 1, uint64, float64, u64[j], f64[i], i, 1, 1) +VSX_CVT_INT_TO_FP(xvcvsxddp, 2, int64, float64, u64[j], f64[i], i, 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxddp, 2, uint64, float64, u64[j], f64[i], i, 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxwdp, 2, int32, float64, u32[j], f64[i], \ + 2*i + JOFFSET, 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxwdp, 2, uint64, float64, u32[j], f64[i], \ + 2*i + JOFFSET, 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxdsp, 2, int64, float32, u64[i], f32[j], \ + 2*i + JOFFSET, 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxdsp, 2, uint64, float32, u64[i], f32[j], \ + 2*i + JOFFSET, 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxwsp, 4, int32, float32, u32[j], f32[i], i, 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxwsp, 4, uint32, float32, u32[j], f32[i], i, 0, 0) + +/* For "use current rounding mode", define a value that will not be one of + * the existing rounding model enums. + */ +#define FLOAT_ROUND_CURRENT (float_round_nearest_even + float_round_down + \ + float_round_up + float_round_to_zero) + +/* VSX_ROUND - VSX floating point round + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * rmode - rounding mode + * sfprf - set FPRF + */ +#define VSX_ROUND(op, nels, tp, fld, rmode, sfprf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + if (rmode != FLOAT_ROUND_CURRENT) { \ + set_float_rounding_mode(rmode, &env->fp_status); \ + } \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_signaling_nan(xb.fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + xt.fld[i] = tp##_snan_to_qnan(xb.fld[i]); \ + } else { \ + xt.fld[i] = tp##_round_to_int(xb.fld[i], &env->fp_status); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + /* If this is not a "use current rounding mode" instruction, \ + * then inhibit setting of the XX bit and restore rounding \ + * mode from FPSCR */ \ + if (rmode != FLOAT_ROUND_CURRENT) { \ + fpscr_set_rounding_mode(env); \ + env->fp_status.float_exception_flags &= ~float_flag_inexact; \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_ROUND(xsrdpi, 1, float64, f64, float_round_nearest_even, 1) +VSX_ROUND(xsrdpic, 1, float64, f64, FLOAT_ROUND_CURRENT, 1) +VSX_ROUND(xsrdpim, 1, float64, f64, float_round_down, 1) +VSX_ROUND(xsrdpip, 1, float64, f64, float_round_up, 1) +VSX_ROUND(xsrdpiz, 1, float64, f64, float_round_to_zero, 1) + +VSX_ROUND(xvrdpi, 2, float64, f64, float_round_nearest_even, 0) +VSX_ROUND(xvrdpic, 2, float64, f64, FLOAT_ROUND_CURRENT, 0) +VSX_ROUND(xvrdpim, 2, float64, f64, float_round_down, 0) +VSX_ROUND(xvrdpip, 2, float64, f64, float_round_up, 0) +VSX_ROUND(xvrdpiz, 2, float64, f64, float_round_to_zero, 0) + +VSX_ROUND(xvrspi, 4, float32, f32, float_round_nearest_even, 0) +VSX_ROUND(xvrspic, 4, float32, f32, FLOAT_ROUND_CURRENT, 0) +VSX_ROUND(xvrspim, 4, float32, f32, float_round_down, 0) +VSX_ROUND(xvrspip, 4, float32, f32, float_round_up, 0) +VSX_ROUND(xvrspiz, 4, float32, f32, float_round_to_zero, 0) + +uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb) +{ + helper_reset_fpstatus(env); + + uint64_t xt = helper_frsp(env, xb); + + helper_compute_fprf(env, xt, 1); + helper_float_check_status(env); + return xt; +} diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 6d282bb32d..99f10deee1 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -31,7 +31,11 @@ DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) DEF_HELPER_3(mulldo, i64, env, i64, i64) +DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) +DEF_HELPER_4(divde, i64, env, i64, i64, i32) #endif +DEF_HELPER_4(divweu, tl, env, tl, tl, i32) +DEF_HELPER_4(divwe, tl, env, tl, tl, i32) DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) @@ -41,6 +45,7 @@ DEF_HELPER_3(sraw, tl, env, tl, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) #endif @@ -61,11 +66,18 @@ DEF_HELPER_4(fcmpo, void, env, i64, i64, i32) DEF_HELPER_4(fcmpu, void, env, i64, i64, i32) DEF_HELPER_2(fctiw, i64, env, i64) +DEF_HELPER_2(fctiwu, i64, env, i64) DEF_HELPER_2(fctiwz, i64, env, i64) +DEF_HELPER_2(fctiwuz, i64, env, i64) #if defined(TARGET_PPC64) DEF_HELPER_2(fcfid, i64, env, i64) +DEF_HELPER_2(fcfidu, i64, env, i64) +DEF_HELPER_2(fcfids, i64, env, i64) +DEF_HELPER_2(fcfidus, i64, env, i64) DEF_HELPER_2(fctid, i64, env, i64) +DEF_HELPER_2(fctidu, i64, env, i64) DEF_HELPER_2(fctidz, i64, env, i64) +DEF_HELPER_2(fctiduz, i64, env, i64) #endif DEF_HELPER_2(frsp, i64, env, i64) DEF_HELPER_2(frin, i64, env, i64) @@ -87,6 +99,9 @@ DEF_HELPER_2(fres, i64, env, i64) DEF_HELPER_2(frsqrte, i64, env, i64) DEF_HELPER_4(fsel, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_2(ftdiv, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) + #define dh_alias_avr ptr #define dh_ctype_avr ppc_avr_t * #define dh_is_signed_avr dh_is_signed_ptr @@ -94,9 +109,11 @@ DEF_HELPER_4(fsel, i64, env, i64, i64, i64) DEF_HELPER_3(vaddubm, void, avr, avr, avr) DEF_HELPER_3(vadduhm, void, avr, avr, avr) DEF_HELPER_3(vadduwm, void, avr, avr, avr) +DEF_HELPER_3(vaddudm, void, avr, avr, avr) DEF_HELPER_3(vsububm, void, avr, avr, avr) DEF_HELPER_3(vsubuhm, void, avr, avr, avr) DEF_HELPER_3(vsubuwm, void, avr, avr, avr) +DEF_HELPER_3(vsubudm, void, avr, avr, avr) DEF_HELPER_3(vavgub, void, avr, avr, avr) DEF_HELPER_3(vavguh, void, avr, avr, avr) DEF_HELPER_3(vavguw, void, avr, avr, avr) @@ -106,24 +123,31 @@ DEF_HELPER_3(vavgsw, void, avr, avr, avr) DEF_HELPER_3(vminsb, void, avr, avr, avr) DEF_HELPER_3(vminsh, void, avr, avr, avr) DEF_HELPER_3(vminsw, void, avr, avr, avr) +DEF_HELPER_3(vminsd, void, avr, avr, avr) DEF_HELPER_3(vmaxsb, void, avr, avr, avr) DEF_HELPER_3(vmaxsh, void, avr, avr, avr) DEF_HELPER_3(vmaxsw, void, avr, avr, avr) +DEF_HELPER_3(vmaxsd, void, avr, avr, avr) DEF_HELPER_3(vminub, void, avr, avr, avr) DEF_HELPER_3(vminuh, void, avr, avr, avr) DEF_HELPER_3(vminuw, void, avr, avr, avr) +DEF_HELPER_3(vminud, void, avr, avr, avr) DEF_HELPER_3(vmaxub, void, avr, avr, avr) DEF_HELPER_3(vmaxuh, void, avr, avr, avr) DEF_HELPER_3(vmaxuw, void, avr, avr, avr) +DEF_HELPER_3(vmaxud, void, avr, avr, avr) DEF_HELPER_4(vcmpequb, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequh, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequd, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtub, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtuh, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtuw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtud, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsb, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsh, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsd, void, env, avr, avr, avr) DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr) @@ -131,12 +155,15 @@ DEF_HELPER_4(vcmpbfp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequb_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequh_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequd_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtub_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtuh_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtuw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtud_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsb_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsh_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsd_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpeqfp_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgefp_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtfp_dot, void, env, avr, avr, avr) @@ -149,21 +176,29 @@ DEF_HELPER_3(vmrghh, void, avr, avr, avr) DEF_HELPER_3(vmrghw, void, avr, avr, avr) DEF_HELPER_3(vmulesb, void, avr, avr, avr) DEF_HELPER_3(vmulesh, void, avr, avr, avr) +DEF_HELPER_3(vmulesw, void, avr, avr, avr) DEF_HELPER_3(vmuleub, void, avr, avr, avr) DEF_HELPER_3(vmuleuh, void, avr, avr, avr) +DEF_HELPER_3(vmuleuw, void, avr, avr, avr) DEF_HELPER_3(vmulosb, void, avr, avr, avr) DEF_HELPER_3(vmulosh, void, avr, avr, avr) +DEF_HELPER_3(vmulosw, void, avr, avr, avr) DEF_HELPER_3(vmuloub, void, avr, avr, avr) DEF_HELPER_3(vmulouh, void, avr, avr, avr) +DEF_HELPER_3(vmulouw, void, avr, avr, avr) +DEF_HELPER_3(vmuluwm, void, avr, avr, avr) DEF_HELPER_3(vsrab, void, avr, avr, avr) DEF_HELPER_3(vsrah, void, avr, avr, avr) DEF_HELPER_3(vsraw, void, avr, avr, avr) +DEF_HELPER_3(vsrad, void, avr, avr, avr) DEF_HELPER_3(vsrb, void, avr, avr, avr) DEF_HELPER_3(vsrh, void, avr, avr, avr) DEF_HELPER_3(vsrw, void, avr, avr, avr) +DEF_HELPER_3(vsrd, void, avr, avr, avr) DEF_HELPER_3(vslb, void, avr, avr, avr) DEF_HELPER_3(vslh, void, avr, avr, avr) DEF_HELPER_3(vslw, void, avr, avr, avr) +DEF_HELPER_3(vsld, void, avr, avr, avr) DEF_HELPER_3(vslo, void, avr, avr, avr) DEF_HELPER_3(vsro, void, avr, avr, avr) DEF_HELPER_3(vaddcuw, void, avr, avr, avr) @@ -182,9 +217,18 @@ DEF_HELPER_4(vadduws, void, env, avr, avr, avr) DEF_HELPER_4(vsububs, void, env, avr, avr, avr) DEF_HELPER_4(vsubuhs, void, env, avr, avr, avr) DEF_HELPER_4(vsubuws, void, env, avr, avr, avr) +DEF_HELPER_3(vadduqm, void, avr, avr, avr) +DEF_HELPER_4(vaddecuq, void, avr, avr, avr, avr) +DEF_HELPER_4(vaddeuqm, void, avr, avr, avr, avr) +DEF_HELPER_3(vaddcuq, void, avr, avr, avr) +DEF_HELPER_3(vsubuqm, void, avr, avr, avr) +DEF_HELPER_4(vsubecuq, void, avr, avr, avr, avr) +DEF_HELPER_4(vsubeuqm, void, avr, avr, avr, avr) +DEF_HELPER_3(vsubcuq, void, avr, avr, avr) DEF_HELPER_3(vrlb, void, avr, avr, avr) DEF_HELPER_3(vrlh, void, avr, avr, avr) DEF_HELPER_3(vrlw, void, avr, avr, avr) +DEF_HELPER_3(vrld, void, avr, avr, avr) DEF_HELPER_3(vsl, void, avr, avr, avr) DEF_HELPER_3(vsr, void, avr, avr, avr) DEF_HELPER_4(vsldoi, void, avr, avr, avr, i32) @@ -198,8 +242,10 @@ DEF_HELPER_2(vupkhpx, void, avr, avr) DEF_HELPER_2(vupklpx, void, avr, avr) DEF_HELPER_2(vupkhsb, void, avr, avr) DEF_HELPER_2(vupkhsh, void, avr, avr) +DEF_HELPER_2(vupkhsw, void, avr, avr) DEF_HELPER_2(vupklsb, void, avr, avr) DEF_HELPER_2(vupklsh, void, avr, avr) +DEF_HELPER_2(vupklsw, void, avr, avr) DEF_HELPER_5(vmsumubm, void, env, avr, avr, avr, avr) DEF_HELPER_5(vmsummbm, void, env, avr, avr, avr, avr) DEF_HELPER_5(vsel, void, env, avr, avr, avr, avr) @@ -208,10 +254,14 @@ DEF_HELPER_4(vpkshss, void, env, avr, avr, avr) DEF_HELPER_4(vpkshus, void, env, avr, avr, avr) DEF_HELPER_4(vpkswss, void, env, avr, avr, avr) DEF_HELPER_4(vpkswus, void, env, avr, avr, avr) +DEF_HELPER_4(vpksdss, void, env, avr, avr, avr) +DEF_HELPER_4(vpksdus, void, env, avr, avr, avr) DEF_HELPER_4(vpkuhus, void, env, avr, avr, avr) DEF_HELPER_4(vpkuwus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkudus, void, env, avr, avr, avr) DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr) DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr) +DEF_HELPER_4(vpkudum, void, env, avr, avr, avr) DEF_HELPER_3(vpkpx, void, avr, avr, avr) DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr) DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr) @@ -251,6 +301,163 @@ DEF_HELPER_4(vcfsx, void, env, avr, avr, i32) DEF_HELPER_4(vctuxs, void, env, avr, avr, i32) DEF_HELPER_4(vctsxs, void, env, avr, avr, i32) +DEF_HELPER_2(vclzb, void, avr, avr) +DEF_HELPER_2(vclzh, void, avr, avr) +DEF_HELPER_2(vclzw, void, avr, avr) +DEF_HELPER_2(vclzd, void, avr, avr) +DEF_HELPER_2(vpopcntb, void, avr, avr) +DEF_HELPER_2(vpopcnth, void, avr, avr) +DEF_HELPER_2(vpopcntw, void, avr, avr) +DEF_HELPER_2(vpopcntd, void, avr, avr) +DEF_HELPER_3(vbpermq, void, avr, avr, avr) +DEF_HELPER_2(vgbbd, void, avr, avr) +DEF_HELPER_3(vpmsumb, void, avr, avr, avr) +DEF_HELPER_3(vpmsumh, void, avr, avr, avr) +DEF_HELPER_3(vpmsumw, void, avr, avr, avr) +DEF_HELPER_3(vpmsumd, void, avr, avr, avr) + +DEF_HELPER_2(vsbox, void, avr, avr) +DEF_HELPER_3(vcipher, void, avr, avr, avr) +DEF_HELPER_3(vcipherlast, void, avr, avr, avr) +DEF_HELPER_3(vncipher, void, avr, avr, avr) +DEF_HELPER_3(vncipherlast, void, avr, avr, avr) +DEF_HELPER_3(vshasigmaw, void, avr, avr, i32) +DEF_HELPER_3(vshasigmad, void, avr, avr, i32) +DEF_HELPER_4(vpermxor, void, avr, avr, avr, avr) + +DEF_HELPER_4(bcdadd, i32, avr, avr, avr, i32) +DEF_HELPER_4(bcdsub, i32, avr, avr, avr, i32) + +DEF_HELPER_2(xsadddp, void, env, i32) +DEF_HELPER_2(xssubdp, void, env, i32) +DEF_HELPER_2(xsmuldp, void, env, i32) +DEF_HELPER_2(xsdivdp, void, env, i32) +DEF_HELPER_2(xsredp, void, env, i32) +DEF_HELPER_2(xssqrtdp, void, env, i32) +DEF_HELPER_2(xsrsqrtedp, void, env, i32) +DEF_HELPER_2(xstdivdp, void, env, i32) +DEF_HELPER_2(xstsqrtdp, void, env, i32) +DEF_HELPER_2(xsmaddadp, void, env, i32) +DEF_HELPER_2(xsmaddmdp, void, env, i32) +DEF_HELPER_2(xsmsubadp, void, env, i32) +DEF_HELPER_2(xsmsubmdp, void, env, i32) +DEF_HELPER_2(xsnmaddadp, void, env, i32) +DEF_HELPER_2(xsnmaddmdp, void, env, i32) +DEF_HELPER_2(xsnmsubadp, void, env, i32) +DEF_HELPER_2(xsnmsubmdp, void, env, i32) +DEF_HELPER_2(xscmpodp, void, env, i32) +DEF_HELPER_2(xscmpudp, void, env, i32) +DEF_HELPER_2(xsmaxdp, void, env, i32) +DEF_HELPER_2(xsmindp, void, env, i32) +DEF_HELPER_2(xscvdpsp, void, env, i32) +DEF_HELPER_2(xscvdpspn, i64, env, i64) +DEF_HELPER_2(xscvspdp, void, env, i32) +DEF_HELPER_2(xscvspdpn, i64, env, i64) +DEF_HELPER_2(xscvdpsxds, void, env, i32) +DEF_HELPER_2(xscvdpsxws, void, env, i32) +DEF_HELPER_2(xscvdpuxds, void, env, i32) +DEF_HELPER_2(xscvdpuxws, void, env, i32) +DEF_HELPER_2(xscvsxddp, void, env, i32) +DEF_HELPER_2(xscvuxdsp, void, env, i32) +DEF_HELPER_2(xscvsxdsp, void, env, i32) +DEF_HELPER_2(xscvuxddp, void, env, i32) +DEF_HELPER_2(xsrdpi, void, env, i32) +DEF_HELPER_2(xsrdpic, void, env, i32) +DEF_HELPER_2(xsrdpim, void, env, i32) +DEF_HELPER_2(xsrdpip, void, env, i32) +DEF_HELPER_2(xsrdpiz, void, env, i32) + +DEF_HELPER_2(xsaddsp, void, env, i32) +DEF_HELPER_2(xssubsp, void, env, i32) +DEF_HELPER_2(xsmulsp, void, env, i32) +DEF_HELPER_2(xsdivsp, void, env, i32) +DEF_HELPER_2(xsresp, void, env, i32) +DEF_HELPER_2(xsrsp, i64, env, i64) +DEF_HELPER_2(xssqrtsp, void, env, i32) +DEF_HELPER_2(xsrsqrtesp, void, env, i32) +DEF_HELPER_2(xsmaddasp, void, env, i32) +DEF_HELPER_2(xsmaddmsp, void, env, i32) +DEF_HELPER_2(xsmsubasp, void, env, i32) +DEF_HELPER_2(xsmsubmsp, void, env, i32) +DEF_HELPER_2(xsnmaddasp, void, env, i32) +DEF_HELPER_2(xsnmaddmsp, void, env, i32) +DEF_HELPER_2(xsnmsubasp, void, env, i32) +DEF_HELPER_2(xsnmsubmsp, void, env, i32) + +DEF_HELPER_2(xvadddp, void, env, i32) +DEF_HELPER_2(xvsubdp, void, env, i32) +DEF_HELPER_2(xvmuldp, void, env, i32) +DEF_HELPER_2(xvdivdp, void, env, i32) +DEF_HELPER_2(xvredp, void, env, i32) +DEF_HELPER_2(xvsqrtdp, void, env, i32) +DEF_HELPER_2(xvrsqrtedp, void, env, i32) +DEF_HELPER_2(xvtdivdp, void, env, i32) +DEF_HELPER_2(xvtsqrtdp, void, env, i32) +DEF_HELPER_2(xvmaddadp, void, env, i32) +DEF_HELPER_2(xvmaddmdp, void, env, i32) +DEF_HELPER_2(xvmsubadp, void, env, i32) +DEF_HELPER_2(xvmsubmdp, void, env, i32) +DEF_HELPER_2(xvnmaddadp, void, env, i32) +DEF_HELPER_2(xvnmaddmdp, void, env, i32) +DEF_HELPER_2(xvnmsubadp, void, env, i32) +DEF_HELPER_2(xvnmsubmdp, void, env, i32) +DEF_HELPER_2(xvmaxdp, void, env, i32) +DEF_HELPER_2(xvmindp, void, env, i32) +DEF_HELPER_2(xvcmpeqdp, void, env, i32) +DEF_HELPER_2(xvcmpgedp, void, env, i32) +DEF_HELPER_2(xvcmpgtdp, void, env, i32) +DEF_HELPER_2(xvcvdpsp, void, env, i32) +DEF_HELPER_2(xvcvdpsxds, void, env, i32) +DEF_HELPER_2(xvcvdpsxws, void, env, i32) +DEF_HELPER_2(xvcvdpuxds, void, env, i32) +DEF_HELPER_2(xvcvdpuxws, void, env, i32) +DEF_HELPER_2(xvcvsxddp, void, env, i32) +DEF_HELPER_2(xvcvuxddp, void, env, i32) +DEF_HELPER_2(xvcvsxwdp, void, env, i32) +DEF_HELPER_2(xvcvuxwdp, void, env, i32) +DEF_HELPER_2(xvrdpi, void, env, i32) +DEF_HELPER_2(xvrdpic, void, env, i32) +DEF_HELPER_2(xvrdpim, void, env, i32) +DEF_HELPER_2(xvrdpip, void, env, i32) +DEF_HELPER_2(xvrdpiz, void, env, i32) + +DEF_HELPER_2(xvaddsp, void, env, i32) +DEF_HELPER_2(xvsubsp, void, env, i32) +DEF_HELPER_2(xvmulsp, void, env, i32) +DEF_HELPER_2(xvdivsp, void, env, i32) +DEF_HELPER_2(xvresp, void, env, i32) +DEF_HELPER_2(xvsqrtsp, void, env, i32) +DEF_HELPER_2(xvrsqrtesp, void, env, i32) +DEF_HELPER_2(xvtdivsp, void, env, i32) +DEF_HELPER_2(xvtsqrtsp, void, env, i32) +DEF_HELPER_2(xvmaddasp, void, env, i32) +DEF_HELPER_2(xvmaddmsp, void, env, i32) +DEF_HELPER_2(xvmsubasp, void, env, i32) +DEF_HELPER_2(xvmsubmsp, void, env, i32) +DEF_HELPER_2(xvnmaddasp, void, env, i32) +DEF_HELPER_2(xvnmaddmsp, void, env, i32) +DEF_HELPER_2(xvnmsubasp, void, env, i32) +DEF_HELPER_2(xvnmsubmsp, void, env, i32) +DEF_HELPER_2(xvmaxsp, void, env, i32) +DEF_HELPER_2(xvminsp, void, env, i32) +DEF_HELPER_2(xvcmpeqsp, void, env, i32) +DEF_HELPER_2(xvcmpgesp, void, env, i32) +DEF_HELPER_2(xvcmpgtsp, void, env, i32) +DEF_HELPER_2(xvcvspdp, void, env, i32) +DEF_HELPER_2(xvcvspsxds, void, env, i32) +DEF_HELPER_2(xvcvspsxws, void, env, i32) +DEF_HELPER_2(xvcvspuxds, void, env, i32) +DEF_HELPER_2(xvcvspuxws, void, env, i32) +DEF_HELPER_2(xvcvsxdsp, void, env, i32) +DEF_HELPER_2(xvcvuxdsp, void, env, i32) +DEF_HELPER_2(xvcvsxwsp, void, env, i32) +DEF_HELPER_2(xvcvuxwsp, void, env, i32) +DEF_HELPER_2(xvrspi, void, env, i32) +DEF_HELPER_2(xvrspic, void, env, i32) +DEF_HELPER_2(xvrspim, void, env, i32) +DEF_HELPER_2(xvrspip, void, env, i32) +DEF_HELPER_2(xvrspiz, void, env, i32) + DEF_HELPER_2(efscfsi, i32, env, i32) DEF_HELPER_2(efscfui, i32, env, i32) DEF_HELPER_2(efscfuf, i32, env, i32) diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c index e50bdd20ec..63dde94b04 100644 --- a/target-ppc/int_helper.c +++ b/target-ppc/int_helper.c @@ -41,6 +41,119 @@ uint64_t helper_mulldo(CPUPPCState *env, uint64_t arg1, uint64_t arg2) } #endif +target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, + uint32_t oe) +{ + uint64_t rt = 0; + int overflow = 0; + + uint64_t dividend = (uint64_t)ra << 32; + uint64_t divisor = (uint32_t)rb; + + if (unlikely(divisor == 0)) { + overflow = 1; + } else { + rt = dividend / divisor; + overflow = rt > UINT32_MAX; + } + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return (target_ulong)rt; +} + +target_ulong helper_divwe(CPUPPCState *env, target_ulong ra, target_ulong rb, + uint32_t oe) +{ + int64_t rt = 0; + int overflow = 0; + + int64_t dividend = (int64_t)ra << 32; + int64_t divisor = (int64_t)((int32_t)rb); + + if (unlikely((divisor == 0) || + ((divisor == -1ull) && (dividend == INT64_MIN)))) { + overflow = 1; + } else { + rt = dividend / divisor; + overflow = rt != (int32_t)rt; + } + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return (target_ulong)rt; +} + +#if defined(TARGET_PPC64) + +uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) +{ + uint64_t rt = 0; + int overflow = 0; + + overflow = divu128(&rt, &ra, rb); + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return rt; +} + +uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) +{ + int64_t rt = 0; + int64_t ra = (int64_t)rau; + int64_t rb = (int64_t)rbu; + int overflow = divs128(&rt, &ra, rb); + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return rt; +} + +#endif + + target_ulong helper_cntlzw(target_ulong t) { return clz32(t); @@ -53,6 +166,26 @@ target_ulong helper_cntlzd(target_ulong t) } #endif +#if defined(TARGET_PPC64) + +uint64_t helper_bpermd(uint64_t rs, uint64_t rb) +{ + int i; + uint64_t ra = 0; + + for (i = 0; i < 8; i++) { + int index = (rs >> (i*8)) & 0xFF; + if (index < 64) { + if (rb & (1ull << (63-index))) { + ra |= 1 << i; + } + } + } + return ra; +} + +#endif + target_ulong helper_cmpb(target_ulong rs, target_ulong rb) { target_ulong mask = 0xff; @@ -371,6 +504,8 @@ void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) VARITH(ubm, u8) VARITH(uhm, u16) VARITH(uwm, u32) +VARITH(udm, u64) +VARITH_DO(muluwm, *, u32) #undef VARITH_DO #undef VARITH @@ -491,15 +626,18 @@ VCF(sx, int32_to_float32, s32) void helper_vcmp##suffix(CPUPPCState *env, ppc_avr_t *r, \ ppc_avr_t *a, ppc_avr_t *b) \ { \ - uint32_t ones = (uint32_t)-1; \ - uint32_t all = ones; \ - uint32_t none = 0; \ + uint64_t ones = (uint64_t)-1; \ + uint64_t all = ones; \ + uint64_t none = 0; \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - uint32_t result = (a->element[i] compare b->element[i] ? \ + uint64_t result = (a->element[i] compare b->element[i] ? \ ones : 0x0); \ switch (sizeof(a->element[0])) { \ + case 8: \ + r->u64[i] = result; \ + break; \ case 4: \ r->u32[i] = result; \ break; \ @@ -523,12 +661,15 @@ VCF(sx, int32_to_float32, s32) VCMP(equb, ==, u8) VCMP(equh, ==, u16) VCMP(equw, ==, u32) +VCMP(equd, ==, u64) VCMP(gtub, >, u8) VCMP(gtuh, >, u16) VCMP(gtuw, >, u32) +VCMP(gtud, >, u64) VCMP(gtsb, >, s8) VCMP(gtsh, >, s16) VCMP(gtsw, >, s32) +VCMP(gtsd, >, s64) #undef VCMP_DO #undef VCMP @@ -689,9 +830,11 @@ void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, VMINMAX(sb, s8) VMINMAX(sh, s16) VMINMAX(sw, s32) +VMINMAX(sd, s64) VMINMAX(ub, u8) VMINMAX(uh, u16) VMINMAX(uw, u32) +VMINMAX(ud, u64) #undef VMINMAX_DO #undef VMINMAX @@ -849,28 +992,32 @@ void helper_vmsumuhs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } } -#define VMUL_DO(name, mul_element, prod_element, evenp) \ +#define VMUL_DO(name, mul_element, prod_element, cast, evenp) \ void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ VECTOR_FOR_INORDER_I(i, prod_element) { \ if (evenp) { \ - r->prod_element[i] = a->mul_element[i * 2 + HI_IDX] * \ - b->mul_element[i * 2 + HI_IDX]; \ + r->prod_element[i] = \ + (cast)a->mul_element[i * 2 + HI_IDX] * \ + (cast)b->mul_element[i * 2 + HI_IDX]; \ } else { \ - r->prod_element[i] = a->mul_element[i * 2 + LO_IDX] * \ - b->mul_element[i * 2 + LO_IDX]; \ + r->prod_element[i] = \ + (cast)a->mul_element[i * 2 + LO_IDX] * \ + (cast)b->mul_element[i * 2 + LO_IDX]; \ } \ } \ } -#define VMUL(suffix, mul_element, prod_element) \ - VMUL_DO(mule##suffix, mul_element, prod_element, 1) \ - VMUL_DO(mulo##suffix, mul_element, prod_element, 0) -VMUL(sb, s8, s16) -VMUL(sh, s16, s32) -VMUL(ub, u8, u16) -VMUL(uh, u16, u32) +#define VMUL(suffix, mul_element, prod_element, cast) \ + VMUL_DO(mule##suffix, mul_element, prod_element, cast, 1) \ + VMUL_DO(mulo##suffix, mul_element, prod_element, cast, 0) +VMUL(sb, s8, s16, int16_t) +VMUL(sh, s16, s32, int32_t) +VMUL(sw, s32, s64, int64_t) +VMUL(ub, u8, u16, uint16_t) +VMUL(uh, u16, u32, uint32_t) +VMUL(uw, u32, u64, uint64_t) #undef VMUL_DO #undef VMUL @@ -898,6 +1045,383 @@ void helper_vperm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, } #if defined(HOST_WORDS_BIGENDIAN) +#define VBPERMQ_INDEX(avr, i) ((avr)->u8[(i)]) +#define VBPERMQ_DW(index) (((index) & 0x40) != 0) +#else +#define VBPERMQ_INDEX(avr, i) ((avr)->u8[15-(i)]) +#define VBPERMQ_DW(index) (((index) & 0x40) == 0) +#endif + +void helper_vbpermq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + uint64_t perm = 0; + + VECTOR_FOR_INORDER_I(i, u8) { + int index = VBPERMQ_INDEX(b, i); + + if (index < 128) { + uint64_t mask = (1ull << (63-(index & 0x3F))); + if (a->u64[VBPERMQ_DW(index)] & mask) { + perm |= (0x8000 >> i); + } + } + } + + r->u64[HI_IDX] = perm; + r->u64[LO_IDX] = 0; +} + +#undef VBPERMQ_INDEX +#undef VBPERMQ_DW + +uint64_t VGBBD_MASKS[256] = { + 0x0000000000000000ull, /* 00 */ + 0x0000000000000080ull, /* 01 */ + 0x0000000000008000ull, /* 02 */ + 0x0000000000008080ull, /* 03 */ + 0x0000000000800000ull, /* 04 */ + 0x0000000000800080ull, /* 05 */ + 0x0000000000808000ull, /* 06 */ + 0x0000000000808080ull, /* 07 */ + 0x0000000080000000ull, /* 08 */ + 0x0000000080000080ull, /* 09 */ + 0x0000000080008000ull, /* 0A */ + 0x0000000080008080ull, /* 0B */ + 0x0000000080800000ull, /* 0C */ + 0x0000000080800080ull, /* 0D */ + 0x0000000080808000ull, /* 0E */ + 0x0000000080808080ull, /* 0F */ + 0x0000008000000000ull, /* 10 */ + 0x0000008000000080ull, /* 11 */ + 0x0000008000008000ull, /* 12 */ + 0x0000008000008080ull, /* 13 */ + 0x0000008000800000ull, /* 14 */ + 0x0000008000800080ull, /* 15 */ + 0x0000008000808000ull, /* 16 */ + 0x0000008000808080ull, /* 17 */ + 0x0000008080000000ull, /* 18 */ + 0x0000008080000080ull, /* 19 */ + 0x0000008080008000ull, /* 1A */ + 0x0000008080008080ull, /* 1B */ + 0x0000008080800000ull, /* 1C */ + 0x0000008080800080ull, /* 1D */ + 0x0000008080808000ull, /* 1E */ + 0x0000008080808080ull, /* 1F */ + 0x0000800000000000ull, /* 20 */ + 0x0000800000000080ull, /* 21 */ + 0x0000800000008000ull, /* 22 */ + 0x0000800000008080ull, /* 23 */ + 0x0000800000800000ull, /* 24 */ + 0x0000800000800080ull, /* 25 */ + 0x0000800000808000ull, /* 26 */ + 0x0000800000808080ull, /* 27 */ + 0x0000800080000000ull, /* 28 */ + 0x0000800080000080ull, /* 29 */ + 0x0000800080008000ull, /* 2A */ + 0x0000800080008080ull, /* 2B */ + 0x0000800080800000ull, /* 2C */ + 0x0000800080800080ull, /* 2D */ + 0x0000800080808000ull, /* 2E */ + 0x0000800080808080ull, /* 2F */ + 0x0000808000000000ull, /* 30 */ + 0x0000808000000080ull, /* 31 */ + 0x0000808000008000ull, /* 32 */ + 0x0000808000008080ull, /* 33 */ + 0x0000808000800000ull, /* 34 */ + 0x0000808000800080ull, /* 35 */ + 0x0000808000808000ull, /* 36 */ + 0x0000808000808080ull, /* 37 */ + 0x0000808080000000ull, /* 38 */ + 0x0000808080000080ull, /* 39 */ + 0x0000808080008000ull, /* 3A */ + 0x0000808080008080ull, /* 3B */ + 0x0000808080800000ull, /* 3C */ + 0x0000808080800080ull, /* 3D */ + 0x0000808080808000ull, /* 3E */ + 0x0000808080808080ull, /* 3F */ + 0x0080000000000000ull, /* 40 */ + 0x0080000000000080ull, /* 41 */ + 0x0080000000008000ull, /* 42 */ + 0x0080000000008080ull, /* 43 */ + 0x0080000000800000ull, /* 44 */ + 0x0080000000800080ull, /* 45 */ + 0x0080000000808000ull, /* 46 */ + 0x0080000000808080ull, /* 47 */ + 0x0080000080000000ull, /* 48 */ + 0x0080000080000080ull, /* 49 */ + 0x0080000080008000ull, /* 4A */ + 0x0080000080008080ull, /* 4B */ + 0x0080000080800000ull, /* 4C */ + 0x0080000080800080ull, /* 4D */ + 0x0080000080808000ull, /* 4E */ + 0x0080000080808080ull, /* 4F */ + 0x0080008000000000ull, /* 50 */ + 0x0080008000000080ull, /* 51 */ + 0x0080008000008000ull, /* 52 */ + 0x0080008000008080ull, /* 53 */ + 0x0080008000800000ull, /* 54 */ + 0x0080008000800080ull, /* 55 */ + 0x0080008000808000ull, /* 56 */ + 0x0080008000808080ull, /* 57 */ + 0x0080008080000000ull, /* 58 */ + 0x0080008080000080ull, /* 59 */ + 0x0080008080008000ull, /* 5A */ + 0x0080008080008080ull, /* 5B */ + 0x0080008080800000ull, /* 5C */ + 0x0080008080800080ull, /* 5D */ + 0x0080008080808000ull, /* 5E */ + 0x0080008080808080ull, /* 5F */ + 0x0080800000000000ull, /* 60 */ + 0x0080800000000080ull, /* 61 */ + 0x0080800000008000ull, /* 62 */ + 0x0080800000008080ull, /* 63 */ + 0x0080800000800000ull, /* 64 */ + 0x0080800000800080ull, /* 65 */ + 0x0080800000808000ull, /* 66 */ + 0x0080800000808080ull, /* 67 */ + 0x0080800080000000ull, /* 68 */ + 0x0080800080000080ull, /* 69 */ + 0x0080800080008000ull, /* 6A */ + 0x0080800080008080ull, /* 6B */ + 0x0080800080800000ull, /* 6C */ + 0x0080800080800080ull, /* 6D */ + 0x0080800080808000ull, /* 6E */ + 0x0080800080808080ull, /* 6F */ + 0x0080808000000000ull, /* 70 */ + 0x0080808000000080ull, /* 71 */ + 0x0080808000008000ull, /* 72 */ + 0x0080808000008080ull, /* 73 */ + 0x0080808000800000ull, /* 74 */ + 0x0080808000800080ull, /* 75 */ + 0x0080808000808000ull, /* 76 */ + 0x0080808000808080ull, /* 77 */ + 0x0080808080000000ull, /* 78 */ + 0x0080808080000080ull, /* 79 */ + 0x0080808080008000ull, /* 7A */ + 0x0080808080008080ull, /* 7B */ + 0x0080808080800000ull, /* 7C */ + 0x0080808080800080ull, /* 7D */ + 0x0080808080808000ull, /* 7E */ + 0x0080808080808080ull, /* 7F */ + 0x8000000000000000ull, /* 80 */ + 0x8000000000000080ull, /* 81 */ + 0x8000000000008000ull, /* 82 */ + 0x8000000000008080ull, /* 83 */ + 0x8000000000800000ull, /* 84 */ + 0x8000000000800080ull, /* 85 */ + 0x8000000000808000ull, /* 86 */ + 0x8000000000808080ull, /* 87 */ + 0x8000000080000000ull, /* 88 */ + 0x8000000080000080ull, /* 89 */ + 0x8000000080008000ull, /* 8A */ + 0x8000000080008080ull, /* 8B */ + 0x8000000080800000ull, /* 8C */ + 0x8000000080800080ull, /* 8D */ + 0x8000000080808000ull, /* 8E */ + 0x8000000080808080ull, /* 8F */ + 0x8000008000000000ull, /* 90 */ + 0x8000008000000080ull, /* 91 */ + 0x8000008000008000ull, /* 92 */ + 0x8000008000008080ull, /* 93 */ + 0x8000008000800000ull, /* 94 */ + 0x8000008000800080ull, /* 95 */ + 0x8000008000808000ull, /* 96 */ + 0x8000008000808080ull, /* 97 */ + 0x8000008080000000ull, /* 98 */ + 0x8000008080000080ull, /* 99 */ + 0x8000008080008000ull, /* 9A */ + 0x8000008080008080ull, /* 9B */ + 0x8000008080800000ull, /* 9C */ + 0x8000008080800080ull, /* 9D */ + 0x8000008080808000ull, /* 9E */ + 0x8000008080808080ull, /* 9F */ + 0x8000800000000000ull, /* A0 */ + 0x8000800000000080ull, /* A1 */ + 0x8000800000008000ull, /* A2 */ + 0x8000800000008080ull, /* A3 */ + 0x8000800000800000ull, /* A4 */ + 0x8000800000800080ull, /* A5 */ + 0x8000800000808000ull, /* A6 */ + 0x8000800000808080ull, /* A7 */ + 0x8000800080000000ull, /* A8 */ + 0x8000800080000080ull, /* A9 */ + 0x8000800080008000ull, /* AA */ + 0x8000800080008080ull, /* AB */ + 0x8000800080800000ull, /* AC */ + 0x8000800080800080ull, /* AD */ + 0x8000800080808000ull, /* AE */ + 0x8000800080808080ull, /* AF */ + 0x8000808000000000ull, /* B0 */ + 0x8000808000000080ull, /* B1 */ + 0x8000808000008000ull, /* B2 */ + 0x8000808000008080ull, /* B3 */ + 0x8000808000800000ull, /* B4 */ + 0x8000808000800080ull, /* B5 */ + 0x8000808000808000ull, /* B6 */ + 0x8000808000808080ull, /* B7 */ + 0x8000808080000000ull, /* B8 */ + 0x8000808080000080ull, /* B9 */ + 0x8000808080008000ull, /* BA */ + 0x8000808080008080ull, /* BB */ + 0x8000808080800000ull, /* BC */ + 0x8000808080800080ull, /* BD */ + 0x8000808080808000ull, /* BE */ + 0x8000808080808080ull, /* BF */ + 0x8080000000000000ull, /* C0 */ + 0x8080000000000080ull, /* C1 */ + 0x8080000000008000ull, /* C2 */ + 0x8080000000008080ull, /* C3 */ + 0x8080000000800000ull, /* C4 */ + 0x8080000000800080ull, /* C5 */ + 0x8080000000808000ull, /* C6 */ + 0x8080000000808080ull, /* C7 */ + 0x8080000080000000ull, /* C8 */ + 0x8080000080000080ull, /* C9 */ + 0x8080000080008000ull, /* CA */ + 0x8080000080008080ull, /* CB */ + 0x8080000080800000ull, /* CC */ + 0x8080000080800080ull, /* CD */ + 0x8080000080808000ull, /* CE */ + 0x8080000080808080ull, /* CF */ + 0x8080008000000000ull, /* D0 */ + 0x8080008000000080ull, /* D1 */ + 0x8080008000008000ull, /* D2 */ + 0x8080008000008080ull, /* D3 */ + 0x8080008000800000ull, /* D4 */ + 0x8080008000800080ull, /* D5 */ + 0x8080008000808000ull, /* D6 */ + 0x8080008000808080ull, /* D7 */ + 0x8080008080000000ull, /* D8 */ + 0x8080008080000080ull, /* D9 */ + 0x8080008080008000ull, /* DA */ + 0x8080008080008080ull, /* DB */ + 0x8080008080800000ull, /* DC */ + 0x8080008080800080ull, /* DD */ + 0x8080008080808000ull, /* DE */ + 0x8080008080808080ull, /* DF */ + 0x8080800000000000ull, /* E0 */ + 0x8080800000000080ull, /* E1 */ + 0x8080800000008000ull, /* E2 */ + 0x8080800000008080ull, /* E3 */ + 0x8080800000800000ull, /* E4 */ + 0x8080800000800080ull, /* E5 */ + 0x8080800000808000ull, /* E6 */ + 0x8080800000808080ull, /* E7 */ + 0x8080800080000000ull, /* E8 */ + 0x8080800080000080ull, /* E9 */ + 0x8080800080008000ull, /* EA */ + 0x8080800080008080ull, /* EB */ + 0x8080800080800000ull, /* EC */ + 0x8080800080800080ull, /* ED */ + 0x8080800080808000ull, /* EE */ + 0x8080800080808080ull, /* EF */ + 0x8080808000000000ull, /* F0 */ + 0x8080808000000080ull, /* F1 */ + 0x8080808000008000ull, /* F2 */ + 0x8080808000008080ull, /* F3 */ + 0x8080808000800000ull, /* F4 */ + 0x8080808000800080ull, /* F5 */ + 0x8080808000808000ull, /* F6 */ + 0x8080808000808080ull, /* F7 */ + 0x8080808080000000ull, /* F8 */ + 0x8080808080000080ull, /* F9 */ + 0x8080808080008000ull, /* FA */ + 0x8080808080008080ull, /* FB */ + 0x8080808080800000ull, /* FC */ + 0x8080808080800080ull, /* FD */ + 0x8080808080808000ull, /* FE */ + 0x8080808080808080ull, /* FF */ +}; + +void helper_vgbbd(ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + uint64_t t[2] = { 0, 0 }; + + VECTOR_FOR_INORDER_I(i, u8) { +#if defined(HOST_WORDS_BIGENDIAN) + t[i>>3] |= VGBBD_MASKS[b->u8[i]] >> (i & 7); +#else + t[i>>3] |= VGBBD_MASKS[b->u8[i]] >> (7-(i & 7)); +#endif + } + + r->u64[0] = t[0]; + r->u64[1] = t[1]; +} + +#define PMSUM(name, srcfld, trgfld, trgtyp) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ +{ \ + int i, j; \ + trgtyp prod[sizeof(ppc_avr_t)/sizeof(a->srcfld[0])]; \ + \ + VECTOR_FOR_INORDER_I(i, srcfld) { \ + prod[i] = 0; \ + for (j = 0; j < sizeof(a->srcfld[0]) * 8; j++) { \ + if (a->srcfld[i] & (1ull<<j)) { \ + prod[i] ^= ((trgtyp)b->srcfld[i] << j); \ + } \ + } \ + } \ + \ + VECTOR_FOR_INORDER_I(i, trgfld) { \ + r->trgfld[i] = prod[2*i] ^ prod[2*i+1]; \ + } \ +} + +PMSUM(vpmsumb, u8, u16, uint16_t) +PMSUM(vpmsumh, u16, u32, uint32_t) +PMSUM(vpmsumw, u32, u64, uint64_t) + +void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + +#ifdef CONFIG_INT128 + int i, j; + __uint128_t prod[2]; + + VECTOR_FOR_INORDER_I(i, u64) { + prod[i] = 0; + for (j = 0; j < 64; j++) { + if (a->u64[i] & (1ull<<j)) { + prod[i] ^= (((__uint128_t)b->u64[i]) << j); + } + } + } + + r->u128 = prod[0] ^ prod[1]; + +#else + int i, j; + ppc_avr_t prod[2]; + + VECTOR_FOR_INORDER_I(i, u64) { + prod[i].u64[LO_IDX] = prod[i].u64[HI_IDX] = 0; + for (j = 0; j < 64; j++) { + if (a->u64[i] & (1ull<<j)) { + ppc_avr_t bshift; + if (j == 0) { + bshift.u64[HI_IDX] = 0; + bshift.u64[LO_IDX] = b->u64[i]; + } else { + bshift.u64[HI_IDX] = b->u64[i] >> (64-j); + bshift.u64[LO_IDX] = b->u64[i] << j; + } + prod[i].u64[LO_IDX] ^= bshift.u64[LO_IDX]; + prod[i].u64[HI_IDX] ^= bshift.u64[HI_IDX]; + } + } + } + + r->u64[LO_IDX] = prod[0].u64[LO_IDX] ^ prod[1].u64[LO_IDX]; + r->u64[HI_IDX] = prod[0].u64[HI_IDX] ^ prod[1].u64[HI_IDX]; +#endif +} + + +#if defined(HOST_WORDS_BIGENDIAN) #define PKBIG 1 #else #define PKBIG 0 @@ -948,10 +1472,14 @@ VPK(shss, s16, s8, cvtshsb, 1) VPK(shus, s16, u8, cvtshub, 1) VPK(swss, s32, s16, cvtswsh, 1) VPK(swus, s32, u16, cvtswuh, 1) +VPK(sdss, s64, s32, cvtsdsw, 1) +VPK(sdus, s64, u32, cvtsduw, 1) VPK(uhus, u16, u8, cvtuhub, 1) VPK(uwus, u32, u16, cvtuwuh, 1) +VPK(udus, u64, u32, cvtuduw, 1) VPK(uhum, u16, u8, I, 0) VPK(uwum, u32, u16, I, 0) +VPK(udum, u64, u32, I, 0) #undef I #undef VPK #undef PKBIG @@ -983,23 +1511,21 @@ VRFI(p, float_round_up) VRFI(z, float_round_to_zero) #undef VRFI -#define VROTATE(suffix, element) \ +#define VROTATE(suffix, element, mask) \ void helper_vrl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << \ - (3 + (sizeof(a->element[0]) >> 1))) \ - - 1); \ unsigned int shift = b->element[i] & mask; \ r->element[i] = (a->element[i] << shift) | \ (a->element[i] >> (sizeof(a->element[0]) * 8 - shift)); \ } \ } -VROTATE(b, u8) -VROTATE(h, u16) -VROTATE(w, u32) +VROTATE(b, u8, 0x7) +VROTATE(h, u16, 0xF) +VROTATE(w, u32, 0x1F) +VROTATE(d, u64, 0x3F) #undef VROTATE void helper_vrsqrtefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) @@ -1080,23 +1606,21 @@ VSHIFT(r, RIGHT) #undef LEFT #undef RIGHT -#define VSL(suffix, element) \ +#define VSL(suffix, element, mask) \ void helper_vsl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << \ - (3 + (sizeof(a->element[0]) >> 1))) \ - - 1); \ unsigned int shift = b->element[i] & mask; \ \ r->element[i] = a->element[i] << shift; \ } \ } -VSL(b, u8) -VSL(h, u16) -VSL(w, u32) +VSL(b, u8, 0x7) +VSL(h, u16, 0x0F) +VSL(w, u32, 0x1F) +VSL(d, u64, 0x3F) #undef VSL void helper_vsldoi(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t shift) @@ -1180,26 +1704,24 @@ VSPLTI(h, s16, int16_t) VSPLTI(w, s32, int32_t) #undef VSPLTI -#define VSR(suffix, element) \ +#define VSR(suffix, element, mask) \ void helper_vsr##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << \ - (3 + (sizeof(a->element[0]) >> 1))) \ - - 1); \ unsigned int shift = b->element[i] & mask; \ - \ r->element[i] = a->element[i] >> shift; \ } \ } -VSR(ab, s8) -VSR(ah, s16) -VSR(aw, s32) -VSR(b, u8) -VSR(h, u16) -VSR(w, u32) +VSR(ab, s8, 0x7) +VSR(ah, s16, 0xF) +VSR(aw, s32, 0x1F) +VSR(ad, s64, 0x3F) +VSR(b, u8, 0x7) +VSR(h, u16, 0xF) +VSR(w, u32, 0x1F) +VSR(d, u64, 0x3F) #undef VSR void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) @@ -1379,12 +1901,819 @@ VUPKPX(hpx, UPKHI) } VUPK(hsb, s16, s8, UPKHI) VUPK(hsh, s32, s16, UPKHI) +VUPK(hsw, s64, s32, UPKHI) VUPK(lsb, s16, s8, UPKLO) VUPK(lsh, s32, s16, UPKLO) +VUPK(lsw, s64, s32, UPKLO) #undef VUPK #undef UPKHI #undef UPKLO +#define VGENERIC_DO(name, element) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *b) \ + { \ + int i; \ + \ + VECTOR_FOR_INORDER_I(i, element) { \ + r->element[i] = name(b->element[i]); \ + } \ + } + +#define clzb(v) ((v) ? clz32((uint32_t)(v) << 24) : 8) +#define clzh(v) ((v) ? clz32((uint32_t)(v) << 16) : 16) +#define clzw(v) clz32((v)) +#define clzd(v) clz64((v)) + +VGENERIC_DO(clzb, u8) +VGENERIC_DO(clzh, u16) +VGENERIC_DO(clzw, u32) +VGENERIC_DO(clzd, u64) + +#undef clzb +#undef clzh +#undef clzw +#undef clzd + +#define popcntb(v) ctpop8(v) +#define popcnth(v) ctpop16(v) +#define popcntw(v) ctpop32(v) +#define popcntd(v) ctpop64(v) + +VGENERIC_DO(popcntb, u8) +VGENERIC_DO(popcnth, u16) +VGENERIC_DO(popcntw, u32) +VGENERIC_DO(popcntd, u64) + +#undef popcntb +#undef popcnth +#undef popcntw +#undef popcntd + +#undef VGENERIC_DO + +#if defined(HOST_WORDS_BIGENDIAN) +#define QW_ONE { .u64 = { 0, 1 } } +#else +#define QW_ONE { .u64 = { 1, 0 } } +#endif + +#ifndef CONFIG_INT128 + +static inline void avr_qw_not(ppc_avr_t *t, ppc_avr_t a) +{ + t->u64[0] = ~a.u64[0]; + t->u64[1] = ~a.u64[1]; +} + +static int avr_qw_cmpu(ppc_avr_t a, ppc_avr_t b) +{ + if (a.u64[HI_IDX] < b.u64[HI_IDX]) { + return -1; + } else if (a.u64[HI_IDX] > b.u64[HI_IDX]) { + return 1; + } else if (a.u64[LO_IDX] < b.u64[LO_IDX]) { + return -1; + } else if (a.u64[LO_IDX] > b.u64[LO_IDX]) { + return 1; + } else { + return 0; + } +} + +static void avr_qw_add(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) +{ + t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX]; + t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] + + (~a.u64[LO_IDX] < b.u64[LO_IDX]); +} + +static int avr_qw_addc(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) +{ + ppc_avr_t not_a; + t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX]; + t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] + + (~a.u64[LO_IDX] < b.u64[LO_IDX]); + avr_qw_not(¬_a, a); + return avr_qw_cmpu(not_a, b) < 0; +} + +#endif + +void helper_vadduqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + b->u128; +#else + avr_qw_add(r, *a, *b); +#endif +} + +void helper_vaddeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + b->u128 + (c->u128 & 1); +#else + + if (c->u64[LO_IDX] & 1) { + ppc_avr_t tmp; + + tmp.u64[HI_IDX] = 0; + tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1; + avr_qw_add(&tmp, *a, tmp); + avr_qw_add(r, tmp, *b); + } else { + avr_qw_add(r, *a, *b); + } +#endif +} + +void helper_vaddcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = (~a->u128 < b->u128); +#else + ppc_avr_t not_a; + + avr_qw_not(¬_a, *a); + + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = (avr_qw_cmpu(not_a, *b) < 0); +#endif +} + +void helper_vaddecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + int carry_out = (~a->u128 < b->u128); + if (!carry_out && (c->u128 & 1)) { + carry_out = ((a->u128 + b->u128 + 1) == 0) && + ((a->u128 != 0) || (b->u128 != 0)); + } + r->u128 = carry_out; +#else + + int carry_in = c->u64[LO_IDX] & 1; + int carry_out = 0; + ppc_avr_t tmp; + + carry_out = avr_qw_addc(&tmp, *a, *b); + + if (!carry_out && carry_in) { + ppc_avr_t one = QW_ONE; + carry_out = avr_qw_addc(&tmp, tmp, one); + } + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry_out; +#endif +} + +void helper_vsubuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 - b->u128; +#else + ppc_avr_t tmp; + ppc_avr_t one = QW_ONE; + + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + avr_qw_add(r, tmp, one); +#endif +} + +void helper_vsubeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + ~b->u128 + (c->u128 & 1); +#else + ppc_avr_t tmp, sum; + + avr_qw_not(&tmp, *b); + avr_qw_add(&sum, *a, tmp); + + tmp.u64[HI_IDX] = 0; + tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1; + avr_qw_add(r, sum, tmp); +#endif +} + +void helper_vsubcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = (~a->u128 < ~b->u128) || + (a->u128 + ~b->u128 == (__uint128_t)-1); +#else + int carry = (avr_qw_cmpu(*a, *b) > 0); + if (!carry) { + ppc_avr_t tmp; + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + carry = ((tmp.s64[HI_IDX] == -1ull) && (tmp.s64[LO_IDX] == -1ull)); + } + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry; +#endif +} + +void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = + (~a->u128 < ~b->u128) || + ((c->u128 & 1) && (a->u128 + ~b->u128 == (__uint128_t)-1)); +#else + int carry_in = c->u64[LO_IDX] & 1; + int carry_out = (avr_qw_cmpu(*a, *b) > 0); + if (!carry_out && carry_in) { + ppc_avr_t tmp; + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + carry_out = ((tmp.u64[HI_IDX] == -1ull) && (tmp.u64[LO_IDX] == -1ull)); + } + + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry_out; +#endif +} + +#define BCD_PLUS_PREF_1 0xC +#define BCD_PLUS_PREF_2 0xF +#define BCD_PLUS_ALT_1 0xA +#define BCD_NEG_PREF 0xD +#define BCD_NEG_ALT 0xB +#define BCD_PLUS_ALT_2 0xE + +#if defined(HOST_WORDS_BIGENDIAN) +#define BCD_DIG_BYTE(n) (15 - (n/2)) +#else +#define BCD_DIG_BYTE(n) (n/2) +#endif + +static int bcd_get_sgn(ppc_avr_t *bcd) +{ + switch (bcd->u8[BCD_DIG_BYTE(0)] & 0xF) { + case BCD_PLUS_PREF_1: + case BCD_PLUS_PREF_2: + case BCD_PLUS_ALT_1: + case BCD_PLUS_ALT_2: + { + return 1; + } + + case BCD_NEG_PREF: + case BCD_NEG_ALT: + { + return -1; + } + + default: + { + return 0; + } + } +} + +static int bcd_preferred_sgn(int sgn, int ps) +{ + if (sgn >= 0) { + return (ps == 0) ? BCD_PLUS_PREF_1 : BCD_PLUS_PREF_2; + } else { + return BCD_NEG_PREF; + } +} + +static uint8_t bcd_get_digit(ppc_avr_t *bcd, int n, int *invalid) +{ + uint8_t result; + if (n & 1) { + result = bcd->u8[BCD_DIG_BYTE(n)] >> 4; + } else { + result = bcd->u8[BCD_DIG_BYTE(n)] & 0xF; + } + + if (unlikely(result > 9)) { + *invalid = true; + } + return result; +} + +static void bcd_put_digit(ppc_avr_t *bcd, uint8_t digit, int n) +{ + if (n & 1) { + bcd->u8[BCD_DIG_BYTE(n)] &= 0x0F; + bcd->u8[BCD_DIG_BYTE(n)] |= (digit<<4); + } else { + bcd->u8[BCD_DIG_BYTE(n)] &= 0xF0; + bcd->u8[BCD_DIG_BYTE(n)] |= digit; + } +} + +static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + int invalid = 0; + for (i = 31; i > 0; i--) { + uint8_t dig_a = bcd_get_digit(a, i, &invalid); + uint8_t dig_b = bcd_get_digit(b, i, &invalid); + if (unlikely(invalid)) { + return 0; /* doesnt matter */ + } else if (dig_a > dig_b) { + return 1; + } else if (dig_a < dig_b) { + return -1; + } + } + + return 0; +} + +static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, + int *overflow) +{ + int carry = 0; + int i; + int is_zero = 1; + for (i = 1; i <= 31; i++) { + uint8_t digit = bcd_get_digit(a, i, invalid) + + bcd_get_digit(b, i, invalid) + carry; + is_zero &= (digit == 0); + if (digit > 9) { + carry = 1; + digit -= 10; + } else { + carry = 0; + } + + bcd_put_digit(t, digit, i); + + if (unlikely(*invalid)) { + return -1; + } + } + + *overflow = carry; + return is_zero; +} + +static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, + int *overflow) +{ + int carry = 0; + int i; + int is_zero = 1; + for (i = 1; i <= 31; i++) { + uint8_t digit = bcd_get_digit(a, i, invalid) - + bcd_get_digit(b, i, invalid) + carry; + is_zero &= (digit == 0); + if (digit & 0x80) { + carry = -1; + digit += 10; + } else { + carry = 0; + } + + bcd_put_digit(t, digit, i); + + if (unlikely(*invalid)) { + return -1; + } + } + + *overflow = carry; + return is_zero; +} + +uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + + int sgna = bcd_get_sgn(a); + int sgnb = bcd_get_sgn(b); + int invalid = (sgna == 0) || (sgnb == 0); + int overflow = 0; + int zero = 0; + uint32_t cr = 0; + ppc_avr_t result = { .u64 = { 0, 0 } }; + + if (!invalid) { + if (sgna == sgnb) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); + zero = bcd_add_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? 4 : 8; + } else if (bcd_cmp_mag(a, b) > 0) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); + zero = bcd_sub_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? 4 : 8; + } else { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps); + zero = bcd_sub_mag(&result, b, a, &invalid, &overflow); + cr = (sgnb > 0) ? 4 : 8; + } + } + + if (unlikely(invalid)) { + result.u64[HI_IDX] = result.u64[LO_IDX] = -1; + cr = 1; + } else if (overflow) { + cr |= 1; + } else if (zero) { + cr = 2; + } + + *r = result; + + return cr; +} + +uint32_t helper_bcdsub(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + ppc_avr_t bcopy = *b; + int sgnb = bcd_get_sgn(b); + if (sgnb < 0) { + bcd_put_digit(&bcopy, BCD_PLUS_PREF_1, 0); + } else if (sgnb > 0) { + bcd_put_digit(&bcopy, BCD_NEG_PREF, 0); + } + /* else invalid ... defer to bcdadd code for proper handling */ + + return helper_bcdadd(r, a, &bcopy, ps); +} + +static uint8_t SBOX[256] = { +0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, +0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, +0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, +0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, +0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, +0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, +0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, +0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, +0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, +0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, +0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, +0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, +0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, +0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, +0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, +0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, +0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, +0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, +0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, +0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, +0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, +0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, +0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, +0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, +0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, +0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, +0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, +0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, +0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, +0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, +0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, +0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, +}; + +static void SubBytes(ppc_avr_t *r, ppc_avr_t *a) +{ + int i; + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = SBOX[a->u8[i]]; + } +} + +static uint8_t InvSBOX[256] = { +0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, +0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, +0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, +0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, +0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, +0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, +0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, +0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, +0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, +0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, +0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, +0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, +0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, +0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, +0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, +0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, +0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, +0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, +0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, +0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, +0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, +0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, +0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, +0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, +0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, +0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, +0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, +0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, +0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, +0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, +0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, +0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D, +}; + +static void InvSubBytes(ppc_avr_t *r, ppc_avr_t *a) +{ + int i; + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = InvSBOX[a->u8[i]]; + } +} + +static uint8_t ROTL8(uint8_t x, int n) +{ + return (x << n) | (x >> (8-n)); +} + +static inline int BIT8(uint8_t x, int n) +{ + return (x & (0x80 >> n)) != 0; +} + +static uint8_t GFx02(uint8_t x) +{ + return ROTL8(x, 1) ^ (BIT8(x, 0) ? 0x1A : 0); +} + +static uint8_t GFx03(uint8_t x) +{ + return x ^ ROTL8(x, 1) ^ (BIT8(x, 0) ? 0x1A : 0); +} + +static uint8_t GFx09(uint8_t x) +{ + uint8_t term2 = ROTL8(x, 3); + uint8_t term3 = (BIT8(x, 0) ? 0x68 : 0) | (BIT8(x, 1) ? 0x14 : 0) | + (BIT8(x, 2) ? 0x02 : 0); + uint8_t term4 = (BIT8(x, 1) ? 0x20 : 0) | (BIT8(x, 2) ? 0x18 : 0); + return x ^ term2 ^ term3 ^ term4; +} + +static uint8_t GFx0B(uint8_t x) +{ + uint8_t term2 = ROTL8(x, 1); + uint8_t term3 = (x << 3) | (BIT8(x, 0) ? 0x06 : 0) | + (BIT8(x, 2) ? 0x01 : 0); + uint8_t term4 = (BIT8(x, 0) ? 0x70 : 0) | (BIT8(x, 1) ? 0x06 : 0) | + (BIT8(x, 2) ? 0x08 : 0); + uint8_t term5 = (BIT8(x, 1) ? 0x30 : 0) | (BIT8(x, 2) ? 0x02 : 0); + uint8_t term6 = BIT8(x, 2) ? 0x10 : 0; + return x ^ term2 ^ term3 ^ term4 ^ term5 ^ term6; +} + +static uint8_t GFx0D(uint8_t x) +{ + uint8_t term2 = ROTL8(x, 2); + uint8_t term3 = (x << 3) | (BIT8(x, 1) ? 0x04 : 0) | + (BIT8(x, 2) ? 0x03 : 0); + uint8_t term4 = (BIT8(x, 0) ? 0x58 : 0) | (BIT8(x, 1) ? 0x20 : 0); + uint8_t term5 = (BIT8(x, 1) ? 0x08 : 0) | (BIT8(x, 2) ? 0x10 : 0); + uint8_t term6 = BIT8(x, 2) ? 0x08 : 0; + return x ^ term2 ^ term3 ^ term4 ^ term5 ^ term6; +} + +static uint8_t GFx0E(uint8_t x) +{ + uint8_t term1 = ROTL8(x, 1); + uint8_t term2 = (x << 2) | (BIT8(x, 2) ? 0x02 : 0) | + (BIT8(x, 1) ? 0x01 : 0); + uint8_t term3 = (x << 3) | (BIT8(x, 1) ? 0x04 : 0) | + (BIT8(x, 2) ? 0x01 : 0); + uint8_t term4 = (BIT8(x, 0) ? 0x40 : 0) | (BIT8(x, 1) ? 0x28 : 0) | + (BIT8(x, 2) ? 0x10 : 0); + uint8_t term5 = (BIT8(x, 2) ? 0x08 : 0); + return term1 ^ term2 ^ term3 ^ term4 ^ term5; +} + +#if defined(HOST_WORDS_BIGENDIAN) +#define MCB(x, i, b) ((x)->u8[(i)*4 + (b)]) +#else +#define MCB(x, i, b) ((x)->u8[15 - ((i)*4 + (b))]) +#endif + +static void MixColumns(ppc_avr_t *r, ppc_avr_t *x) +{ + int i; + for (i = 0; i < 4; i++) { + MCB(r, i, 0) = GFx02(MCB(x, i, 0)) ^ GFx03(MCB(x, i, 1)) ^ + MCB(x, i, 2) ^ MCB(x, i, 3); + MCB(r, i, 1) = MCB(x, i, 0) ^ GFx02(MCB(x, i, 1)) ^ + GFx03(MCB(x, i, 2)) ^ MCB(x, i, 3); + MCB(r, i, 2) = MCB(x, i, 0) ^ MCB(x, i, 1) ^ + GFx02(MCB(x, i, 2)) ^ GFx03(MCB(x, i, 3)); + MCB(r, i, 3) = GFx03(MCB(x, i, 0)) ^ MCB(x, i, 1) ^ + MCB(x, i, 2) ^ GFx02(MCB(x, i, 3)); + } +} + +static void InvMixColumns(ppc_avr_t *r, ppc_avr_t *x) +{ + int i; + for (i = 0; i < 4; i++) { + MCB(r, i, 0) = GFx0E(MCB(x, i, 0)) ^ GFx0B(MCB(x, i, 1)) ^ + GFx0D(MCB(x, i, 2)) ^ GFx09(MCB(x, i, 3)); + MCB(r, i, 1) = GFx09(MCB(x, i, 0)) ^ GFx0E(MCB(x, i, 1)) ^ + GFx0B(MCB(x, i, 2)) ^ GFx0D(MCB(x, i, 3)); + MCB(r, i, 2) = GFx0D(MCB(x, i, 0)) ^ GFx09(MCB(x, i, 1)) ^ + GFx0E(MCB(x, i, 2)) ^ GFx0B(MCB(x, i, 3)); + MCB(r, i, 3) = GFx0B(MCB(x, i, 0)) ^ GFx0D(MCB(x, i, 1)) ^ + GFx09(MCB(x, i, 2)) ^ GFx0E(MCB(x, i, 3)); + } +} + +static void ShiftRows(ppc_avr_t *r, ppc_avr_t *x) +{ + MCB(r, 0, 0) = MCB(x, 0, 0); + MCB(r, 1, 0) = MCB(x, 1, 0); + MCB(r, 2, 0) = MCB(x, 2, 0); + MCB(r, 3, 0) = MCB(x, 3, 0); + + MCB(r, 0, 1) = MCB(x, 1, 1); + MCB(r, 1, 1) = MCB(x, 2, 1); + MCB(r, 2, 1) = MCB(x, 3, 1); + MCB(r, 3, 1) = MCB(x, 0, 1); + + MCB(r, 0, 2) = MCB(x, 2, 2); + MCB(r, 1, 2) = MCB(x, 3, 2); + MCB(r, 2, 2) = MCB(x, 0, 2); + MCB(r, 3, 2) = MCB(x, 1, 2); + + MCB(r, 0, 3) = MCB(x, 3, 3); + MCB(r, 1, 3) = MCB(x, 0, 3); + MCB(r, 2, 3) = MCB(x, 1, 3); + MCB(r, 3, 3) = MCB(x, 2, 3); +} + +static void InvShiftRows(ppc_avr_t *r, ppc_avr_t *x) +{ + MCB(r, 0, 0) = MCB(x, 0, 0); + MCB(r, 1, 0) = MCB(x, 1, 0); + MCB(r, 2, 0) = MCB(x, 2, 0); + MCB(r, 3, 0) = MCB(x, 3, 0); + + MCB(r, 0, 1) = MCB(x, 3, 1); + MCB(r, 1, 1) = MCB(x, 0, 1); + MCB(r, 2, 1) = MCB(x, 1, 1); + MCB(r, 3, 1) = MCB(x, 2, 1); + + MCB(r, 0, 2) = MCB(x, 2, 2); + MCB(r, 1, 2) = MCB(x, 3, 2); + MCB(r, 2, 2) = MCB(x, 0, 2); + MCB(r, 3, 2) = MCB(x, 1, 2); + + MCB(r, 0, 3) = MCB(x, 1, 3); + MCB(r, 1, 3) = MCB(x, 2, 3); + MCB(r, 2, 3) = MCB(x, 3, 3); + MCB(r, 3, 3) = MCB(x, 0, 3); +} + +#undef MCB + +void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a) +{ + SubBytes(r, a); +} + +void helper_vcipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t vtemp1, vtemp2, vtemp3; + SubBytes(&vtemp1, a); + ShiftRows(&vtemp2, &vtemp1); + MixColumns(&vtemp3, &vtemp2); + r->u64[0] = vtemp3.u64[0] ^ b->u64[0]; + r->u64[1] = vtemp3.u64[1] ^ b->u64[1]; +} + +void helper_vcipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t vtemp1, vtemp2; + SubBytes(&vtemp1, a); + ShiftRows(&vtemp2, &vtemp1); + r->u64[0] = vtemp2.u64[0] ^ b->u64[0]; + r->u64[1] = vtemp2.u64[1] ^ b->u64[1]; +} + +void helper_vncipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + /* This differs from what is written in ISA V2.07. The RTL is */ + /* incorrect and will be fixed in V2.07B. */ + ppc_avr_t vtemp1, vtemp2, vtemp3; + InvShiftRows(&vtemp1, a); + InvSubBytes(&vtemp2, &vtemp1); + vtemp3.u64[0] = vtemp2.u64[0] ^ b->u64[0]; + vtemp3.u64[1] = vtemp2.u64[1] ^ b->u64[1]; + InvMixColumns(r, &vtemp3); +} + +void helper_vncipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t vtemp1, vtemp2; + InvShiftRows(&vtemp1, a); + InvSubBytes(&vtemp2, &vtemp1); + r->u64[0] = vtemp2.u64[0] ^ b->u64[0]; + r->u64[1] = vtemp2.u64[1] ^ b->u64[1]; +} + +#define ROTRu32(v, n) (((v) >> (n)) | ((v) << (32-n))) +#if defined(HOST_WORDS_BIGENDIAN) +#define EL_IDX(i) (i) +#else +#define EL_IDX(i) (3 - (i)) +#endif + +void helper_vshasigmaw(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) +{ + int st = (st_six & 0x10) != 0; + int six = st_six & 0xF; + int i; + + VECTOR_FOR_INORDER_I(i, u32) { + if (st == 0) { + if ((six & (0x8 >> i)) == 0) { + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 7) ^ + ROTRu32(a->u32[EL_IDX(i)], 18) ^ + (a->u32[EL_IDX(i)] >> 3); + } else { /* six.bit[i] == 1 */ + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 17) ^ + ROTRu32(a->u32[EL_IDX(i)], 19) ^ + (a->u32[EL_IDX(i)] >> 10); + } + } else { /* st == 1 */ + if ((six & (0x8 >> i)) == 0) { + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 2) ^ + ROTRu32(a->u32[EL_IDX(i)], 13) ^ + ROTRu32(a->u32[EL_IDX(i)], 22); + } else { /* six.bit[i] == 1 */ + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 6) ^ + ROTRu32(a->u32[EL_IDX(i)], 11) ^ + ROTRu32(a->u32[EL_IDX(i)], 25); + } + } + } +} + +#undef ROTRu32 +#undef EL_IDX + +#define ROTRu64(v, n) (((v) >> (n)) | ((v) << (64-n))) +#if defined(HOST_WORDS_BIGENDIAN) +#define EL_IDX(i) (i) +#else +#define EL_IDX(i) (1 - (i)) +#endif + +void helper_vshasigmad(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) +{ + int st = (st_six & 0x10) != 0; + int six = st_six & 0xF; + int i; + + VECTOR_FOR_INORDER_I(i, u64) { + if (st == 0) { + if ((six & (0x8 >> (2*i))) == 0) { + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 1) ^ + ROTRu64(a->u64[EL_IDX(i)], 8) ^ + (a->u64[EL_IDX(i)] >> 7); + } else { /* six.bit[2*i] == 1 */ + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 19) ^ + ROTRu64(a->u64[EL_IDX(i)], 61) ^ + (a->u64[EL_IDX(i)] >> 6); + } + } else { /* st == 1 */ + if ((six & (0x8 >> (2*i))) == 0) { + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 28) ^ + ROTRu64(a->u64[EL_IDX(i)], 34) ^ + ROTRu64(a->u64[EL_IDX(i)], 39); + } else { /* six.bit[2*i] == 1 */ + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 14) ^ + ROTRu64(a->u64[EL_IDX(i)], 18) ^ + ROTRu64(a->u64[EL_IDX(i)], 41); + } + } + } +} + +#undef ROTRu64 +#undef EL_IDX + +void helper_vpermxor(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ + int i; + VECTOR_FOR_INORDER_I(i, u8) { + int indexA = c->u8[i] >> 4; + int indexB = c->u8[i] & 0xF; +#if defined(HOST_WORDS_BIGENDIAN) + r->u8[i] = a->u8[indexA] ^ b->u8[indexB]; +#else + r->u8[i] = a->u8[15-indexA] ^ b->u8[15-indexB]; +#endif + } +} + #undef VECTOR_FOR_INORDER_I #undef HI_IDX #undef LO_IDX diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 7af3fe277d..32e7a8c0a7 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -36,6 +36,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "sysemu/watchdog.h" +#include "trace.h" //#define DEBUG_KVM @@ -401,7 +402,7 @@ static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu) unsigned long kvm_arch_vcpu_id(CPUState *cpu) { - return cpu->cpu_index; + return ppc_get_vcpu_dt_id(POWERPC_CPU(cpu)); } int kvm_arch_init_vcpu(CPUState *cs) @@ -480,8 +481,7 @@ static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr) ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret != 0) { - fprintf(stderr, "Warning: Unable to retrieve SPR %d from KVM: %s\n", - spr, strerror(errno)); + trace_kvm_failed_spr_get(spr, strerror(errno)); } else { switch (id & KVM_REG_SIZE_MASK) { case KVM_REG_SIZE_U32: @@ -529,8 +529,7 @@ static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr) ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret != 0) { - fprintf(stderr, "Warning: Unable to set SPR %d to KVM: %s\n", - spr, strerror(errno)); + trace_kvm_failed_spr_set(spr, strerror(errno)); } } @@ -820,6 +819,9 @@ int kvm_arch_put_registers(CPUState *cs, int level) #ifdef TARGET_PPC64 for (i = 0; i < ARRAY_SIZE(env->slb); i++) { sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid; + if (env->slb[i].esid & SLB_ESID_V) { + sregs.u.s.ppc64.slb[i].slbe |= i; + } sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid; } #endif @@ -1029,7 +1031,9 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } - ppc_store_sdr1(env, sregs.u.s.sdr1); + if (!env->external_htab) { + ppc_store_sdr1(env, sregs.u.s.sdr1); + } /* Sync SLB */ #ifdef TARGET_PPC64 @@ -1766,22 +1770,14 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) } } -int kvmppc_fixup_cpu(PowerPCCPU *cpu) +bool kvmppc_has_cap_epr(void) { - CPUState *cs = CPU(cpu); - int smt; - - /* Adjust cpu index for SMT */ - smt = kvmppc_smt_threads(); - cs->cpu_index = (cs->cpu_index / smp_threads) * smt - + (cs->cpu_index % smp_threads); - - return 0; + return cap_epr; } -bool kvmppc_has_cap_epr(void) +bool kvmppc_has_cap_htab_fd(void) { - return cap_epr; + return cap_htab_fd; } static int kvm_ppc_register_host_cpu_type(void) @@ -1934,3 +1930,88 @@ void kvm_arch_remove_all_hw_breakpoints(void) void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) { } + +struct kvm_get_htab_buf { + struct kvm_get_htab_header header; + /* + * We require one extra byte for read + */ + target_ulong hpte[(HPTES_PER_GROUP * 2) + 1]; +}; + +uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, target_ulong pte_index) +{ + int htab_fd; + struct kvm_get_htab_fd ghf; + struct kvm_get_htab_buf *hpte_buf; + + ghf.flags = 0; + ghf.start_index = pte_index; + htab_fd = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &ghf); + if (htab_fd < 0) { + goto error_out; + } + + hpte_buf = g_malloc0(sizeof(*hpte_buf)); + /* + * Read the hpte group + */ + if (read(htab_fd, hpte_buf, sizeof(*hpte_buf)) < 0) { + goto out_close; + } + + close(htab_fd); + return (uint64_t)(uintptr_t) hpte_buf->hpte; + +out_close: + g_free(hpte_buf); + close(htab_fd); +error_out: + return 0; +} + +void kvmppc_hash64_free_pteg(uint64_t token) +{ + struct kvm_get_htab_buf *htab_buf; + + htab_buf = container_of((void *)(uintptr_t) token, struct kvm_get_htab_buf, + hpte); + g_free(htab_buf); + return; +} + +void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + int htab_fd; + struct kvm_get_htab_fd ghf; + struct kvm_get_htab_buf hpte_buf; + + ghf.flags = 0; + ghf.start_index = 0; /* Ignored */ + htab_fd = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &ghf); + if (htab_fd < 0) { + goto error_out; + } + + hpte_buf.header.n_valid = 1; + hpte_buf.header.n_invalid = 0; + hpte_buf.header.index = pte_index; + hpte_buf.hpte[0] = pte0; + hpte_buf.hpte[1] = pte1; + /* + * Write the hpte entry. + * CAUTION: write() has the warn_unused_result attribute. Hence we + * need to check the return value, even though we do nothing. + */ + if (write(htab_fd, &hpte_buf, sizeof(hpte_buf)) < 0) { + goto out_close; + } + +out_close: + close(htab_fd); + return; + +error_out: + return; +} diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 5f78e4be14..ff077ec502 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -36,13 +36,18 @@ int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); int kvmppc_reset_htab(int shift_hint); uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); #endif /* !CONFIG_USER_ONLY */ -int kvmppc_fixup_cpu(PowerPCCPU *cpu); bool kvmppc_has_cap_epr(void); int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function); +bool kvmppc_has_cap_htab_fd(void); int kvmppc_get_htab_fd(bool write); int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns); int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, uint16_t n_valid, uint16_t n_invalid); +uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, target_ulong pte_index); +void kvmppc_hash64_free_pteg(uint64_t token); + +void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index, + target_ulong pte0, target_ulong pte1); #else @@ -155,11 +160,6 @@ static inline int kvmppc_update_sdr1(CPUPPCState *env) #endif /* !CONFIG_USER_ONLY */ -static inline int kvmppc_fixup_cpu(PowerPCCPU *cpu) -{ - return -1; -} - static inline bool kvmppc_has_cap_epr(void) { return false; @@ -171,6 +171,11 @@ static inline int kvmppc_define_rtas_kernel_token(uint32_t token, return -1; } +static inline bool kvmppc_has_cap_htab_fd(void) +{ + return false; +} + static inline int kvmppc_get_htab_fd(bool write) { return -1; @@ -188,6 +193,24 @@ static inline int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, abort(); } +static inline uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, + target_ulong pte_index) +{ + abort(); +} + +static inline void kvmppc_hash64_free_pteg(uint64_t token) +{ + abort(); +} + +static inline void kvmppc_hash64_write_pte(CPUPPCState *env, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + abort(); +} + #endif #ifndef CONFIG_KVM diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 12c174f7f3..2d46ceccca 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -70,7 +70,9 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_betls(f, &env->pb[i]); for (i = 0; i < 1024; i++) qemu_get_betls(f, &env->spr[i]); - ppc_store_sdr1(env, sdr1); + if (!env->external_htab) { + ppc_store_sdr1(env, sdr1); + } qemu_get_be32s(f, &env->vscr); qemu_get_be64s(f, &env->spe_acc); qemu_get_be32s(f, &env->spe_fscr); @@ -179,9 +181,10 @@ static int cpu_post_load(void *opaque, int version_id) env->IBAT[1][i+4] = env->spr[SPR_IBAT4U + 2*i + 1]; } - /* Restore htab_base and htab_mask variables */ - ppc_store_sdr1(env, env->spr[SPR_SDR1]); - + if (!env->external_htab) { + /* Restore htab_base and htab_mask variables */ + ppc_store_sdr1(env, env->spr[SPR_SDR1]); + } hreg_compute_hflags(env); hreg_compute_mem_idx(env); diff --git a/target-ppc/misc_helper.c b/target-ppc/misc_helper.c index 616aab6fb6..dc2ebfc452 100644 --- a/target-ppc/misc_helper.c +++ b/target-ppc/misc_helper.c @@ -38,7 +38,9 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn) void helper_store_sdr1(CPUPPCState *env, target_ulong val) { - ppc_store_sdr1(env, val); + if (!env->external_htab) { + ppc_store_sdr1(env, val); + } } void helper_store_hid0_601(CPUPPCState *env, target_ulong val) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 67fc1b5dec..f2af4fbaa7 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -41,6 +41,11 @@ #endif /* + * Used to indicate whether we have allocated htab in the + * host kernel + */ +bool kvmppc_kern_htab; +/* * SLB handling */ @@ -278,12 +283,12 @@ static int ppc_hash64_pte_prot(CPUPPCState *env, static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte) { int key, amrbits; - int prot = PAGE_EXEC; + int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; /* Only recent MMUs implement Virtual Page Class Key Protection */ if (!(env->mmu_model & POWERPC_MMU_AMR)) { - return PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return prot; } key = HPTE64_R_KEY(pte.pte1); @@ -292,39 +297,94 @@ static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte) /* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */ /* env->spr[SPR_AMR]); */ + /* + * A store is permitted if the AMR bit is 0. Remove write + * protection if it is set. + */ if (amrbits & 0x2) { - prot |= PAGE_WRITE; + prot &= ~PAGE_WRITE; } + /* + * A load is permitted if the AMR bit is 0. Remove read + * protection if it is set. + */ if (amrbits & 0x1) { - prot |= PAGE_READ; + prot &= ~PAGE_READ; } return prot; } -static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, +uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index) +{ + uint64_t token = 0; + hwaddr pte_offset; + + pte_offset = pte_index * HASH_PTE_SIZE_64; + if (kvmppc_kern_htab) { + /* + * HTAB is controlled by KVM. Fetch the PTEG into a new buffer. + */ + token = kvmppc_hash64_read_pteg(cpu, pte_index); + if (token) { + return token; + } + /* + * pteg read failed, even though we have allocated htab via + * kvmppc_reset_htab. + */ + return 0; + } + /* + * HTAB is controlled by QEMU. Just point to the internally + * accessible PTEG. + */ + if (cpu->env.external_htab) { + token = (uint64_t)(uintptr_t) cpu->env.external_htab + pte_offset; + } else if (cpu->env.htab_base) { + token = cpu->env.htab_base + pte_offset; + } + return token; +} + +void ppc_hash64_stop_access(uint64_t token) +{ + if (kvmppc_kern_htab) { + return kvmppc_hash64_free_pteg(token); + } +} + +static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr hash, bool secondary, target_ulong ptem, ppc_hash_pte64_t *pte) { - hwaddr pte_offset = pteg_off; - target_ulong pte0, pte1; int i; + uint64_t token; + target_ulong pte0, pte1; + target_ulong pte_index; + pte_index = (hash & env->htab_mask) * HPTES_PER_GROUP; + token = ppc_hash64_start_access(ppc_env_get_cpu(env), pte_index); + if (!token) { + return -1; + } for (i = 0; i < HPTES_PER_GROUP; i++) { - pte0 = ppc_hash64_load_hpte0(env, pte_offset); - pte1 = ppc_hash64_load_hpte1(env, pte_offset); + pte0 = ppc_hash64_load_hpte0(env, token, i); + pte1 = ppc_hash64_load_hpte1(env, token, i); if ((pte0 & HPTE64_V_VALID) && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) && HPTE64_V_COMPARE(pte0, ptem)) { pte->pte0 = pte0; pte->pte1 = pte1; - return pte_offset; + ppc_hash64_stop_access(token); + return (pte_index + i) * HASH_PTE_SIZE_64; } - - pte_offset += HASH_PTE_SIZE_64; } - + ppc_hash64_stop_access(token); + /* + * We didn't find a valid entry. + */ return -1; } @@ -332,7 +392,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, ppc_slb_t *slb, target_ulong eaddr, ppc_hash_pte64_t *pte) { - hwaddr pteg_off, pte_offset; + hwaddr pte_offset; hwaddr hash; uint64_t vsid, epnshift, epnmask, epn, ptem; @@ -367,8 +427,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); - pteg_off = (hash * HASH_PTEG_SIZE_64) & env->htab_mask; - pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, pte); + pte_offset = ppc_hash64_pteg_search(env, hash, 0, ptem, pte); if (pte_offset == -1) { /* Secondary PTEG lookup */ @@ -377,8 +436,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); - pteg_off = (~hash * HASH_PTEG_SIZE_64) & env->htab_mask; - pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, pte); + pte_offset = ppc_hash64_pteg_search(env, ~hash, 1, ptem, pte); } return pte_offset; @@ -508,7 +566,8 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, } if (new_pte1 != pte.pte1) { - ppc_hash64_store_hpte1(env, pte_offset, new_pte1); + ppc_hash64_store_hpte(env, pte_offset / HASH_PTE_SIZE_64, + pte.pte0, new_pte1); } /* 7. Determine the real address from the PTE */ @@ -544,3 +603,23 @@ hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK; } + +void ppc_hash64_store_hpte(CPUPPCState *env, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + CPUState *cs = ENV_GET_CPU(env); + + if (kvmppc_kern_htab) { + return kvmppc_hash64_write_pte(env, pte_index, pte0, pte1); + } + + pte_index *= HASH_PTE_SIZE_64; + if (env->external_htab) { + stq_p(env->external_htab + pte_index, pte0); + stq_p(env->external_htab + pte_index + HASH_PTE_SIZE_64/2, pte1); + } else { + stq_phys(cs->as, env->htab_base + pte_index, pte0); + stq_phys(cs->as, env->htab_base + pte_index + HASH_PTE_SIZE_64/2, pte1); + } +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index a8da558ca2..1746b3e2d3 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -9,6 +9,8 @@ int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr); int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx); +void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index, + target_ulong pte0, target_ulong pte1); #endif /* @@ -75,49 +77,34 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, #define HPTE64_V_1TB_SEG 0x4000000000000000ULL #define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL -static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env, - hwaddr pte_offset) -{ - CPUState *cs = ENV_GET_CPU(env); - if (env->external_htab) { - return ldq_p(env->external_htab + pte_offset); - } else { - return ldq_phys(cs->as, env->htab_base + pte_offset); - } -} -static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env, - hwaddr pte_offset) -{ - CPUState *cs = ENV_GET_CPU(env); - if (env->external_htab) { - return ldq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2); - } else { - return ldq_phys(cs->as, - env->htab_base + pte_offset + HASH_PTE_SIZE_64/2); - } -} +extern bool kvmppc_kern_htab; +uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index); +void ppc_hash64_stop_access(uint64_t token); -static inline void ppc_hash64_store_hpte0(CPUPPCState *env, - hwaddr pte_offset, target_ulong pte0) +static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env, + uint64_t token, int index) { CPUState *cs = ENV_GET_CPU(env); + uint64_t addr; + addr = token + (index * HASH_PTE_SIZE_64); if (env->external_htab) { - stq_p(env->external_htab + pte_offset, pte0); + return ldq_p((const void *)(uintptr_t)addr); } else { - stq_phys(cs->as, env->htab_base + pte_offset, pte0); + return ldq_phys(cs->as, addr); } } -static inline void ppc_hash64_store_hpte1(CPUPPCState *env, - hwaddr pte_offset, target_ulong pte1) +static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env, + uint64_t token, int index) { CPUState *cs = ENV_GET_CPU(env); + uint64_t addr; + addr = token + (index * HASH_PTE_SIZE_64) + HASH_PTE_SIZE_64/2; if (env->external_htab) { - stq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2, pte1); + return ldq_p((const void *)(uintptr_t)addr); } else { - stq_phys(cs->as, - env->htab_base + pte_offset + HASH_PTE_SIZE_64/2, pte1); + return ldq_phys(cs->as, addr); } } diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 04a840b016..8e2f8e736a 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -2014,6 +2014,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value); + assert(!env->external_htab); if (env->spr[SPR_SDR1] != value) { env->spr[SPR_SDR1] = value; #if defined(TARGET_PPC64) @@ -2025,7 +2026,7 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) " stored in SDR1\n", htabsize); htabsize = 28; } - env->htab_mask = (1ULL << (htabsize + 18)) - 1; + env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; env->htab_base = value & SDR_64_HTABORG; } else #endif /* defined(TARGET_PPC64) */ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index c5c1108e92..91c33dcd1d 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -387,6 +387,8 @@ EXTRACT_HELPER(opc2, 1, 5); EXTRACT_HELPER(opc3, 6, 5); /* Update Cr0 flags */ EXTRACT_HELPER(Rc, 0, 1); +/* Update Cr6 flags (Altivec) */ +EXTRACT_HELPER(Rc21, 10, 1); /* Destination */ EXTRACT_HELPER(rD, 21, 5); /* Source */ @@ -622,6 +624,20 @@ static opc_handler_t invalid_handler = { .handler = gen_invalid, }; +#if defined(TARGET_PPC64) +/* NOTE: as this time, the only use of is_user_mode() is in 64 bit code. And */ +/* so the function is wrapped in the standard 64-bit ifdef in order to */ +/* avoid compiler warnings in 32-bit implementations. */ +static bool is_user_mode(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + return true; +#else + return ctx->mem_idx == 0; +#endif +} +#endif + /*** Integer comparison ***/ static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf) @@ -984,6 +1000,25 @@ GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1); /* divw divw. divwo divwo. */ GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0); GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1); + +/* div[wd]eu[o][.] */ +#define GEN_DIVE(name, hlpr, compute_ov) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0 = tcg_const_i32(compute_ov); \ + gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], cpu_env, \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); \ + tcg_temp_free_i32(t0); \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \ + } \ +} + +GEN_DIVE(divweu, divweu, 0); +GEN_DIVE(divweuo, divweu, 1); +GEN_DIVE(divwe, divwe, 0); +GEN_DIVE(divweo, divwe, 1); + #if defined(TARGET_PPC64) static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign, int compute_ov) @@ -1032,6 +1067,11 @@ GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1); /* divw divw. divwo divwo. */ GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0); GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1); + +GEN_DIVE(divdeu, divdeu, 0); +GEN_DIVE(divdeuo, divdeu, 1); +GEN_DIVE(divde, divde, 0); +GEN_DIVE(divdeo, divde, 1); #endif /* mulhw mulhw. */ @@ -1525,6 +1565,15 @@ static void gen_prtyd(DisasContext *ctx) #endif #if defined(TARGET_PPC64) +/* bpermd */ +static void gen_bpermd(DisasContext *ctx) +{ + gen_helper_bpermd(cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); +} +#endif + +#if defined(TARGET_PPC64) /* extsw & extsw. */ GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B); @@ -2169,17 +2218,31 @@ GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT); /*** Floating-Point round & convert ***/ /* fctiw */ GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT); +/* fctiwu */ +GEN_FLOAT_B(ctiwu, 0x0E, 0x04, 0, PPC2_FP_CVT_ISA206); /* fctiwz */ GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT); +/* fctiwuz */ +GEN_FLOAT_B(ctiwuz, 0x0F, 0x04, 0, PPC2_FP_CVT_ISA206); /* frsp */ GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT); #if defined(TARGET_PPC64) /* fcfid */ GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC_64B); +/* fcfids */ +GEN_FLOAT_B(cfids, 0x0E, 0x1A, 0, PPC2_FP_CVT_ISA206); +/* fcfidu */ +GEN_FLOAT_B(cfidu, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206); +/* fcfidus */ +GEN_FLOAT_B(cfidus, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206); /* fctid */ GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC_64B); +/* fctidu */ +GEN_FLOAT_B(ctidu, 0x0E, 0x1D, 0, PPC2_FP_CVT_ISA206); /* fctidz */ GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B); +/* fctidu */ +GEN_FLOAT_B(ctiduz, 0x0F, 0x1D, 0, PPC2_FP_CVT_ISA206); #endif /* frin */ @@ -2191,6 +2254,27 @@ GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT); /* frim */ GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); +static void gen_ftdiv(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)]); +} + +static void gen_ftsqrt(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); +} + + + /*** Floating-Point compare ***/ /* fcmpo */ @@ -2294,6 +2378,32 @@ static void gen_fcpsgn(DisasContext *ctx) gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0); } +static void gen_fmrgew(DisasContext *ctx) +{ + TCGv_i64 b0; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + b0 = tcg_temp_new_i64(); + tcg_gen_shri_i64(b0, cpu_fpr[rB(ctx->opcode)], 32); + tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], + b0, 0, 32); + tcg_temp_free_i64(b0); +} + +static void gen_fmrgow(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)], + cpu_fpr[rA(ctx->opcode)], + 32, 32); +} + /*** Floating-Point status & ctrl register ***/ /* mcrfs */ @@ -2585,6 +2695,14 @@ static inline void gen_qemu_ld32s(DisasContext *ctx, TCGv arg1, TCGv arg2) tcg_gen_qemu_ld32s(arg1, arg2, ctx->mem_idx); } +static void gen_qemu_ld32s_i64(DisasContext *ctx, TCGv_i64 val, TCGv addr) +{ + TCGv tmp = tcg_temp_new(); + gen_qemu_ld32s(ctx, tmp, addr); + tcg_gen_ext_tl_i64(val, tmp); + tcg_temp_free(tmp); +} + static inline void gen_qemu_ld64(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) { tcg_gen_qemu_ld64(arg1, arg2, ctx->mem_idx); @@ -2756,36 +2874,44 @@ static void gen_ld(DisasContext *ctx) /* lq */ static void gen_lq(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else int ra, rd; TCGv EA; - /* Restore CPU state */ - if (unlikely(ctx->mem_idx == 0)) { + /* lq is a legal user mode instruction starting in ISA 2.07 */ + bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + + if (!legal_in_user_mode && is_user_mode(ctx)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } + + if (!le_is_supported && ctx->le_mode) { + gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE); + return; + } + ra = rA(ctx->opcode); rd = rD(ctx->opcode); if (unlikely((rd & 1) || rd == ra)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } - if (unlikely(ctx->le_mode)) { - /* Little-endian mode is not handled */ - gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE); - return; - } + gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0x0F); - gen_qemu_ld64(ctx, cpu_gpr[rd], EA); - gen_addr_add(ctx, EA, EA, 8); - gen_qemu_ld64(ctx, cpu_gpr[rd+1], EA); + + if (unlikely(ctx->le_mode)) { + gen_qemu_ld64(ctx, cpu_gpr[rd+1], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_ld64(ctx, cpu_gpr[rd], EA); + } else { + gen_qemu_ld64(ctx, cpu_gpr[rd], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_ld64(ctx, cpu_gpr[rd+1], EA); + } tcg_temp_free(EA); -#endif } #endif @@ -2871,34 +2997,41 @@ static void gen_std(DisasContext *ctx) TCGv EA; rs = rS(ctx->opcode); - if ((ctx->opcode & 0x3) == 0x2) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else - /* stq */ - if (unlikely(ctx->mem_idx == 0)) { + if ((ctx->opcode & 0x3) == 0x2) { /* stq */ + + bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + + if (!legal_in_user_mode && is_user_mode(ctx)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - if (unlikely(rs & 1)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + + if (!le_is_supported && ctx->le_mode) { + gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE); return; } - if (unlikely(ctx->le_mode)) { - /* Little-endian mode is not handled */ - gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE); + + if (unlikely(rs & 1)) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0x03); - gen_qemu_st64(ctx, cpu_gpr[rs], EA); - gen_addr_add(ctx, EA, EA, 8); - gen_qemu_st64(ctx, cpu_gpr[rs+1], EA); + + if (unlikely(ctx->le_mode)) { + gen_qemu_st64(ctx, cpu_gpr[rs+1], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_st64(ctx, cpu_gpr[rs], EA); + } else { + gen_qemu_st64(ctx, cpu_gpr[rs], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_st64(ctx, cpu_gpr[rs+1], EA); + } tcg_temp_free(EA); -#endif } else { - /* std / stdu */ + /* std / stdu*/ if (Rc(ctx->opcode)) { if (unlikely(rA(ctx->opcode) == 0)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); @@ -3140,24 +3273,32 @@ static void gen_isync(DisasContext *ctx) gen_stop_exception(ctx); } -/* lwarx */ -static void gen_lwarx(DisasContext *ctx) -{ - TCGv t0; - TCGv gpr = cpu_gpr[rD(ctx->opcode)]; - gen_set_access_type(ctx, ACCESS_RES); - t0 = tcg_temp_local_new(); - gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x03); - gen_qemu_ld32u(ctx, gpr, t0); - tcg_gen_mov_tl(cpu_reserve, t0); - tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_temp_free(t0); +#define LARX(name, len, loadop) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv t0; \ + TCGv gpr = cpu_gpr[rD(ctx->opcode)]; \ + gen_set_access_type(ctx, ACCESS_RES); \ + t0 = tcg_temp_local_new(); \ + gen_addr_reg_index(ctx, t0); \ + if ((len) > 1) { \ + gen_check_align(ctx, t0, (len)-1); \ + } \ + gen_qemu_##loadop(ctx, gpr, t0); \ + tcg_gen_mov_tl(cpu_reserve, t0); \ + tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUPPCState, reserve_val)); \ + tcg_temp_free(t0); \ } +/* lwarx */ +LARX(lbarx, 1, ld8u); +LARX(lharx, 2, ld16u); +LARX(lwarx, 4, ld32u); + + #if defined(CONFIG_USER_ONLY) -static void gen_conditional_store (DisasContext *ctx, TCGv EA, - int reg, int size) +static void gen_conditional_store(DisasContext *ctx, TCGv EA, + int reg, int size) { TCGv t0 = tcg_temp_new(); uint32_t save_exception = ctx->exception; @@ -3171,74 +3312,115 @@ static void gen_conditional_store (DisasContext *ctx, TCGv EA, gen_exception(ctx, POWERPC_EXCP_STCX); ctx->exception = save_exception; } -#endif - -/* stwcx. */ -static void gen_stwcx_(DisasContext *ctx) -{ - TCGv t0; - gen_set_access_type(ctx, ACCESS_RES); - t0 = tcg_temp_local_new(); - gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x03); -#if defined(CONFIG_USER_ONLY) - gen_conditional_store(ctx, t0, rS(ctx->opcode), 4); #else - { - int l1; +static void gen_conditional_store(DisasContext *ctx, TCGv EA, + int reg, int size) +{ + int l1; - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - l1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); - gen_qemu_st32(ctx, cpu_gpr[rS(ctx->opcode)], t0); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_reserve, -1); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + l1 = gen_new_label(); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); +#if defined(TARGET_PPC64) + if (size == 8) { + gen_qemu_st64(ctx, cpu_gpr[reg], EA); + } else +#endif + if (size == 4) { + gen_qemu_st32(ctx, cpu_gpr[reg], EA); + } else if (size == 2) { + gen_qemu_st16(ctx, cpu_gpr[reg], EA); +#if defined(TARGET_PPC64) + } else if (size == 16) { + TCGv gpr1, gpr2 , EA8; + if (unlikely(ctx->le_mode)) { + gpr1 = cpu_gpr[reg+1]; + gpr2 = cpu_gpr[reg]; + } else { + gpr1 = cpu_gpr[reg]; + gpr2 = cpu_gpr[reg+1]; + } + gen_qemu_st64(ctx, gpr1, EA); + EA8 = tcg_temp_local_new(); + gen_addr_add(ctx, EA8, EA, 8); + gen_qemu_st64(ctx, gpr2, EA8); + tcg_temp_free(EA8); +#endif + } else { + gen_qemu_st8(ctx, cpu_gpr[reg], EA); } + gen_set_label(l1); + tcg_gen_movi_tl(cpu_reserve, -1); +} #endif - tcg_temp_free(t0); + +#define STCX(name, len) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv t0; \ + if (unlikely((len == 16) && (rD(ctx->opcode) & 1))) { \ + gen_inval_exception(ctx, \ + POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_RES); \ + t0 = tcg_temp_local_new(); \ + gen_addr_reg_index(ctx, t0); \ + if (len > 1) { \ + gen_check_align(ctx, t0, (len)-1); \ + } \ + gen_conditional_store(ctx, t0, rS(ctx->opcode), len); \ + tcg_temp_free(t0); \ } +STCX(stbcx_, 1); +STCX(sthcx_, 2); +STCX(stwcx_, 4); + #if defined(TARGET_PPC64) /* ldarx */ -static void gen_ldarx(DisasContext *ctx) -{ - TCGv t0; - TCGv gpr = cpu_gpr[rD(ctx->opcode)]; - gen_set_access_type(ctx, ACCESS_RES); - t0 = tcg_temp_local_new(); - gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x07); - gen_qemu_ld64(ctx, gpr, t0); - tcg_gen_mov_tl(cpu_reserve, t0); - tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_temp_free(t0); -} +LARX(ldarx, 8, ld64); -/* stdcx. */ -static void gen_stdcx_(DisasContext *ctx) +/* lqarx */ +static void gen_lqarx(DisasContext *ctx) { - TCGv t0; + TCGv EA; + int rd = rD(ctx->opcode); + TCGv gpr1, gpr2; + + if (unlikely((rd & 1) || (rd == rA(ctx->opcode)) || + (rd == rB(ctx->opcode)))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + gen_set_access_type(ctx, ACCESS_RES); - t0 = tcg_temp_local_new(); - gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x07); -#if defined(CONFIG_USER_ONLY) - gen_conditional_store(ctx, t0, rS(ctx->opcode), 8); -#else - { - int l1; - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - l1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); - gen_qemu_st64(ctx, cpu_gpr[rS(ctx->opcode)], t0); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_reserve, -1); + EA = tcg_temp_local_new(); + gen_addr_reg_index(ctx, EA); + gen_check_align(ctx, EA, 15); + if (unlikely(ctx->le_mode)) { + gpr1 = cpu_gpr[rd+1]; + gpr2 = cpu_gpr[rd]; + } else { + gpr1 = cpu_gpr[rd]; + gpr2 = cpu_gpr[rd+1]; } -#endif - tcg_temp_free(t0); + gen_qemu_ld64(ctx, gpr1, EA); + tcg_gen_mov_tl(cpu_reserve, EA); + + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_ld64(ctx, gpr2, EA); + + tcg_gen_st_tl(gpr1, cpu_env, offsetof(CPUPPCState, reserve_val)); + tcg_gen_st_tl(gpr2, cpu_env, offsetof(CPUPPCState, reserve_val2)); + + tcg_temp_free(EA); } + +/* stdcx. */ +STCX(stdcx_, 8); +STCX(stqcx_, 16); #endif /* defined(TARGET_PPC64) */ /* sync */ @@ -3415,6 +3597,20 @@ static void gen_lfiwax(DisasContext *ctx) tcg_temp_free(t0); } +/* lfiwzx */ +static void gen_lfiwzx(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + gen_qemu_ld32u_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + tcg_temp_free(EA); +} /*** Floating-point store ***/ #define GEN_STF(name, stop, opc, type) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -3638,6 +3834,7 @@ static void gen_b(DisasContext *ctx) #define BCOND_IM 0 #define BCOND_LR 1 #define BCOND_CTR 2 +#define BCOND_TAR 3 static inline void gen_bcond(DisasContext *ctx, int type) { @@ -3646,10 +3843,12 @@ static inline void gen_bcond(DisasContext *ctx, int type) TCGv target; ctx->exception = POWERPC_EXCP_BRANCH; - if (type == BCOND_LR || type == BCOND_CTR) { + if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { target = tcg_temp_local_new(); if (type == BCOND_CTR) tcg_gen_mov_tl(target, cpu_ctr); + else if (type == BCOND_TAR) + gen_load_spr(target, SPR_TAR); else tcg_gen_mov_tl(target, cpu_lr); } else { @@ -3731,6 +3930,11 @@ static void gen_bclr(DisasContext *ctx) gen_bcond(ctx, BCOND_LR); } +static void gen_bctar(DisasContext *ctx) +{ + gen_bcond(ctx, BCOND_TAR); +} + /*** Condition register logical ***/ #define GEN_CRLOGIC(name, tcg_op, opc) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -6650,6 +6854,9 @@ GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17); GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18); GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19); GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20); +GEN_VX_LOGICAL(veqv, tcg_gen_eqv_i64, 2, 26); +GEN_VX_LOGICAL(vnand, tcg_gen_nand_i64, 2, 22); +GEN_VX_LOGICAL(vorc, tcg_gen_orc_i64, 2, 21); #define GEN_VXFORM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -6685,24 +6892,69 @@ static void glue(gen_, name)(DisasContext *ctx) \ tcg_temp_free_ptr(rd); \ } +#define GEN_VXFORM3(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rc, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rc = gen_avr_ptr(rC(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(rd, ra, rb, rc); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rc); \ + tcg_temp_free_ptr(rd); \ +} + +/* + * Support for Altivec instruction pairs that use bit 31 (Rc) as + * an opcode bit. In general, these pairs come from different + * versions of the ISA, so we must also support a pair of flags for + * each instruction. + */ +#define GEN_VXFORM_DUAL(name0, flg0, flg2_0, name1, flg1, flg2_1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0))) { \ + gen_##name0(ctx); \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1))) { \ + gen_##name1(ctx); \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + GEN_VXFORM(vaddubm, 0, 0); GEN_VXFORM(vadduhm, 0, 1); GEN_VXFORM(vadduwm, 0, 2); +GEN_VXFORM(vaddudm, 0, 3); GEN_VXFORM(vsububm, 0, 16); GEN_VXFORM(vsubuhm, 0, 17); GEN_VXFORM(vsubuwm, 0, 18); +GEN_VXFORM(vsubudm, 0, 19); GEN_VXFORM(vmaxub, 1, 0); GEN_VXFORM(vmaxuh, 1, 1); GEN_VXFORM(vmaxuw, 1, 2); +GEN_VXFORM(vmaxud, 1, 3); GEN_VXFORM(vmaxsb, 1, 4); GEN_VXFORM(vmaxsh, 1, 5); GEN_VXFORM(vmaxsw, 1, 6); +GEN_VXFORM(vmaxsd, 1, 7); GEN_VXFORM(vminub, 1, 8); GEN_VXFORM(vminuh, 1, 9); GEN_VXFORM(vminuw, 1, 10); +GEN_VXFORM(vminud, 1, 11); GEN_VXFORM(vminsb, 1, 12); GEN_VXFORM(vminsh, 1, 13); GEN_VXFORM(vminsw, 1, 14); +GEN_VXFORM(vminsd, 1, 15); GEN_VXFORM(vavgub, 1, 16); GEN_VXFORM(vavguh, 1, 17); GEN_VXFORM(vavguw, 1, 18); @@ -6715,23 +6967,68 @@ GEN_VXFORM(vmrghw, 6, 2); GEN_VXFORM(vmrglb, 6, 4); GEN_VXFORM(vmrglh, 6, 5); GEN_VXFORM(vmrglw, 6, 6); + +static void gen_vmrgew(DisasContext *ctx) +{ + TCGv_i64 tmp; + int VT, VA, VB; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + VT = rD(ctx->opcode); + VA = rA(ctx->opcode); + VB = rB(ctx->opcode); + tmp = tcg_temp_new_i64(); + tcg_gen_shri_i64(tmp, cpu_avrh[VB], 32); + tcg_gen_deposit_i64(cpu_avrh[VT], cpu_avrh[VA], tmp, 0, 32); + tcg_gen_shri_i64(tmp, cpu_avrl[VB], 32); + tcg_gen_deposit_i64(cpu_avrl[VT], cpu_avrl[VA], tmp, 0, 32); + tcg_temp_free_i64(tmp); +} + +static void gen_vmrgow(DisasContext *ctx) +{ + int VT, VA, VB; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + VT = rD(ctx->opcode); + VA = rA(ctx->opcode); + VB = rB(ctx->opcode); + + tcg_gen_deposit_i64(cpu_avrh[VT], cpu_avrh[VB], cpu_avrh[VA], 32, 32); + tcg_gen_deposit_i64(cpu_avrl[VT], cpu_avrl[VB], cpu_avrl[VA], 32, 32); +} + GEN_VXFORM(vmuloub, 4, 0); GEN_VXFORM(vmulouh, 4, 1); +GEN_VXFORM(vmulouw, 4, 2); +GEN_VXFORM(vmuluwm, 4, 2); +GEN_VXFORM_DUAL(vmulouw, PPC_ALTIVEC, PPC_NONE, + vmuluwm, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM(vmulosb, 4, 4); GEN_VXFORM(vmulosh, 4, 5); +GEN_VXFORM(vmulosw, 4, 6); GEN_VXFORM(vmuleub, 4, 8); GEN_VXFORM(vmuleuh, 4, 9); +GEN_VXFORM(vmuleuw, 4, 10); GEN_VXFORM(vmulesb, 4, 12); GEN_VXFORM(vmulesh, 4, 13); +GEN_VXFORM(vmulesw, 4, 14); GEN_VXFORM(vslb, 2, 4); GEN_VXFORM(vslh, 2, 5); GEN_VXFORM(vslw, 2, 6); +GEN_VXFORM(vsld, 2, 23); GEN_VXFORM(vsrb, 2, 8); GEN_VXFORM(vsrh, 2, 9); GEN_VXFORM(vsrw, 2, 10); +GEN_VXFORM(vsrd, 2, 27); GEN_VXFORM(vsrab, 2, 12); GEN_VXFORM(vsrah, 2, 13); GEN_VXFORM(vsraw, 2, 14); +GEN_VXFORM(vsrad, 2, 15); GEN_VXFORM(vslo, 6, 16); GEN_VXFORM(vsro, 6, 17); GEN_VXFORM(vaddcuw, 0, 6); @@ -6748,19 +7045,36 @@ GEN_VXFORM_ENV(vsubuws, 0, 26); GEN_VXFORM_ENV(vsubsbs, 0, 28); GEN_VXFORM_ENV(vsubshs, 0, 29); GEN_VXFORM_ENV(vsubsws, 0, 30); +GEN_VXFORM(vadduqm, 0, 4); +GEN_VXFORM(vaddcuq, 0, 5); +GEN_VXFORM3(vaddeuqm, 30, 0); +GEN_VXFORM3(vaddecuq, 30, 0); +GEN_VXFORM_DUAL(vaddeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ + vaddecuq, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM(vsubuqm, 0, 20); +GEN_VXFORM(vsubcuq, 0, 21); +GEN_VXFORM3(vsubeuqm, 31, 0); +GEN_VXFORM3(vsubecuq, 31, 0); +GEN_VXFORM_DUAL(vsubeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ + vsubecuq, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM(vrlb, 2, 0); GEN_VXFORM(vrlh, 2, 1); GEN_VXFORM(vrlw, 2, 2); +GEN_VXFORM(vrld, 2, 3); GEN_VXFORM(vsl, 2, 7); GEN_VXFORM(vsr, 2, 11); GEN_VXFORM_ENV(vpkuhum, 7, 0); GEN_VXFORM_ENV(vpkuwum, 7, 1); +GEN_VXFORM_ENV(vpkudum, 7, 17); GEN_VXFORM_ENV(vpkuhus, 7, 2); GEN_VXFORM_ENV(vpkuwus, 7, 3); +GEN_VXFORM_ENV(vpkudus, 7, 19); GEN_VXFORM_ENV(vpkshus, 7, 4); GEN_VXFORM_ENV(vpkswus, 7, 5); +GEN_VXFORM_ENV(vpksdus, 7, 21); GEN_VXFORM_ENV(vpkshss, 7, 6); GEN_VXFORM_ENV(vpkswss, 7, 7); +GEN_VXFORM_ENV(vpksdss, 7, 23); GEN_VXFORM(vpkpx, 7, 12); GEN_VXFORM_ENV(vsum4ubs, 4, 24); GEN_VXFORM_ENV(vsum4sbs, 4, 28); @@ -6793,20 +7107,58 @@ static void glue(gen_, name)(DisasContext *ctx) \ GEN_VXRFORM1(name, name, #name, opc2, opc3) \ GEN_VXRFORM1(name##_dot, name##_, #name ".", opc2, (opc3 | (0x1 << 4))) +/* + * Support for Altivec instructions that use bit 31 (Rc) as an opcode + * bit but also use bit 21 as an actual Rc bit. In general, thse pairs + * come from different versions of the ISA, so we must also support a + * pair of flags for each instruction. + */ +#define GEN_VXRFORM_DUAL(name0, flg0, flg2_0, name1, flg1, flg2_1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0))) { \ + if (Rc21(ctx->opcode) == 0) { \ + gen_##name0(ctx); \ + } else { \ + gen_##name0##_(ctx); \ + } \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1))) { \ + if (Rc21(ctx->opcode) == 0) { \ + gen_##name1(ctx); \ + } else { \ + gen_##name1##_(ctx); \ + } \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + GEN_VXRFORM(vcmpequb, 3, 0) GEN_VXRFORM(vcmpequh, 3, 1) GEN_VXRFORM(vcmpequw, 3, 2) +GEN_VXRFORM(vcmpequd, 3, 3) GEN_VXRFORM(vcmpgtsb, 3, 12) GEN_VXRFORM(vcmpgtsh, 3, 13) GEN_VXRFORM(vcmpgtsw, 3, 14) +GEN_VXRFORM(vcmpgtsd, 3, 15) GEN_VXRFORM(vcmpgtub, 3, 8) GEN_VXRFORM(vcmpgtuh, 3, 9) GEN_VXRFORM(vcmpgtuw, 3, 10) +GEN_VXRFORM(vcmpgtud, 3, 11) GEN_VXRFORM(vcmpeqfp, 3, 3) GEN_VXRFORM(vcmpgefp, 3, 7) GEN_VXRFORM(vcmpgtfp, 3, 11) GEN_VXRFORM(vcmpbfp, 3, 15) +GEN_VXRFORM_DUAL(vcmpeqfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpequd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXRFORM_DUAL(vcmpbfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpgtsd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXRFORM_DUAL(vcmpgtfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpgtud, PPC_NONE, PPC2_ALTIVEC_207) + #define GEN_VXFORM_SIMM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ @@ -6860,8 +7212,10 @@ static void glue(gen_, name)(DisasContext *ctx) \ GEN_VXFORM_NOA(vupkhsb, 7, 8); GEN_VXFORM_NOA(vupkhsh, 7, 9); +GEN_VXFORM_NOA(vupkhsw, 7, 25); GEN_VXFORM_NOA(vupklsb, 7, 10); GEN_VXFORM_NOA(vupklsh, 7, 11); +GEN_VXFORM_NOA(vupklsw, 7, 27); GEN_VXFORM_NOA(vupkhpx, 7, 13); GEN_VXFORM_NOA(vupklpx, 7, 15); GEN_VXFORM_NOA_ENV(vrefp, 5, 4); @@ -7002,6 +7356,115 @@ GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20) GEN_VAFORM_PAIRED(vsel, vperm, 21) GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23) +GEN_VXFORM_NOA(vclzb, 1, 28) +GEN_VXFORM_NOA(vclzh, 1, 29) +GEN_VXFORM_NOA(vclzw, 1, 30) +GEN_VXFORM_NOA(vclzd, 1, 31) +GEN_VXFORM_NOA(vpopcntb, 1, 28) +GEN_VXFORM_NOA(vpopcnth, 1, 29) +GEN_VXFORM_NOA(vpopcntw, 1, 30) +GEN_VXFORM_NOA(vpopcntd, 1, 31) +GEN_VXFORM_DUAL(vclzb, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntb, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzh, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcnth, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzw, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntw, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzd, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM(vbpermq, 6, 21); +GEN_VXFORM_NOA(vgbbd, 6, 20); +GEN_VXFORM(vpmsumb, 4, 16) +GEN_VXFORM(vpmsumh, 4, 17) +GEN_VXFORM(vpmsumw, 4, 18) +GEN_VXFORM(vpmsumd, 4, 19) + +#define GEN_BCD(op) \ +static void gen_##op(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rd; \ + TCGv_i32 ps; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + \ + ps = tcg_const_i32((ctx->opcode & 0x200) != 0); \ + \ + gen_helper_##op(cpu_crf[6], rd, ra, rb, ps); \ + \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + tcg_temp_free_i32(ps); \ +} + +GEN_BCD(bcdadd) +GEN_BCD(bcdsub) + +GEN_VXFORM_DUAL(vsububm, PPC_ALTIVEC, PPC_NONE, \ + bcdadd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsububs, PPC_ALTIVEC, PPC_NONE, \ + bcdadd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsubuhm, PPC_ALTIVEC, PPC_NONE, \ + bcdsub, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsubuhs, PPC_ALTIVEC, PPC_NONE, \ + bcdsub, PPC_NONE, PPC2_ALTIVEC_207) + +static void gen_vsbox(DisasContext *ctx) +{ + TCGv_ptr ra, rd; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + ra = gen_avr_ptr(rA(ctx->opcode)); + rd = gen_avr_ptr(rD(ctx->opcode)); + gen_helper_vsbox(rd, ra); + tcg_temp_free_ptr(ra); + tcg_temp_free_ptr(rd); +} + +GEN_VXFORM(vcipher, 4, 20) +GEN_VXFORM(vcipherlast, 4, 20) +GEN_VXFORM(vncipher, 4, 21) +GEN_VXFORM(vncipherlast, 4, 21) + +GEN_VXFORM_DUAL(vcipher, PPC_NONE, PPC2_ALTIVEC_207, + vcipherlast, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vncipher, PPC_NONE, PPC2_ALTIVEC_207, + vncipherlast, PPC_NONE, PPC2_ALTIVEC_207) + +#define VSHASIGMA(op) \ +static void gen_##op(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rd; \ + TCGv_i32 st_six; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + st_six = tcg_const_i32(rB(ctx->opcode)); \ + gen_helper_##op(rd, ra, st_six); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rd); \ + tcg_temp_free_i32(st_six); \ +} + +VSHASIGMA(vshasigmaw) +VSHASIGMA(vshasigmad) + +GEN_VXFORM3(vpermxor, 22, 0xFF) +GEN_VXFORM_DUAL(vsldoi, PPC_ALTIVEC, PPC_NONE, + vpermxor, PPC_NONE, PPC2_ALTIVEC_207) + /*** VSX extension ***/ static inline TCGv_i64 cpu_vsrh(int n) @@ -7022,21 +7485,27 @@ static inline TCGv_i64 cpu_vsrl(int n) } } -static void gen_lxsdx(DisasContext *ctx) -{ - TCGv EA; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - gen_qemu_ld64(ctx, cpu_vsrh(xT(ctx->opcode)), EA); - /* NOTE: cpu_vsrl is undefined */ - tcg_temp_free(EA); +#define VSX_LOAD_SCALAR(name, operation) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##operation(ctx, cpu_vsrh(xT(ctx->opcode)), EA); \ + /* NOTE: cpu_vsrl is undefined */ \ + tcg_temp_free(EA); \ } +VSX_LOAD_SCALAR(lxsdx, ld64) +VSX_LOAD_SCALAR(lxsiwax, ld32s_i64) +VSX_LOAD_SCALAR(lxsiwzx, ld32u_i64) +VSX_LOAD_SCALAR(lxsspx, ld32fs) + static void gen_lxvd2x(DisasContext *ctx) { TCGv EA; @@ -7098,20 +7567,25 @@ static void gen_lxvw4x(DisasContext *ctx) tcg_temp_free_i64(tmp); } -static void gen_stxsdx(DisasContext *ctx) -{ - TCGv EA; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - gen_qemu_st64(ctx, cpu_vsrh(xS(ctx->opcode)), EA); - tcg_temp_free(EA); +#define VSX_STORE_SCALAR(name, operation) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##operation(ctx, cpu_vsrh(xS(ctx->opcode)), EA); \ + tcg_temp_free(EA); \ } +VSX_STORE_SCALAR(stxsdx, st64) +VSX_STORE_SCALAR(stxsiwx, st32_i64) +VSX_STORE_SCALAR(stxsspx, st32fs) + static void gen_stxvd2x(DisasContext *ctx) { TCGv EA; @@ -7156,6 +7630,57 @@ static void gen_stxvw4x(DisasContext *ctx) tcg_temp_free_i64(tmp); } +#define MV_VSRW(name, tcgop1, tcgop2, target, source) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + if (xS(ctx->opcode) < 32) { \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + } else { \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + } \ + TCGv_i64 tmp = tcg_temp_new_i64(); \ + tcg_gen_##tcgop1(tmp, source); \ + tcg_gen_##tcgop2(target, tmp); \ + tcg_temp_free_i64(tmp); \ +} + + +MV_VSRW(mfvsrwz, ext32u_i64, trunc_i64_tl, cpu_gpr[rA(ctx->opcode)], \ + cpu_vsrh(xS(ctx->opcode))) +MV_VSRW(mtvsrwa, extu_tl_i64, ext32s_i64, cpu_vsrh(xT(ctx->opcode)), \ + cpu_gpr[rA(ctx->opcode)]) +MV_VSRW(mtvsrwz, extu_tl_i64, ext32u_i64, cpu_vsrh(xT(ctx->opcode)), \ + cpu_gpr[rA(ctx->opcode)]) + +#if defined(TARGET_PPC64) +#define MV_VSRD(name, target, source) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + if (xS(ctx->opcode) < 32) { \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + } else { \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + } \ + tcg_gen_mov_i64(target, source); \ +} + +MV_VSRD(mfvsrd, cpu_gpr[rA(ctx->opcode)], cpu_vsrh(xS(ctx->opcode))) +MV_VSRD(mtvsrd, cpu_vsrh(xT(ctx->opcode)), cpu_gpr[rA(ctx->opcode)]) + +#endif + static void gen_xxpermdi(DisasContext *ctx) { if (unlikely(!ctx->vsx_enabled)) { @@ -7163,15 +7688,40 @@ static void gen_xxpermdi(DisasContext *ctx) return; } - if ((DM(ctx->opcode) & 2) == 0) { - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode))); - } else { - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode))); - } - if ((DM(ctx->opcode) & 1) == 0) { - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xB(ctx->opcode))); + if (unlikely((xT(ctx->opcode) == xA(ctx->opcode)) || + (xT(ctx->opcode) == xB(ctx->opcode)))) { + TCGv_i64 xh, xl; + + xh = tcg_temp_new_i64(); + xl = tcg_temp_new_i64(); + + if ((DM(ctx->opcode) & 2) == 0) { + tcg_gen_mov_i64(xh, cpu_vsrh(xA(ctx->opcode))); + } else { + tcg_gen_mov_i64(xh, cpu_vsrl(xA(ctx->opcode))); + } + if ((DM(ctx->opcode) & 1) == 0) { + tcg_gen_mov_i64(xl, cpu_vsrh(xB(ctx->opcode))); + } else { + tcg_gen_mov_i64(xl, cpu_vsrl(xB(ctx->opcode))); + } + + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xh); + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xl); + + tcg_temp_free_i64(xh); + tcg_temp_free_i64(xl); } else { - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xB(ctx->opcode))); + if ((DM(ctx->opcode) & 2) == 0) { + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode))); + } else { + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode))); + } + if ((DM(ctx->opcode) & 1) == 0) { + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xB(ctx->opcode))); + } else { + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xB(ctx->opcode))); + } } } @@ -7179,8 +7729,8 @@ static void gen_xxpermdi(DisasContext *ctx) #define OP_NABS 2 #define OP_NEG 3 #define OP_CPSGN 4 -#define SGN_MASK_DP 0x8000000000000000ul -#define SGN_MASK_SP 0x8000000080000000ul +#define SGN_MASK_DP 0x8000000000000000ull +#define SGN_MASK_SP 0x8000000080000000ull #define VSX_SCALAR_MOVE(name, op, sgn_mask) \ static void glue(gen_, name)(DisasContext * ctx) \ @@ -7289,6 +7839,165 @@ VSX_VECTOR_MOVE(xvnabssp, OP_NABS, SGN_MASK_SP) VSX_VECTOR_MOVE(xvnegsp, OP_NEG, SGN_MASK_SP) VSX_VECTOR_MOVE(xvcpsgnsp, OP_CPSGN, SGN_MASK_SP) +#define GEN_VSX_HELPER_2(name, op1, op2, inval, type) \ +static void gen_##name(DisasContext * ctx) \ +{ \ + TCGv_i32 opc; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + /* NIP cannot be restored if the memory exception comes from an helper */ \ + gen_update_nip(ctx, ctx->nip - 4); \ + opc = tcg_const_i32(ctx->opcode); \ + gen_helper_##name(cpu_env, opc); \ + tcg_temp_free_i32(opc); \ +} + +#define GEN_VSX_HELPER_XT_XB_ENV(name, op1, op2, inval, type) \ +static void gen_##name(DisasContext * ctx) \ +{ \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + /* NIP cannot be restored if the exception comes */ \ + /* from a helper. */ \ + gen_update_nip(ctx, ctx->nip - 4); \ + \ + gen_helper_##name(cpu_vsrh(xT(ctx->opcode)), cpu_env, \ + cpu_vsrh(xB(ctx->opcode))); \ +} + +GEN_VSX_HELPER_2(xsadddp, 0x00, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xssubdp, 0x00, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmuldp, 0x00, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsdivdp, 0x00, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsredp, 0x14, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrsqrtedp, 0x14, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xstdivdp, 0x14, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xstsqrtdp, 0x14, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaddadp, 0x04, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaddmdp, 0x04, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmsubadp, 0x04, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmsubmdp, 0x04, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmaddadp, 0x04, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmaddmdp, 0x04, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmsubadp, 0x04, 0x16, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmsubmdp, 0x04, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmindp, 0x00, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xscvdpspn, 0x16, 0x10, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvspdp, 0x12, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xscvspdpn, 0x16, 0x14, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvdpsxds, 0x10, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpsxws, 0x10, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpuxds, 0x10, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpuxws, 0x10, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvsxddp, 0x10, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvuxddp, 0x10, 0x16, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpi, 0x12, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpic, 0x16, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpim, 0x12, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpip, 0x12, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpiz, 0x12, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xsrsp, 0x12, 0x11, 0, PPC2_VSX207) + +GEN_VSX_HELPER_2(xsaddsp, 0x00, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xssubsp, 0x00, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmulsp, 0x00, 0x02, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsdivsp, 0x00, 0x03, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsresp, 0x14, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xssqrtsp, 0x16, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsrsqrtesp, 0x14, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmaddasp, 0x04, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmaddmsp, 0x04, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmsubasp, 0x04, 0x02, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmsubmsp, 0x04, 0x03, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmaddasp, 0x04, 0x10, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmaddmsp, 0x04, 0x11, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmsubasp, 0x04, 0x12, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmsubmsp, 0x04, 0x13, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207) + +GEN_VSX_HELPER_2(xvadddp, 0x00, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmuldp, 0x00, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvdivdp, 0x00, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvredp, 0x14, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsqrtdp, 0x16, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrsqrtedp, 0x14, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtdivdp, 0x14, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtsqrtdp, 0x14, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddadp, 0x04, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddmdp, 0x04, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubadp, 0x04, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubmdp, 0x04, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddadp, 0x04, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddmdp, 0x04, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubadp, 0x04, 0x1E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubmdp, 0x04, 0x1F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaxdp, 0x00, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmindp, 0x00, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpuxds, 0x10, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpuxws, 0x10, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxddp, 0x10, 0x1F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxddp, 0x10, 0x1E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxwdp, 0x10, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxwdp, 0x10, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpi, 0x12, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpic, 0x16, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpim, 0x12, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpip, 0x12, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpiz, 0x12, 0x0D, 0, PPC2_VSX) + +GEN_VSX_HELPER_2(xvaddsp, 0x00, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsubsp, 0x00, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmulsp, 0x00, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvdivsp, 0x00, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvresp, 0x14, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsqrtsp, 0x16, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrsqrtesp, 0x14, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtdivsp, 0x14, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtsqrtsp, 0x14, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddasp, 0x04, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddmsp, 0x04, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubasp, 0x04, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubmsp, 0x04, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddasp, 0x04, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddmsp, 0x04, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubasp, 0x04, 0x1A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubmsp, 0x04, 0x1B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaxsp, 0x00, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvminsp, 0x00, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspsxds, 0x10, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspsxws, 0x10, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspuxds, 0x10, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspuxws, 0x10, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxdsp, 0x10, 0x1B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxdsp, 0x10, 0x1A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxwsp, 0x10, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxwsp, 0x10, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspi, 0x12, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspic, 0x16, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX) #define VSX_LOGICAL(name, tcg_op) \ static void glue(gen_, name)(DisasContext * ctx) \ @@ -7308,6 +8017,9 @@ VSX_LOGICAL(xxlandc, tcg_gen_andc_i64) VSX_LOGICAL(xxlor, tcg_gen_or_i64) VSX_LOGICAL(xxlxor, tcg_gen_xor_i64) VSX_LOGICAL(xxlnor, tcg_gen_nor_i64) +VSX_LOGICAL(xxleqv, tcg_gen_eqv_i64) +VSX_LOGICAL(xxlnand, tcg_gen_nand_i64) +VSX_LOGICAL(xxlorc, tcg_gen_orc_i64) #define VSX_XXMRG(name, high) \ static void glue(gen_, name)(DisasContext * ctx) \ @@ -9175,6 +9887,7 @@ GEN_HANDLER_E(prtyw, 0x1F, 0x1A, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA205), GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), GEN_HANDLER_E(prtyd, 0x1F, 0x1A, 0x05, 0x0000F801, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(bpermd, 0x1F, 0x1C, 0x07, 0x00000001, PPC_NONE, PPC2_PERM_ISA206), #endif GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), @@ -9200,6 +9913,8 @@ GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT), GEN_HANDLER(fnabs, 0x3F, 0x08, 0x04, 0x001F0000, PPC_FLOAT), GEN_HANDLER(fneg, 0x3F, 0x08, 0x01, 0x001F0000, PPC_FLOAT), GEN_HANDLER_E(fcpsgn, 0x3F, 0x08, 0x00, 0x00000000, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(fmrgew, 0x3F, 0x06, 0x1E, 0x00000001, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(fmrgow, 0x3F, 0x06, 0x1A, 0x00000001, PPC_NONE, PPC2_VSX207), GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT), GEN_HANDLER(mffs, 0x3F, 0x07, 0x12, 0x001FF800, PPC_FLOAT), GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT), @@ -9219,11 +9934,17 @@ GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING), GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FFF801, PPC_MEM_EIEIO), GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), +GEN_HANDLER_E(lbarx, 0x1F, 0x14, 0x01, 0, PPC_NONE, PPC2_ATOMIC_ISA206), +GEN_HANDLER_E(lharx, 0x1F, 0x14, 0x03, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER(lwarx, 0x1F, 0x14, 0x00, 0x00000000, PPC_RES), +GEN_HANDLER_E(stbcx_, 0x1F, 0x16, 0x15, 0, PPC_NONE, PPC2_ATOMIC_ISA206), +GEN_HANDLER_E(sthcx_, 0x1F, 0x16, 0x16, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER2(stwcx_, "stwcx.", 0x1F, 0x16, 0x04, 0x00000000, PPC_RES), #if defined(TARGET_PPC64) GEN_HANDLER(ldarx, 0x1F, 0x14, 0x02, 0x00000000, PPC_64B), +GEN_HANDLER_E(lqarx, 0x1F, 0x14, 0x08, 0, PPC_NONE, PPC2_LSQ_ISA207), GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B), +GEN_HANDLER_E(stqcx_, 0x1F, 0x16, 0x05, 0, PPC_NONE, PPC2_LSQ_ISA207), #endif GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC), GEN_HANDLER(wait, 0x1F, 0x1E, 0x01, 0x03FFF801, PPC_WAIT), @@ -9231,6 +9952,7 @@ GEN_HANDLER(b, 0x12, 0xFF, 0xFF, 0x00000000, PPC_FLOW), GEN_HANDLER(bc, 0x10, 0xFF, 0xFF, 0x00000000, PPC_FLOW), GEN_HANDLER(bcctr, 0x13, 0x10, 0x10, 0x00000000, PPC_FLOW), GEN_HANDLER(bclr, 0x13, 0x10, 0x00, 0x00000000, PPC_FLOW), +GEN_HANDLER_E(bctar, 0x13, 0x10, 0x11, 0, PPC_NONE, PPC2_BCTAR_ISA207), GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER), GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW), #if defined(TARGET_PPC64) @@ -9258,8 +9980,8 @@ GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC), GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03C00001, PPC_CACHE), GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE), GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE), -GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x02000001, PPC_CACHE), -GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x02000001, PPC_CACHE), +GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x00000001, PPC_CACHE), +GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x00000001, PPC_CACHE), GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC), @@ -9395,7 +10117,6 @@ GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -GEN_HANDLER(vsldoi, 0x04, 0x16, 0xFF, 0x00000400, PPC_ALTIVEC), GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC), GEN_HANDLER2(evsel0, "evsel", 0x04, 0x1c, 0x09, 0x00000000, PPC_SPE), GEN_HANDLER2(evsel1, "evsel", 0x04, 0x1d, 0x09, 0x00000000, PPC_SPE), @@ -9427,6 +10148,10 @@ GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0), GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1), GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0), GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1), +GEN_HANDLER_E(divwe, 0x1F, 0x0B, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweo, 0x1F, 0x0B, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweu, 0x1F, 0x0B, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweuo, 0x1F, 0x0B, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), #if defined(TARGET_PPC64) #undef GEN_INT_ARITH_DIVD @@ -9437,6 +10162,11 @@ GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1), GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0), GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1), +GEN_HANDLER_E(divdeu, 0x1F, 0x09, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divdeuo, 0x1F, 0x09, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divde, 0x1F, 0x09, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divdeo, 0x1F, 0x09, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), + #undef GEN_INT_ARITH_MUL_HELPER #define GEN_INT_ARITH_MUL_HELPER(name, opc3) \ GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) @@ -9544,13 +10274,22 @@ GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT), GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT), GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT), GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT), +GEN_HANDLER_E(ftdiv, 0x3F, 0x00, 0x04, 1, PPC_NONE, PPC2_FP_TST_ISA206), +GEN_HANDLER_E(ftsqrt, 0x3F, 0x00, 0x05, 1, PPC_NONE, PPC2_FP_TST_ISA206), GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT), +GEN_HANDLER_E(fctiwu, 0x3F, 0x0E, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT), +GEN_HANDLER_E(fctiwuz, 0x3F, 0x0F, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT), #if defined(TARGET_PPC64) GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC_64B), +GEN_HANDLER_E(fcfids, 0x3B, 0x0E, 0x1A, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(fcfidu, 0x3F, 0x0E, 0x1E, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(fcfidus, 0x3B, 0x0E, 0x1E, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC_64B), +GEN_HANDLER_E(fctidu, 0x3F, 0x0E, 0x1D, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B), +GEN_HANDLER_E(fctiduz, 0x3F, 0x0F, 0x1D, 0, PPC_NONE, PPC2_FP_CVT_ISA206), #endif GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT), GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT), @@ -9642,6 +10381,7 @@ GEN_LDXF(name, ldop, 0x17, op | 0x00, type) GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT) GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT) GEN_HANDLER_E(lfiwax, 0x1f, 0x17, 0x1a, 0x00000001, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(lfiwzx, 0x1f, 0x17, 0x1b, 0x1, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_HANDLER_E(lfdp, 0x39, 0xFF, 0xFF, 0x00200003, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(lfdpx, 0x1F, 0x17, 0x18, 0x00200001, PPC_NONE, PPC2_ISA205), @@ -9754,33 +10494,61 @@ GEN_VR_STVE(wx, 0x07, 0x06), #undef GEN_VX_LOGICAL #define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) + +#undef GEN_VX_LOGICAL_207 +#define GEN_VX_LOGICAL_207(name, tcg_op, opc2, opc3) \ +GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) + GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16), GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17), GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18), GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19), GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20), +GEN_VX_LOGICAL_207(veqv, tcg_gen_eqv_i64, 2, 26), +GEN_VX_LOGICAL_207(vnand, tcg_gen_nand_i64, 2, 22), +GEN_VX_LOGICAL_207(vorc, tcg_gen_orc_i64, 2, 21), #undef GEN_VXFORM #define GEN_VXFORM(name, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) + +#undef GEN_VXFORM_207 +#define GEN_VXFORM_207(name, opc2, opc3) \ +GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) + +#undef GEN_VXFORM_DUAL +#define GEN_VXFORM_DUAL(name0, name1, opc2, opc3, type0, type1) \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, type0, type1) + +#undef GEN_VXRFORM_DUAL +#define GEN_VXRFORM_DUAL(name0, name1, opc2, opc3, tp0, tp1) \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, tp0, tp1), \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, (opc3 | 0x10), 0x00000000, tp0, tp1), + GEN_VXFORM(vaddubm, 0, 0), GEN_VXFORM(vadduhm, 0, 1), GEN_VXFORM(vadduwm, 0, 2), -GEN_VXFORM(vsububm, 0, 16), -GEN_VXFORM(vsubuhm, 0, 17), +GEN_VXFORM_207(vaddudm, 0, 3), +GEN_VXFORM_DUAL(vsububm, bcdadd, 0, 16, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vsubuhm, bcdsub, 0, 17, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vsubuwm, 0, 18), +GEN_VXFORM_207(vsubudm, 0, 19), GEN_VXFORM(vmaxub, 1, 0), GEN_VXFORM(vmaxuh, 1, 1), GEN_VXFORM(vmaxuw, 1, 2), +GEN_VXFORM_207(vmaxud, 1, 3), GEN_VXFORM(vmaxsb, 1, 4), GEN_VXFORM(vmaxsh, 1, 5), GEN_VXFORM(vmaxsw, 1, 6), +GEN_VXFORM_207(vmaxsd, 1, 7), GEN_VXFORM(vminub, 1, 8), GEN_VXFORM(vminuh, 1, 9), GEN_VXFORM(vminuw, 1, 10), +GEN_VXFORM_207(vminud, 1, 11), GEN_VXFORM(vminsb, 1, 12), GEN_VXFORM(vminsh, 1, 13), GEN_VXFORM(vminsw, 1, 14), +GEN_VXFORM_207(vminsd, 1, 15), GEN_VXFORM(vavgub, 1, 16), GEN_VXFORM(vavguh, 1, 17), GEN_VXFORM(vavguw, 1, 18), @@ -9793,23 +10561,32 @@ GEN_VXFORM(vmrghw, 6, 2), GEN_VXFORM(vmrglb, 6, 4), GEN_VXFORM(vmrglh, 6, 5), GEN_VXFORM(vmrglw, 6, 6), +GEN_VXFORM_207(vmrgew, 6, 30), +GEN_VXFORM_207(vmrgow, 6, 26), GEN_VXFORM(vmuloub, 4, 0), GEN_VXFORM(vmulouh, 4, 1), +GEN_VXFORM_DUAL(vmulouw, vmuluwm, 4, 2, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vmulosb, 4, 4), GEN_VXFORM(vmulosh, 4, 5), +GEN_VXFORM_207(vmulosw, 4, 6), GEN_VXFORM(vmuleub, 4, 8), GEN_VXFORM(vmuleuh, 4, 9), +GEN_VXFORM_207(vmuleuw, 4, 10), GEN_VXFORM(vmulesb, 4, 12), GEN_VXFORM(vmulesh, 4, 13), +GEN_VXFORM_207(vmulesw, 4, 14), GEN_VXFORM(vslb, 2, 4), GEN_VXFORM(vslh, 2, 5), GEN_VXFORM(vslw, 2, 6), +GEN_VXFORM_207(vsld, 2, 23), GEN_VXFORM(vsrb, 2, 8), GEN_VXFORM(vsrh, 2, 9), GEN_VXFORM(vsrw, 2, 10), +GEN_VXFORM_207(vsrd, 2, 27), GEN_VXFORM(vsrab, 2, 12), GEN_VXFORM(vsrah, 2, 13), GEN_VXFORM(vsraw, 2, 14), +GEN_VXFORM_207(vsrad, 2, 15), GEN_VXFORM(vslo, 6, 16), GEN_VXFORM(vsro, 6, 17), GEN_VXFORM(vaddcuw, 0, 6), @@ -9820,25 +10597,36 @@ GEN_VXFORM(vadduws, 0, 10), GEN_VXFORM(vaddsbs, 0, 12), GEN_VXFORM(vaddshs, 0, 13), GEN_VXFORM(vaddsws, 0, 14), -GEN_VXFORM(vsububs, 0, 24), -GEN_VXFORM(vsubuhs, 0, 25), +GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vsubuws, 0, 26), GEN_VXFORM(vsubsbs, 0, 28), GEN_VXFORM(vsubshs, 0, 29), GEN_VXFORM(vsubsws, 0, 30), +GEN_VXFORM_207(vadduqm, 0, 4), +GEN_VXFORM_207(vaddcuq, 0, 5), +GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_207(vsubuqm, 0, 20), +GEN_VXFORM_207(vsubcuq, 0, 21), +GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), GEN_VXFORM(vrlb, 2, 0), GEN_VXFORM(vrlh, 2, 1), GEN_VXFORM(vrlw, 2, 2), +GEN_VXFORM_207(vrld, 2, 3), GEN_VXFORM(vsl, 2, 7), GEN_VXFORM(vsr, 2, 11), GEN_VXFORM(vpkuhum, 7, 0), GEN_VXFORM(vpkuwum, 7, 1), +GEN_VXFORM_207(vpkudum, 7, 17), GEN_VXFORM(vpkuhus, 7, 2), GEN_VXFORM(vpkuwus, 7, 3), +GEN_VXFORM_207(vpkudus, 7, 19), GEN_VXFORM(vpkshus, 7, 4), GEN_VXFORM(vpkswus, 7, 5), +GEN_VXFORM_207(vpksdus, 7, 21), GEN_VXFORM(vpkshss, 7, 6), GEN_VXFORM(vpkswss, 7, 7), +GEN_VXFORM_207(vpksdss, 7, 23), GEN_VXFORM(vpkpx, 7, 12), GEN_VXFORM(vsum4ubs, 4, 24), GEN_VXFORM(vsum4sbs, 4, 28), @@ -9866,10 +10654,10 @@ GEN_VXRFORM(vcmpgtsw, 3, 14) GEN_VXRFORM(vcmpgtub, 3, 8) GEN_VXRFORM(vcmpgtuh, 3, 9) GEN_VXRFORM(vcmpgtuw, 3, 10) -GEN_VXRFORM(vcmpeqfp, 3, 3) +GEN_VXRFORM_DUAL(vcmpeqfp, vcmpequd, 3, 3, PPC_ALTIVEC, PPC_NONE) GEN_VXRFORM(vcmpgefp, 3, 7) -GEN_VXRFORM(vcmpgtfp, 3, 11) -GEN_VXRFORM(vcmpbfp, 3, 15) +GEN_VXRFORM_DUAL(vcmpgtfp, vcmpgtud, 3, 11, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM_DUAL(vcmpbfp, vcmpgtsd, 3, 15, PPC_ALTIVEC, PPC_NONE) #undef GEN_VXFORM_SIMM #define GEN_VXFORM_SIMM(name, opc2, opc3) \ @@ -9883,8 +10671,10 @@ GEN_VXFORM_SIMM(vspltisw, 6, 14), GEN_HANDLER(name, 0x04, opc2, opc3, 0x001f0000, PPC_ALTIVEC) GEN_VXFORM_NOA(vupkhsb, 7, 8), GEN_VXFORM_NOA(vupkhsh, 7, 9), +GEN_VXFORM_207(vupkhsw, 7, 25), GEN_VXFORM_NOA(vupklsb, 7, 10), GEN_VXFORM_NOA(vupklsh, 7, 11), +GEN_VXFORM_207(vupklsw, 7, 27), GEN_VXFORM_NOA(vupkhpx, 7, 13), GEN_VXFORM_NOA(vupklpx, 7, 15), GEN_VXFORM_NOA(vrefp, 5, 4), @@ -9917,15 +10707,50 @@ GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20), GEN_VAFORM_PAIRED(vsel, vperm, 21), GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23), +GEN_VXFORM_DUAL(vclzb, vpopcntb, 1, 28, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzh, vpopcnth, 1, 29, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzw, vpopcntw, 1, 30, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzd, vpopcntd, 1, 31, PPC_NONE, PPC2_ALTIVEC_207), + +GEN_VXFORM_207(vbpermq, 6, 21), +GEN_VXFORM_207(vgbbd, 6, 20), +GEN_VXFORM_207(vpmsumb, 4, 16), +GEN_VXFORM_207(vpmsumh, 4, 17), +GEN_VXFORM_207(vpmsumw, 4, 18), +GEN_VXFORM_207(vpmsumd, 4, 19), + +GEN_VXFORM_207(vsbox, 4, 23), + +GEN_VXFORM_DUAL(vcipher, vcipherlast, 4, 20, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vncipher, vncipherlast, 4, 21, PPC_NONE, PPC2_ALTIVEC_207), + +GEN_VXFORM_207(vshasigmaw, 1, 26), +GEN_VXFORM_207(vshasigmad, 1, 27), + +GEN_VXFORM_DUAL(vsldoi, vpermxor, 22, 0xFF, PPC_ALTIVEC, PPC_NONE), + GEN_HANDLER_E(lxsdx, 0x1F, 0x0C, 0x12, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(lxsiwax, 0x1F, 0x0C, 0x02, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(lxsiwzx, 0x1F, 0x0C, 0x00, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(lxsspx, 0x1F, 0x0C, 0x10, 0, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(lxvd2x, 0x1F, 0x0C, 0x1A, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(stxsiwx, 0x1F, 0xC, 0x04, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(stxsspx, 0x1F, 0xC, 0x14, 0, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrwz, 0x1F, 0x13, 0x07, 0x0000F800, PPC_NONE, PPC2_VSX207), +#if defined(TARGET_PPC64) +GEN_HANDLER_E(mfvsrd, 0x1F, 0x13, 0x01, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrd, 0x1F, 0x13, 0x05, 0x0000F800, PPC_NONE, PPC2_VSX207), +#endif + #undef GEN_XX2FORM #define GEN_XX2FORM(name, opc2, opc3, fl2) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ @@ -9938,6 +10763,17 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 0, PPC_NONE, fl2) +#undef GEN_XX3_RC_FORM +#define GEN_XX3_RC_FORM(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x10, 0, PPC_NONE, fl2) + #undef GEN_XX3FORM_DM #define GEN_XX3FORM_DM(name, opc2, opc3) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ @@ -9971,6 +10807,136 @@ GEN_XX2FORM(xvnabssp, 0x12, 0x1A, PPC2_VSX), GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX), GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX), +GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX), +GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX), +GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX), +GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX), +GEN_XX2FORM(xsredp, 0x14, 0x05, PPC2_VSX), +GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX), +GEN_XX2FORM(xsrsqrtedp, 0x14, 0x04, PPC2_VSX), +GEN_XX3FORM(xstdivdp, 0x14, 0x07, PPC2_VSX), +GEN_XX2FORM(xstsqrtdp, 0x14, 0x06, PPC2_VSX), +GEN_XX3FORM(xsmaddadp, 0x04, 0x04, PPC2_VSX), +GEN_XX3FORM(xsmaddmdp, 0x04, 0x05, PPC2_VSX), +GEN_XX3FORM(xsmsubadp, 0x04, 0x06, PPC2_VSX), +GEN_XX3FORM(xsmsubmdp, 0x04, 0x07, PPC2_VSX), +GEN_XX3FORM(xsnmaddadp, 0x04, 0x14, PPC2_VSX), +GEN_XX3FORM(xsnmaddmdp, 0x04, 0x15, PPC2_VSX), +GEN_XX3FORM(xsnmsubadp, 0x04, 0x16, PPC2_VSX), +GEN_XX3FORM(xsnmsubmdp, 0x04, 0x17, PPC2_VSX), +GEN_XX2FORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), +GEN_XX2FORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), +GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), +GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX), +GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX), +GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207), +GEN_XX2FORM(xscvspdp, 0x12, 0x14, PPC2_VSX), +GEN_XX2FORM(xscvspdpn, 0x16, 0x14, PPC2_VSX207), +GEN_XX2FORM(xscvdpsxds, 0x10, 0x15, PPC2_VSX), +GEN_XX2FORM(xscvdpsxws, 0x10, 0x05, PPC2_VSX), +GEN_XX2FORM(xscvdpuxds, 0x10, 0x14, PPC2_VSX), +GEN_XX2FORM(xscvdpuxws, 0x10, 0x04, PPC2_VSX), +GEN_XX2FORM(xscvsxddp, 0x10, 0x17, PPC2_VSX), +GEN_XX2FORM(xscvuxddp, 0x10, 0x16, PPC2_VSX), +GEN_XX2FORM(xsrdpi, 0x12, 0x04, PPC2_VSX), +GEN_XX2FORM(xsrdpic, 0x16, 0x06, PPC2_VSX), +GEN_XX2FORM(xsrdpim, 0x12, 0x07, PPC2_VSX), +GEN_XX2FORM(xsrdpip, 0x12, 0x06, PPC2_VSX), +GEN_XX2FORM(xsrdpiz, 0x12, 0x05, PPC2_VSX), + +GEN_XX3FORM(xsaddsp, 0x00, 0x00, PPC2_VSX207), +GEN_XX3FORM(xssubsp, 0x00, 0x01, PPC2_VSX207), +GEN_XX3FORM(xsmulsp, 0x00, 0x02, PPC2_VSX207), +GEN_XX3FORM(xsdivsp, 0x00, 0x03, PPC2_VSX207), +GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207), +GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207), +GEN_XX2FORM(xssqrtsp, 0x16, 0x00, PPC2_VSX207), +GEN_XX2FORM(xsrsqrtesp, 0x14, 0x00, PPC2_VSX207), +GEN_XX3FORM(xsmaddasp, 0x04, 0x00, PPC2_VSX207), +GEN_XX3FORM(xsmaddmsp, 0x04, 0x01, PPC2_VSX207), +GEN_XX3FORM(xsmsubasp, 0x04, 0x02, PPC2_VSX207), +GEN_XX3FORM(xsmsubmsp, 0x04, 0x03, PPC2_VSX207), +GEN_XX3FORM(xsnmaddasp, 0x04, 0x10, PPC2_VSX207), +GEN_XX3FORM(xsnmaddmsp, 0x04, 0x11, PPC2_VSX207), +GEN_XX3FORM(xsnmsubasp, 0x04, 0x12, PPC2_VSX207), +GEN_XX3FORM(xsnmsubmsp, 0x04, 0x13, PPC2_VSX207), +GEN_XX2FORM(xscvsxdsp, 0x10, 0x13, PPC2_VSX207), +GEN_XX2FORM(xscvuxdsp, 0x10, 0x12, PPC2_VSX207), + +GEN_XX3FORM(xvadddp, 0x00, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvsubdp, 0x00, 0x0D, PPC2_VSX), +GEN_XX3FORM(xvmuldp, 0x00, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvdivdp, 0x00, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvredp, 0x14, 0x0D, PPC2_VSX), +GEN_XX2FORM(xvsqrtdp, 0x16, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvrsqrtedp, 0x14, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvtdivdp, 0x14, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvtsqrtdp, 0x14, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvmaddadp, 0x04, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvmaddmdp, 0x04, 0x0D, PPC2_VSX), +GEN_XX3FORM(xvmsubadp, 0x04, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvmsubmdp, 0x04, 0x0F, PPC2_VSX), +GEN_XX3FORM(xvnmaddadp, 0x04, 0x1C, PPC2_VSX), +GEN_XX3FORM(xvnmaddmdp, 0x04, 0x1D, PPC2_VSX), +GEN_XX3FORM(xvnmsubadp, 0x04, 0x1E, PPC2_VSX), +GEN_XX3FORM(xvnmsubmdp, 0x04, 0x1F, PPC2_VSX), +GEN_XX3FORM(xvmaxdp, 0x00, 0x1C, PPC2_VSX), +GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX), +GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX), +GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX), +GEN_XX2FORM(xvcvdpuxds, 0x10, 0x1C, PPC2_VSX), +GEN_XX2FORM(xvcvdpuxws, 0x10, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvcvsxddp, 0x10, 0x1F, PPC2_VSX), +GEN_XX2FORM(xvcvuxddp, 0x10, 0x1E, PPC2_VSX), +GEN_XX2FORM(xvcvsxwdp, 0x10, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvcvuxwdp, 0x10, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpi, 0x12, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvrdpic, 0x16, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpim, 0x12, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvrdpip, 0x12, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpiz, 0x12, 0x0D, PPC2_VSX), + +GEN_XX3FORM(xvaddsp, 0x00, 0x08, PPC2_VSX), +GEN_XX3FORM(xvsubsp, 0x00, 0x09, PPC2_VSX), +GEN_XX3FORM(xvmulsp, 0x00, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvdivsp, 0x00, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvresp, 0x14, 0x09, PPC2_VSX), +GEN_XX2FORM(xvsqrtsp, 0x16, 0x08, PPC2_VSX), +GEN_XX2FORM(xvrsqrtesp, 0x14, 0x08, PPC2_VSX), +GEN_XX3FORM(xvtdivsp, 0x14, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvtsqrtsp, 0x14, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvmaddasp, 0x04, 0x08, PPC2_VSX), +GEN_XX3FORM(xvmaddmsp, 0x04, 0x09, PPC2_VSX), +GEN_XX3FORM(xvmsubasp, 0x04, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvmsubmsp, 0x04, 0x0B, PPC2_VSX), +GEN_XX3FORM(xvnmaddasp, 0x04, 0x18, PPC2_VSX), +GEN_XX3FORM(xvnmaddmsp, 0x04, 0x19, PPC2_VSX), +GEN_XX3FORM(xvnmsubasp, 0x04, 0x1A, PPC2_VSX), +GEN_XX3FORM(xvnmsubmsp, 0x04, 0x1B, PPC2_VSX), +GEN_XX3FORM(xvmaxsp, 0x00, 0x18, PPC2_VSX), +GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX), +GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX), +GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX), +GEN_XX2FORM(xvcvspuxds, 0x10, 0x18, PPC2_VSX), +GEN_XX2FORM(xvcvspuxws, 0x10, 0x08, PPC2_VSX), +GEN_XX2FORM(xvcvsxdsp, 0x10, 0x1B, PPC2_VSX), +GEN_XX2FORM(xvcvuxdsp, 0x10, 0x1A, PPC2_VSX), +GEN_XX2FORM(xvcvsxwsp, 0x10, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvcvuxwsp, 0x10, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspi, 0x12, 0x08, PPC2_VSX), +GEN_XX2FORM(xvrspic, 0x16, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspim, 0x12, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvrspip, 0x12, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspiz, 0x12, 0x09, PPC2_VSX), + #undef VSX_LOGICAL #define VSX_LOGICAL(name, opc2, opc3, fl2) \ GEN_XX3FORM(name, opc2, opc3, fl2) @@ -9980,6 +10946,9 @@ VSX_LOGICAL(xxlandc, 0x8, 0x11, PPC2_VSX), VSX_LOGICAL(xxlor, 0x8, 0x12, PPC2_VSX), VSX_LOGICAL(xxlxor, 0x8, 0x13, PPC2_VSX), VSX_LOGICAL(xxlnor, 0x8, 0x14, PPC2_VSX), +VSX_LOGICAL(xxleqv, 0x8, 0x17, PPC2_VSX207), +VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207), +VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207), GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), GEN_XX2FORM(xxspltw, 0x08, 0x0A, PPC2_VSX), @@ -10260,8 +11229,13 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, case POWERPC_MMU_SOFT_74xx: #if defined(TARGET_PPC64) case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06a: + case POWERPC_MMU_2_06d: #endif - cpu_fprintf(f, " SDR1 " TARGET_FMT_lx "\n", env->spr[SPR_SDR1]); + cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " DAR " TARGET_FMT_lx + " DSISR " TARGET_FMT_lx "\n", env->spr[SPR_SDR1], + env->spr[SPR_DAR], env->spr[SPR_DSISR]); break; case POWERPC_MMU_BOOKE206: cpu_fprintf(f, " MAS0 " TARGET_FMT_lx " MAS1 " TARGET_FMT_lx diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 445c3606fe..3eafbb0d1a 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -628,6 +628,9 @@ static inline void _spr_register(CPUPPCState *env, int num, spr->oea_read = oea_read; spr->oea_write = oea_write; #endif +#if defined(CONFIG_KVM) + spr->one_reg_id = one_reg_id, +#endif env->spr[num] = initial_value; } @@ -1064,7 +1067,7 @@ static void gen_spr_amr (CPUPPCState *env) spr_register_kvm(env, SPR_AMR, "AMR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, - KVM_REG_PPC_AMR, 0xffffffffffffffffULL); + KVM_REG_PPC_AMR, 0); spr_register_kvm(env, SPR_UAMOR, "UAMOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -2578,7 +2581,6 @@ static void gen_spr_8xx (CPUPPCState *env) * HRMOR => SPR 313 (Power 2.04 hypv) * HSRR0 => SPR 314 (Power 2.04 hypv) * HSRR1 => SPR 315 (Power 2.04 hypv) - * LPCR => SPR 316 (970) * LPIDR => SPR 317 (970) * EPR => SPR 702 (Power 2.04 emb) * perf => 768-783 (Power 2.04) @@ -4720,7 +4722,7 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) PPC_FLOAT_STFIWX | PPC_WAIT | PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC | PPC_64B | PPC_POPCNTB | PPC_POPCNTWD; - pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL; + pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL | PPC2_PERM_ISA206; pcc->msr_mask = 0x000000009402FB36ULL; pcc->mmu_model = POWERPC_MMU_BOOKE206; pcc->excp_model = POWERPC_EXCP_BOOKE; @@ -6644,33 +6646,13 @@ static void init_proc_970 (CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ spr_register(env, SPR_970_HID5, "HID5", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, POWERPC970_HID5_INIT); - /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); /* Memory management */ /* XXX: not correct */ gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, @@ -6744,44 +6726,24 @@ static void init_proc_970FX (CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ spr_register(env, SPR_970_HID5, "HID5", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, POWERPC970_HID5_INIT); - /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); /* Memory management */ /* XXX: not correct */ gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, 0x00000000); spr_register(env, SPR_CTRL, "SPR_CTRL", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic, 0x00000000); spr_register(env, SPR_UCTRL, "SPR_UCTRL", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, SPR_NOACCESS, 0x00000000); spr_register(env, SPR_VRSAVE, "SPR_VRSAVE", &spr_read_generic, &spr_write_generic, @@ -6830,106 +6792,6 @@ POWERPC_FAMILY(970FX)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static int check_pow_970GX (CPUPPCState *env) -{ - if (env->spr[SPR_HID0] & 0x00600000) - return 1; - - return 0; -} - -static void init_proc_970GX (CPUPPCState *env) -{ - gen_spr_ne_601(env); - gen_spr_7xx(env); - /* Time base */ - gen_tbl(env); - /* Hardware implementation registers */ - /* XXX : not implemented */ - spr_register(env, SPR_HID0, "HID0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_clear, - 0x60000000); - /* XXX : not implemented */ - spr_register(env, SPR_HID1, "HID1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_970_HID5, "HID5", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - POWERPC970_HID5_INIT); - /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); - /* Memory management */ - /* XXX: not correct */ - gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ - spr_register(env, SPR_HIOR, "SPR_HIOR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_hior, &spr_write_hior, - 0x00000000); -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 32; -#endif - init_excp_970(env); - env->dcache_line_size = 128; - env->icache_line_size = 128; - /* Allocate hardware IRQ controller */ - ppc970_irq_init(env); - /* Can't find information on what this should be on reset. This - * value is the one used by 74xx processors. */ - vscr_init(env, 0x00010000); -} - -POWERPC_FAMILY(970GX)(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - dc->desc = "PowerPC 970 GX"; - pcc->init_proc = init_proc_970GX; - pcc->check_pow = check_pow_970GX; - pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | - PPC_FLOAT_STFIWX | - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | - PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_ALTIVEC | - PPC_SEGMENT_64B | PPC_SLBI; - pcc->msr_mask = 0x800000000204FF36ULL; - pcc->mmu_model = POWERPC_MMU_64B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; -#endif - pcc->excp_model = POWERPC_EXCP_970; - pcc->bus_model = PPC_FLAGS_INPUT_970; - pcc->bfd_mach = bfd_mach_ppc64; - pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | - POWERPC_FLAG_BUS_CLK; -} - static int check_pow_970MP (CPUPPCState *env) { if (env->spr[SPR_HID0] & 0x01C00000) @@ -6956,37 +6818,23 @@ static void init_proc_970MP (CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ spr_register(env, SPR_970_HID5, "HID5", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, POWERPC970_HID5_INIT); /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); /* Memory management */ /* XXX: not correct */ gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, 0x00000000); + /* Logical partitionning */ + spr_register_kvm(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_LPCR, 0x00000000); #if !defined(CONFIG_USER_ONLY) env->slb_nr = 32; #endif @@ -7048,49 +6896,34 @@ static void init_proc_power5plus(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ spr_register(env, SPR_970_HID5, "HID5", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, POWERPC970_HID5_INIT); - /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); /* Memory management */ /* XXX: not correct */ gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, 0x00000000); spr_register(env, SPR_CTRL, "SPR_CTRL", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic, 0x00000000); spr_register(env, SPR_UCTRL, "SPR_UCTRL", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, SPR_NOACCESS, 0x00000000); spr_register(env, SPR_VRSAVE, "SPR_VRSAVE", &spr_read_generic, &spr_write_generic, &spr_read_generic, &spr_write_generic, 0x00000000); + /* Logical partitionning */ + spr_register_kvm(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_LPCR, 0x00000000); #if !defined(CONFIG_USER_ONLY) env->slb_nr = 64; #endif @@ -7177,21 +7010,15 @@ static void init_proc_POWER7 (CPUPPCState *env) &spr_read_generic, &spr_write_generic, KVM_REG_PPC_PMC6, 0x00000000); #endif /* !CONFIG_USER_ONLY */ - /* Memory management */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ gen_spr_amr(env); /* XXX : not implemented */ spr_register(env, SPR_CTRL, "SPR_CTRLT", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic, 0x80800000); spr_register(env, SPR_UCTRL, "SPR_CTRLF", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, SPR_NOACCESS, 0x80800000); spr_register(env, SPR_VRSAVE, "SPR_VRSAVE", &spr_read_generic, &spr_write_generic, @@ -7201,6 +7028,11 @@ static void init_proc_POWER7 (CPUPPCState *env) &spr_read_generic, &spr_write_generic, &spr_read_generic, &spr_write_generic, 0x00000000); + /* Logical partitionning */ + spr_register_kvm(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_LPCR, 0x00000000); #if !defined(CONFIG_USER_ONLY) env->slb_nr = 32; #endif @@ -7229,14 +7061,19 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; - pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205; + pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206; pcc->msr_mask = 0x800000000284FF37ULL; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) @@ -7267,14 +7104,19 @@ POWERPC_FAMILY(POWER7P)(ObjectClass *oc, void *data) pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; - pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205; + pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206; pcc->msr_mask = 0x800000000204FF37ULL; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) @@ -7291,6 +7133,18 @@ POWERPC_FAMILY(POWER7P)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } +static void init_proc_POWER8(CPUPPCState *env) +{ + /* inherit P7 */ + init_proc_POWER7(env); + + /* P8 supports the TAR */ + spr_register(env, SPR_TAR, "TAR", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -7300,19 +7154,25 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) dc->desc = "POWER8"; pcc->pvr = CPU_POWERPC_POWER8_BASE; pcc->pvr_mask = CPU_POWERPC_POWER8_MASK; - pcc->init_proc = init_proc_POWER7; + pcc->init_proc = init_proc_POWER8; pcc->check_pow = check_pow_nocheck; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_ALTIVEC | + PPC_64B | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; - pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX; + pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | + PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207; pcc->msr_mask = 0x800000000284FF36ULL; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) @@ -7987,14 +7847,12 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) max_smt, kvm_enabled() ? "KVM" : "TCG"); return; } + + cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt + + (cs->cpu_index % smp_threads); #endif - if (kvm_enabled()) { - if (kvmppc_fixup_cpu(cpu) != 0) { - error_setg(errp, "Unable to virtualize selected CPU with KVM"); - return; - } - } else if (tcg_enabled()) { + if (tcg_enabled()) { if (ppc_fixup_cpu(cpu) != 0) { error_setg(errp, "Unable to emulate selected CPU with TCG"); return; @@ -8149,9 +8007,10 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) } printf("PowerPC %-12s : PVR %08x MSR %016" PRIx64 "\n" " MMU model : %s\n", - pcc->name, pcc->pvr, pcc->msr_mask, mmu_model); + object_class_get_name(OBJECT_CLASS(pcc)), + pcc->pvr, pcc->msr_mask, mmu_model); #if !defined(CONFIG_USER_ONLY) - if (env->tlb != NULL) { + if (env->tlb.tlb6) { printf(" %d %s TLB in %d ways\n", env->nb_tlb, env->id_tlbs ? "splitted" : "merged", env->nb_ways); @@ -8598,6 +8457,7 @@ static void ppc_cpu_initfn(Object *obj) cs->env_ptr = env; cpu_exec_init(env); + cpu->cpu_dt_id = cs->cpu_index; env->msr_mask = pcc->msr_mask; env->mmu_model = pcc->mmu_model; diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h index 613da49b3b..5bbc67d15e 100644 --- a/target-s390x/ioinst.h +++ b/target-s390x/ioinst.h @@ -212,6 +212,8 @@ typedef struct IOIntCode { #define IO_INT_WORD_ISC(_int_word) ((_int_word & 0x38000000) >> 24) #define ISC_TO_ISC_BITS(_isc) ((0x80 >> _isc) << 24) +#define IO_INT_WORD_AI 0x80000000 + int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, int *schid); void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1); diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 11feda9eb9..56b9af7505 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -891,8 +891,12 @@ void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, { uint32_t type; - type = ((subchannel_id & 0xff00) << 24) | - ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); + if (io_int_word & IO_INT_WORD_AI) { + type = KVM_S390_INT_IO(1, 0, 0, 0); + } else { + type = ((subchannel_id & 0xff00) << 24) | + ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); + } kvm_s390_interrupt_internal(cpu, type, ((uint32_t)subchannel_id << 16) | subchannel_nr, ((uint64_t)io_int_parm << 32) | io_int_word, 1); diff --git a/trace-events b/trace-events index d86f98cb31..aec420292c 100644 --- a/trace-events +++ b/trace-events @@ -1020,6 +1020,15 @@ gd_switch(int width, int height) "width=%d, height=%d" gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)" +# ui/input.c +input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%d, down %d" +input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d" +input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d" +input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d" +input_event_abs(int conidx, const char *axis, int value) "con %d, axis %s, value 0x%x" +input_event_sync(void) "" +input_mouse_mode(int absolute) "absolute %d" + # hw/display/vmware_vga.c vmware_value_read(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_value_write(uint32_t index, uint32_t value) "index %d, value 0x%x" @@ -1136,6 +1145,7 @@ xics_ics_eoi(int nr) "ics_eoi: irq %#x" # hw/ppc/spapr_iommu.c spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 +spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64 spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, unsigned pgsize) "liobn=%"PRIx64" 0x%"PRIx64" -> 0x%"PRIx64" perm=%u mask=%x" spapr_iommu_new_table(uint64_t liobn, void *tcet, void *table, int fd) "liobn=%"PRIx64" tcet=%p table=%p fd=%d" @@ -1157,6 +1167,7 @@ css_chpid_add(uint8_t cssid, uint8_t chpid, uint8_t type) "CSS: add chpid %x.%02 css_new_image(uint8_t cssid, const char *default_cssid) "CSS: add css image %02x %s" css_assign_subch(const char *do_assign, uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno) "CSS: %s %x.%x.%04x (devno %04x)" css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, const char *conditional) "CSS: I/O interrupt on sch %x.%x.%04x (intparm %08x, isc %x) %s" +css_adapter_interrupt(uint8_t isc) "CSS: adapter I/O interrupt (isc %x)" # hw/s390x/virtio-ccw.c virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x" @@ -1176,6 +1187,8 @@ kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p" kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p" kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d" kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p" +kvm_failed_spr_set(int str, const char *msg) "Warning: Unable to set SPR %d to KVM: %s" +kvm_failed_spr_get(int str, const char *msg) "Warning: Unable to retrieve SPR %d from KVM: %s" # memory.c memory_region_ops_read(void *mr, uint64_t addr, uint64_t value, unsigned size) "mr %p addr %#"PRIx64" value %#"PRIx64" size %u" diff --git a/ui/Makefile.objs b/ui/Makefile.objs index f33be47576..6f2294efda 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -7,14 +7,14 @@ vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o vnc-obj-y += vnc-jobs.o -common-obj-y += keymaps.o console.o cursor.o input.o qemu-pixman.o +common-obj-y += keymaps.o console.o cursor.o input.o input-legacy.o qemu-pixman.o common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o -common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o +common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o sdl2.o common-obj-$(CONFIG_COCOA) += cocoa.o common-obj-$(CONFIG_CURSES) += curses.o common-obj-$(CONFIG_VNC) += $(vnc-obj-y) common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o -$(obj)/sdl.o $(obj)/sdl_zoom.o: QEMU_CFLAGS += $(SDL_CFLAGS) +$(obj)/sdl.o $(obj)/sdl_zoom.o $(obj)/sdl2.o: QEMU_CFLAGS += $(SDL_CFLAGS) $(obj)/gtk.o: QEMU_CFLAGS += $(GTK_CFLAGS) $(VTE_CFLAGS) diff --git a/ui/cocoa.m b/ui/cocoa.m index 866177770a..f20fd1ffa2 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -27,6 +27,7 @@ #include "qemu-common.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #ifndef MAC_OS_X_VERSION_10_4 @@ -49,14 +50,6 @@ #endif #define cgrect(nsrect) (*(CGRect *)&(nsrect)) -#define COCOA_MOUSE_EVENT \ - if (isTabletEnabled) { \ - kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \ - } else if (isMouseGrabbed) { \ - kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \ - } else { \ - [NSApp sendEvent:event]; \ - } typedef struct { int width; @@ -67,6 +60,7 @@ typedef struct { NSWindow *normalWindow; static DisplayChangeListener *dcl; +static int last_buttons; int gArgc; char **gArgv; @@ -501,6 +495,7 @@ QemuCocoaView *cocoaView; int buttons = 0; int keycode; + bool mouse_event = false; NSPoint p = [event locationInWindow]; switch ([event type]) { @@ -514,16 +509,14 @@ QemuCocoaView *cocoaView; if (keycode) { if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup - kbd_put_keycode(keycode); - kbd_put_keycode(keycode | 0x80); + qemu_input_event_send_key_number(dcl->con, keycode, true); + qemu_input_event_send_key_number(dcl->con, keycode, false); } else if (qemu_console_is_graphic(NULL)) { - if (keycode & 0x80) - kbd_put_keycode(0xe0); if (modifiers_state[keycode] == 0) { // keydown - kbd_put_keycode(keycode & 0x7f); + qemu_input_event_send_key_number(dcl->con, keycode, true); modifiers_state[keycode] = 1; } else { // keyup - kbd_put_keycode(keycode | 0x80); + qemu_input_event_send_key_number(dcl->con, keycode, false); modifiers_state[keycode] = 0; } } @@ -557,9 +550,7 @@ QemuCocoaView *cocoaView; // handle keys for graphic console } else if (qemu_console_is_graphic(NULL)) { - if (keycode & 0x80) //check bit for e0 in front - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front + qemu_input_event_send_key_number(dcl->con, keycode, true); // handlekeys for Monitor } else { @@ -607,9 +598,7 @@ QemuCocoaView *cocoaView; } if (qemu_console_is_graphic(NULL)) { - if (keycode & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key + qemu_input_event_send_key_number(dcl->con, keycode, false); } break; case NSMouseMoved: @@ -626,7 +615,7 @@ QemuCocoaView *cocoaView; } } } - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSLeftMouseDown: if ([event modifierFlags] & NSCommandKeyMask) { @@ -634,15 +623,15 @@ QemuCocoaView *cocoaView; } else { buttons |= MOUSE_EVENT_LBUTTON; } - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSRightMouseDown: buttons |= MOUSE_EVENT_RBUTTON; - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSOtherMouseDown: buttons |= MOUSE_EVENT_MBUTTON; - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSLeftMouseDragged: if ([event modifierFlags] & NSCommandKeyMask) { @@ -650,19 +639,19 @@ QemuCocoaView *cocoaView; } else { buttons |= MOUSE_EVENT_LBUTTON; } - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSRightMouseDragged: buttons |= MOUSE_EVENT_RBUTTON; - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSOtherMouseDragged: buttons |= MOUSE_EVENT_MBUTTON; - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSLeftMouseUp: if (isTabletEnabled) { - COCOA_MOUSE_EVENT + mouse_event = true; } else if (!isMouseGrabbed) { if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) { [self grabMouse]; @@ -670,18 +659,20 @@ QemuCocoaView *cocoaView; [NSApp sendEvent:event]; } } else { - COCOA_MOUSE_EVENT + mouse_event = true; } break; case NSRightMouseUp: - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSOtherMouseUp: - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSScrollWheel: if (isTabletEnabled || isMouseGrabbed) { - kbd_mouse_event(0, 0, -[event deltaY], 0); + buttons |= ([event deltaY] < 0) ? + MOUSE_EVENT_WHEELUP : MOUSE_EVENT_WHEELDN; + mouse_event = true; } else { [NSApp sendEvent:event]; } @@ -689,6 +680,30 @@ QemuCocoaView *cocoaView; default: [NSApp sendEvent:event]; } + + if (mouse_event) { + if (last_buttons != buttons) { + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, + [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, + [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, + [INPUT_BUTTON_WHEEL_UP] = MOUSE_EVENT_WHEELUP, + [INPUT_BUTTON_WHEEL_DOWN] = MOUSE_EVENT_WHEELDN, + }; + qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons); + last_buttons = buttons; + } + if (isTabletEnabled) { + qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width); + qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, p.y, screen.height); + } else if (isMouseGrabbed) { + qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]); + qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]); + } else { + [NSApp sendEvent:event]; + } + qemu_input_event_sync(); + } } - (void) grabMouse @@ -1023,7 +1038,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl) COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); - if (kbd_mouse_is_absolute()) { + if (qemu_input_is_absolute()) { if (![cocoaView isAbsoluteEnabled]) { if ([cocoaView isMouseGrabbed]) { [cocoaView ungrabMouse]; diff --git a/ui/console.c b/ui/console.c index 502e1600ab..4df251d579 100644 --- a/ui/console.c +++ b/ui/console.c @@ -124,6 +124,8 @@ struct QemuConsole { /* Graphic console state. */ Object *device; + uint32_t head; + QemuUIInfo ui_info; const GraphicHwOps *hw_ops; void *hw; @@ -1179,6 +1181,8 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type) s = QEMU_CONSOLE(obj); object_property_add_link(obj, "device", TYPE_DEVICE, (Object **)&s->device, &local_err); + object_property_add_uint32_ptr(obj, "head", + &s->head, &local_err); if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && (console_type == GRAPHIC_CONSOLE))) { @@ -1344,6 +1348,16 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl) gui_setup_refresh(ds); } +int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info) +{ + assert(con != NULL); + con->ui_info = *info; + if (con->hw_ops->ui_info) { + return con->hw_ops->ui_info(con->hw, con->head, info); + } + return -1; +} + void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) { DisplayState *s = con->ds; @@ -1569,7 +1583,7 @@ DisplayState *init_displaystate(void) return display_state; } -QemuConsole *graphic_console_init(DeviceState *dev, +QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, const GraphicHwOps *hw_ops, void *opaque) { @@ -1587,6 +1601,8 @@ QemuConsole *graphic_console_init(DeviceState *dev, if (dev) { object_property_set_link(OBJECT(s), OBJECT(dev), "device", &local_err); + object_property_set_int(OBJECT(s), head, + "head", &local_err); } s->surface = qemu_create_displaysurface(width, height); @@ -1601,10 +1617,11 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index) return consoles[index]; } -QemuConsole *qemu_console_lookup_by_device(DeviceState *dev) +QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) { Error *local_err = NULL; Object *obj; + uint32_t h; int i; for (i = 0; i < nb_consoles; i++) { @@ -1613,9 +1630,15 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev) } obj = object_property_get_link(OBJECT(consoles[i]), "device", &local_err); - if (DEVICE(obj) == dev) { - return consoles[i]; + if (DEVICE(obj) != dev) { + continue; } + h = object_property_get_int(OBJECT(consoles[i]), + "head", &local_err); + if (h != head) { + continue; + } + return consoles[i]; } return NULL; } @@ -1641,6 +1664,44 @@ bool qemu_console_is_fixedsize(QemuConsole *con) return con && (con->console_type != TEXT_CONSOLE); } +int qemu_console_get_index(QemuConsole *con) +{ + if (con == NULL) { + con = active_console; + } + return con ? con->index : -1; +} + +uint32_t qemu_console_get_head(QemuConsole *con) +{ + if (con == NULL) { + con = active_console; + } + return con ? con->head : -1; +} + +QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con) +{ + assert(con != NULL); + return &con->ui_info; +} + +int qemu_console_get_width(QemuConsole *con, int fallback) +{ + if (con == NULL) { + con = active_console; + } + return con ? surface_width(con->surface) : fallback; +} + +int qemu_console_get_height(QemuConsole *con, int fallback) +{ + if (con == NULL) { + con = active_console; + } + return con ? surface_height(con->surface) : fallback; +} + static void text_console_set_echo(CharDriverState *chr, bool echo) { QemuConsole *s = chr->opaque; diff --git a/ui/curses.c b/ui/curses.c index dbc3d5ec73..b044790e43 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -30,6 +30,7 @@ #include "qemu-common.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #define FONT_HEIGHT 16 @@ -274,32 +275,34 @@ static void curses_refresh(DisplayChangeListener *dcl) if (qemu_console_is_graphic(NULL)) { /* since terminals don't know about key press and release * events, we need to emit both for each key received */ - if (keycode & SHIFT) - kbd_put_keycode(SHIFT_CODE); - if (keycode & CNTRL) - kbd_put_keycode(CNTRL_CODE); - if (keycode & ALT) - kbd_put_keycode(ALT_CODE); + if (keycode & SHIFT) { + qemu_input_event_send_key_number(NULL, SHIFT_CODE, true); + } + if (keycode & CNTRL) { + qemu_input_event_send_key_number(NULL, CNTRL_CODE, true); + } + if (keycode & ALT) { + qemu_input_event_send_key_number(NULL, ALT_CODE, true); + } if (keycode & ALTGR) { - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(ALT_CODE); + qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true); } - if (keycode & GREY) - kbd_put_keycode(GREY_CODE); - kbd_put_keycode(keycode & KEY_MASK); - if (keycode & GREY) - kbd_put_keycode(GREY_CODE); - kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE); + + qemu_input_event_send_key_number(NULL, keycode, true); + qemu_input_event_send_key_number(NULL, keycode, false); + if (keycode & ALTGR) { - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(ALT_CODE | KEY_RELEASE); + qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false); + } + if (keycode & ALT) { + qemu_input_event_send_key_number(NULL, ALT_CODE, false); + } + if (keycode & CNTRL) { + qemu_input_event_send_key_number(NULL, CNTRL_CODE, false); + } + if (keycode & SHIFT) { + qemu_input_event_send_key_number(NULL, SHIFT_CODE, false); } - if (keycode & ALT) - kbd_put_keycode(ALT_CODE | KEY_RELEASE); - if (keycode & CNTRL) - kbd_put_keycode(CNTRL_CODE | KEY_RELEASE); - if (keycode & SHIFT) - kbd_put_keycode(SHIFT_CODE | KEY_RELEASE); } else { keysym = curses2qemu[chr]; if (keysym == -1) @@ -59,6 +59,7 @@ #include "trace.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #include "qmp-commands.h" #include "x_keymap.h" @@ -193,7 +194,7 @@ static void gd_update_cursor(GtkDisplayState *s, gboolean override) on_vga = gd_on_vga(s); if ((override || on_vga) && - (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) { + (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s))) { gdk_window_set_cursor(window, s->null_cursor); } else { gdk_window_set_cursor(window, NULL); @@ -280,10 +281,7 @@ static void gtk_release_modifiers(GtkDisplayState *s) if (!s->modifier_pressed[i]) { continue; } - if (keycode & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); - } - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(s->dcl.con, keycode, false); s->modifier_pressed[i] = false; } } @@ -582,7 +580,6 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, void *opaque) { GtkDisplayState *s = opaque; - int dx, dy; int x, y; int mx, my; int fbh, fbw; @@ -610,25 +607,21 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, return TRUE; } - if (kbd_mouse_is_absolute()) { - dx = x * 0x7FFF / (surface_width(s->ds) - 1); - dy = y * 0x7FFF / (surface_height(s->ds) - 1); - } else if (s->last_x == -1 || s->last_y == -1) { - dx = 0; - dy = 0; - } else { - dx = x - s->last_x; - dy = y - s->last_y; + if (qemu_input_is_absolute()) { + qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_X, x, + surface_width(s->ds)); + qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_Y, y, + surface_height(s->ds)); + qemu_input_event_sync(); + } else if (s->last_x != -1 && s->last_y != -1 && gd_is_grab_active(s)) { + qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_X, x - s->last_x); + qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_Y, y - s->last_y); + qemu_input_event_sync(); } - s->last_x = x; s->last_y = y; - if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) { - kbd_mouse_event(dx, dy, 0, s->button_mask); - } - - if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) { + if (!qemu_input_is_absolute() && gd_is_grab_active(s)) { GdkScreen *screen = gtk_widget_get_screen(s->drawing_area); int x = (int)motion->x_root; int y = (int)motion->y_root; @@ -673,35 +666,20 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, void *opaque) { GtkDisplayState *s = opaque; - int dx, dy; - int n; + InputButton btn; if (button->button == 1) { - n = 0x01; + btn = INPUT_BUTTON_LEFT; } else if (button->button == 2) { - n = 0x04; + btn = INPUT_BUTTON_MIDDLE; } else if (button->button == 3) { - n = 0x02; + btn = INPUT_BUTTON_RIGHT; } else { - n = 0x00; - } - - if (button->type == GDK_BUTTON_PRESS) { - s->button_mask |= n; - } else if (button->type == GDK_BUTTON_RELEASE) { - s->button_mask &= ~n; - } - - if (kbd_mouse_is_absolute()) { - dx = s->last_x * 0x7FFF / (surface_width(s->ds) - 1); - dy = s->last_y * 0x7FFF / (surface_height(s->ds) - 1); - } else { - dx = 0; - dy = 0; + return TRUE; } - kbd_mouse_event(dx, dy, 0, s->button_mask); - + qemu_input_queue_btn(s->dcl.con, btn, button->type == GDK_BUTTON_PRESS); + qemu_input_event_sync(); return TRUE; } @@ -745,17 +723,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) } } - if (qemu_keycode & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); - } - - if (key->type == GDK_KEY_PRESS) { - kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK); - } else if (key->type == GDK_KEY_RELEASE) { - kbd_put_keycode(qemu_keycode | SCANCODE_UP); - } else { - g_assert_not_reached(); - } + qemu_input_event_send_key_number(s->dcl.con, qemu_keycode, + key->type == GDK_KEY_PRESS); return TRUE; } diff --git a/ui/input-legacy.c b/ui/input-legacy.c new file mode 100644 index 0000000000..f38984b192 --- /dev/null +++ b/ui/input-legacy.c @@ -0,0 +1,453 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysemu/sysemu.h" +#include "monitor/monitor.h" +#include "ui/console.h" +#include "qapi/error.h" +#include "qmp-commands.h" +#include "qapi-types.h" +#include "ui/keymaps.h" +#include "ui/input.h" + +struct QEMUPutMouseEntry { + QEMUPutMouseEvent *qemu_put_mouse_event; + void *qemu_put_mouse_event_opaque; + int qemu_put_mouse_event_absolute; + + /* new input core */ + QemuInputHandler h; + QemuInputHandlerState *s; + int axis[INPUT_AXIS_MAX]; + int buttons; +}; + +struct QEMUPutKbdEntry { + QEMUPutKBDEvent *put_kbd; + void *opaque; + QemuInputHandlerState *s; +}; + +struct QEMUPutLEDEntry { + QEMUPutLEDEvent *put_led; + void *opaque; + QTAILQ_ENTRY(QEMUPutLEDEntry) next; +}; + +static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = + QTAILQ_HEAD_INITIALIZER(led_handlers); +static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = + QTAILQ_HEAD_INITIALIZER(mouse_handlers); + +static const int key_defs[] = { + [Q_KEY_CODE_SHIFT] = 0x2a, + [Q_KEY_CODE_SHIFT_R] = 0x36, + + [Q_KEY_CODE_ALT] = 0x38, + [Q_KEY_CODE_ALT_R] = 0xb8, + [Q_KEY_CODE_ALTGR] = 0x64, + [Q_KEY_CODE_ALTGR_R] = 0xe4, + [Q_KEY_CODE_CTRL] = 0x1d, + [Q_KEY_CODE_CTRL_R] = 0x9d, + + [Q_KEY_CODE_MENU] = 0xdd, + + [Q_KEY_CODE_ESC] = 0x01, + + [Q_KEY_CODE_1] = 0x02, + [Q_KEY_CODE_2] = 0x03, + [Q_KEY_CODE_3] = 0x04, + [Q_KEY_CODE_4] = 0x05, + [Q_KEY_CODE_5] = 0x06, + [Q_KEY_CODE_6] = 0x07, + [Q_KEY_CODE_7] = 0x08, + [Q_KEY_CODE_8] = 0x09, + [Q_KEY_CODE_9] = 0x0a, + [Q_KEY_CODE_0] = 0x0b, + [Q_KEY_CODE_MINUS] = 0x0c, + [Q_KEY_CODE_EQUAL] = 0x0d, + [Q_KEY_CODE_BACKSPACE] = 0x0e, + + [Q_KEY_CODE_TAB] = 0x0f, + [Q_KEY_CODE_Q] = 0x10, + [Q_KEY_CODE_W] = 0x11, + [Q_KEY_CODE_E] = 0x12, + [Q_KEY_CODE_R] = 0x13, + [Q_KEY_CODE_T] = 0x14, + [Q_KEY_CODE_Y] = 0x15, + [Q_KEY_CODE_U] = 0x16, + [Q_KEY_CODE_I] = 0x17, + [Q_KEY_CODE_O] = 0x18, + [Q_KEY_CODE_P] = 0x19, + [Q_KEY_CODE_BRACKET_LEFT] = 0x1a, + [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b, + [Q_KEY_CODE_RET] = 0x1c, + + [Q_KEY_CODE_A] = 0x1e, + [Q_KEY_CODE_S] = 0x1f, + [Q_KEY_CODE_D] = 0x20, + [Q_KEY_CODE_F] = 0x21, + [Q_KEY_CODE_G] = 0x22, + [Q_KEY_CODE_H] = 0x23, + [Q_KEY_CODE_J] = 0x24, + [Q_KEY_CODE_K] = 0x25, + [Q_KEY_CODE_L] = 0x26, + [Q_KEY_CODE_SEMICOLON] = 0x27, + [Q_KEY_CODE_APOSTROPHE] = 0x28, + [Q_KEY_CODE_GRAVE_ACCENT] = 0x29, + + [Q_KEY_CODE_BACKSLASH] = 0x2b, + [Q_KEY_CODE_Z] = 0x2c, + [Q_KEY_CODE_X] = 0x2d, + [Q_KEY_CODE_C] = 0x2e, + [Q_KEY_CODE_V] = 0x2f, + [Q_KEY_CODE_B] = 0x30, + [Q_KEY_CODE_N] = 0x31, + [Q_KEY_CODE_M] = 0x32, + [Q_KEY_CODE_COMMA] = 0x33, + [Q_KEY_CODE_DOT] = 0x34, + [Q_KEY_CODE_SLASH] = 0x35, + + [Q_KEY_CODE_ASTERISK] = 0x37, + + [Q_KEY_CODE_SPC] = 0x39, + [Q_KEY_CODE_CAPS_LOCK] = 0x3a, + [Q_KEY_CODE_F1] = 0x3b, + [Q_KEY_CODE_F2] = 0x3c, + [Q_KEY_CODE_F3] = 0x3d, + [Q_KEY_CODE_F4] = 0x3e, + [Q_KEY_CODE_F5] = 0x3f, + [Q_KEY_CODE_F6] = 0x40, + [Q_KEY_CODE_F7] = 0x41, + [Q_KEY_CODE_F8] = 0x42, + [Q_KEY_CODE_F9] = 0x43, + [Q_KEY_CODE_F10] = 0x44, + [Q_KEY_CODE_NUM_LOCK] = 0x45, + [Q_KEY_CODE_SCROLL_LOCK] = 0x46, + + [Q_KEY_CODE_KP_DIVIDE] = 0xb5, + [Q_KEY_CODE_KP_MULTIPLY] = 0x37, + [Q_KEY_CODE_KP_SUBTRACT] = 0x4a, + [Q_KEY_CODE_KP_ADD] = 0x4e, + [Q_KEY_CODE_KP_ENTER] = 0x9c, + [Q_KEY_CODE_KP_DECIMAL] = 0x53, + [Q_KEY_CODE_SYSRQ] = 0x54, + + [Q_KEY_CODE_KP_0] = 0x52, + [Q_KEY_CODE_KP_1] = 0x4f, + [Q_KEY_CODE_KP_2] = 0x50, + [Q_KEY_CODE_KP_3] = 0x51, + [Q_KEY_CODE_KP_4] = 0x4b, + [Q_KEY_CODE_KP_5] = 0x4c, + [Q_KEY_CODE_KP_6] = 0x4d, + [Q_KEY_CODE_KP_7] = 0x47, + [Q_KEY_CODE_KP_8] = 0x48, + [Q_KEY_CODE_KP_9] = 0x49, + + [Q_KEY_CODE_LESS] = 0x56, + + [Q_KEY_CODE_F11] = 0x57, + [Q_KEY_CODE_F12] = 0x58, + + [Q_KEY_CODE_PRINT] = 0xb7, + + [Q_KEY_CODE_HOME] = 0xc7, + [Q_KEY_CODE_PGUP] = 0xc9, + [Q_KEY_CODE_PGDN] = 0xd1, + [Q_KEY_CODE_END] = 0xcf, + + [Q_KEY_CODE_LEFT] = 0xcb, + [Q_KEY_CODE_UP] = 0xc8, + [Q_KEY_CODE_DOWN] = 0xd0, + [Q_KEY_CODE_RIGHT] = 0xcd, + + [Q_KEY_CODE_INSERT] = 0xd2, + [Q_KEY_CODE_DELETE] = 0xd3, +#ifdef NEED_CPU_H +#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64) + [Q_KEY_CODE_STOP] = 0xf0, + [Q_KEY_CODE_AGAIN] = 0xf1, + [Q_KEY_CODE_PROPS] = 0xf2, + [Q_KEY_CODE_UNDO] = 0xf3, + [Q_KEY_CODE_FRONT] = 0xf4, + [Q_KEY_CODE_COPY] = 0xf5, + [Q_KEY_CODE_OPEN] = 0xf6, + [Q_KEY_CODE_PASTE] = 0xf7, + [Q_KEY_CODE_FIND] = 0xf8, + [Q_KEY_CODE_CUT] = 0xf9, + [Q_KEY_CODE_LF] = 0xfa, + [Q_KEY_CODE_HELP] = 0xfb, + [Q_KEY_CODE_META_L] = 0xfc, + [Q_KEY_CODE_META_R] = 0xfd, + [Q_KEY_CODE_COMPOSE] = 0xfe, +#endif +#endif + [Q_KEY_CODE_MAX] = 0, +}; + +int index_from_key(const char *key) +{ + int i; + + for (i = 0; QKeyCode_lookup[i] != NULL; i++) { + if (!strcmp(key, QKeyCode_lookup[i])) { + break; + } + } + + /* Return Q_KEY_CODE_MAX if the key is invalid */ + return i; +} + +static int *keycodes; +static int keycodes_size; +static QEMUTimer *key_timer; + +static int keycode_from_keyvalue(const KeyValue *value) +{ + if (value->kind == KEY_VALUE_KIND_QCODE) { + return key_defs[value->qcode]; + } else { + assert(value->kind == KEY_VALUE_KIND_NUMBER); + return value->number; + } +} + +static void free_keycodes(void) +{ + g_free(keycodes); + keycodes = NULL; + keycodes_size = 0; +} + +static void release_keys(void *opaque) +{ + while (keycodes_size > 0) { + qemu_input_event_send_key_number(NULL, keycodes[--keycodes_size], + false); + } + + free_keycodes(); +} + +void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, + Error **errp) +{ + int keycode; + KeyValueList *p; + + if (!key_timer) { + key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL); + } + + if (keycodes != NULL) { + timer_del(key_timer); + release_keys(NULL); + } + + if (!has_hold_time) { + hold_time = 100; + } + + for (p = keys; p != NULL; p = p->next) { + /* key down events */ + keycode = keycode_from_keyvalue(p->value); + if (keycode < 0x01 || keycode > 0xff) { + error_setg(errp, "invalid hex keycode 0x%x", keycode); + free_keycodes(); + return; + } + + qemu_input_event_send_key_number(NULL, keycode, true); + + keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1)); + keycodes[keycodes_size++] = keycode; + } + + /* delayed key up events */ + timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + muldiv64(get_ticks_per_sec(), hold_time, 1000)); +} + +static void legacy_kbd_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev; + int keycode = keycode_from_keyvalue(evt->key->key); + + if (!entry || !entry->put_kbd) { + return; + } + if (evt->key->key->kind == KEY_VALUE_KIND_QCODE && + evt->key->key->qcode == Q_KEY_CODE_PAUSE) { + /* specific case */ + int v = evt->key->down ? 0 : 0x80; + entry->put_kbd(entry->opaque, 0xe1); + entry->put_kbd(entry->opaque, 0x1d | v); + entry->put_kbd(entry->opaque, 0x45 | v); + return; + } + if (keycode & SCANCODE_GREY) { + entry->put_kbd(entry->opaque, SCANCODE_EMUL0); + keycode &= ~SCANCODE_GREY; + } + if (!evt->key->down) { + keycode |= SCANCODE_UP; + } + entry->put_kbd(entry->opaque, keycode); +} + +static QemuInputHandler legacy_kbd_handler = { + .name = "legacy-kbd", + .mask = INPUT_EVENT_MASK_KEY, + .event = legacy_kbd_event, +}; + +QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) +{ + QEMUPutKbdEntry *entry; + + entry = g_new0(QEMUPutKbdEntry, 1); + entry->put_kbd = func; + entry->opaque = opaque; + entry->s = qemu_input_handler_register((DeviceState *)entry, + &legacy_kbd_handler); + return entry; +} + +void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry) +{ + qemu_input_handler_unregister(entry->s); + g_free(entry); +} + +static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + static const int bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, + [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, + [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, + }; + QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev; + + switch (evt->kind) { + case INPUT_EVENT_KIND_BTN: + if (evt->btn->down) { + s->buttons |= bmap[evt->btn->button]; + } else { + s->buttons &= ~bmap[evt->btn->button]; + } + break; + case INPUT_EVENT_KIND_ABS: + s->axis[evt->abs->axis] = evt->abs->value; + break; + case INPUT_EVENT_KIND_REL: + s->axis[evt->rel->axis] += evt->rel->value; + break; + default: + break; + } +} + +static void legacy_mouse_sync(DeviceState *dev) +{ + QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev; + + s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, + s->axis[INPUT_AXIS_X], + s->axis[INPUT_AXIS_Y], + 0, + s->buttons); + + if (!s->qemu_put_mouse_event_absolute) { + s->axis[INPUT_AXIS_X] = 0; + s->axis[INPUT_AXIS_Y] = 0; + } +} + +QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, + void *opaque, int absolute, + const char *name) +{ + QEMUPutMouseEntry *s; + + s = g_malloc0(sizeof(QEMUPutMouseEntry)); + + s->qemu_put_mouse_event = func; + s->qemu_put_mouse_event_opaque = opaque; + s->qemu_put_mouse_event_absolute = absolute; + + s->h.name = name; + s->h.mask = INPUT_EVENT_MASK_BTN | + (absolute ? INPUT_EVENT_MASK_ABS : INPUT_EVENT_MASK_REL); + s->h.event = legacy_mouse_event; + s->h.sync = legacy_mouse_sync; + s->s = qemu_input_handler_register((DeviceState *)s, + &s->h); + + return s; +} + +void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry) +{ + qemu_input_handler_activate(entry->s); +} + +void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry) +{ + qemu_input_handler_unregister(entry->s); + + g_free(entry); +} + +QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, + void *opaque) +{ + QEMUPutLEDEntry *s; + + s = g_malloc0(sizeof(QEMUPutLEDEntry)); + + s->put_led = func; + s->opaque = opaque; + QTAILQ_INSERT_TAIL(&led_handlers, s, next); + return s; +} + +void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry) +{ + if (entry == NULL) + return; + QTAILQ_REMOVE(&led_handlers, entry, next); + g_free(entry); +} + +void kbd_put_ledstate(int ledstate) +{ + QEMUPutLEDEntry *cursor; + + QTAILQ_FOREACH(cursor, &led_handlers, next) { + cursor->put_led(cursor->opaque, ledstate); + } +} diff --git a/ui/input.c b/ui/input.c index 1c70f60e0d..2761911f3c 100644 --- a/ui/input.c +++ b/ui/input.c @@ -1,520 +1,333 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - #include "sysemu/sysemu.h" -#include "monitor/monitor.h" -#include "ui/console.h" -#include "qapi/error.h" -#include "qmp-commands.h" #include "qapi-types.h" -#include "ui/keymaps.h" - -struct QEMUPutMouseEntry { - QEMUPutMouseEvent *qemu_put_mouse_event; - void *qemu_put_mouse_event_opaque; - int qemu_put_mouse_event_absolute; - char *qemu_put_mouse_event_name; - - int index; - - /* used internally by qemu for handling mice */ - QTAILQ_ENTRY(QEMUPutMouseEntry) node; -}; - -struct QEMUPutKbdEntry { - QEMUPutKBDEvent *put_kbd; - void *opaque; - QTAILQ_ENTRY(QEMUPutKbdEntry) next; -}; +#include "qmp-commands.h" +#include "trace.h" +#include "ui/input.h" +#include "ui/console.h" -struct QEMUPutLEDEntry { - QEMUPutLEDEvent *put_led; - void *opaque; - QTAILQ_ENTRY(QEMUPutLEDEntry) next; +struct QemuInputHandlerState { + DeviceState *dev; + QemuInputHandler *handler; + int id; + int events; + QTAILQ_ENTRY(QemuInputHandlerState) node; }; - -static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = - QTAILQ_HEAD_INITIALIZER(led_handlers); -static QTAILQ_HEAD(, QEMUPutKbdEntry) kbd_handlers = - QTAILQ_HEAD_INITIALIZER(kbd_handlers); -static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = - QTAILQ_HEAD_INITIALIZER(mouse_handlers); +static QTAILQ_HEAD(, QemuInputHandlerState) handlers = + QTAILQ_HEAD_INITIALIZER(handlers); static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); -static const int key_defs[] = { - [Q_KEY_CODE_SHIFT] = 0x2a, - [Q_KEY_CODE_SHIFT_R] = 0x36, - - [Q_KEY_CODE_ALT] = 0x38, - [Q_KEY_CODE_ALT_R] = 0xb8, - [Q_KEY_CODE_ALTGR] = 0x64, - [Q_KEY_CODE_ALTGR_R] = 0xe4, - [Q_KEY_CODE_CTRL] = 0x1d, - [Q_KEY_CODE_CTRL_R] = 0x9d, - - [Q_KEY_CODE_MENU] = 0xdd, - - [Q_KEY_CODE_ESC] = 0x01, - - [Q_KEY_CODE_1] = 0x02, - [Q_KEY_CODE_2] = 0x03, - [Q_KEY_CODE_3] = 0x04, - [Q_KEY_CODE_4] = 0x05, - [Q_KEY_CODE_5] = 0x06, - [Q_KEY_CODE_6] = 0x07, - [Q_KEY_CODE_7] = 0x08, - [Q_KEY_CODE_8] = 0x09, - [Q_KEY_CODE_9] = 0x0a, - [Q_KEY_CODE_0] = 0x0b, - [Q_KEY_CODE_MINUS] = 0x0c, - [Q_KEY_CODE_EQUAL] = 0x0d, - [Q_KEY_CODE_BACKSPACE] = 0x0e, - - [Q_KEY_CODE_TAB] = 0x0f, - [Q_KEY_CODE_Q] = 0x10, - [Q_KEY_CODE_W] = 0x11, - [Q_KEY_CODE_E] = 0x12, - [Q_KEY_CODE_R] = 0x13, - [Q_KEY_CODE_T] = 0x14, - [Q_KEY_CODE_Y] = 0x15, - [Q_KEY_CODE_U] = 0x16, - [Q_KEY_CODE_I] = 0x17, - [Q_KEY_CODE_O] = 0x18, - [Q_KEY_CODE_P] = 0x19, - [Q_KEY_CODE_BRACKET_LEFT] = 0x1a, - [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b, - [Q_KEY_CODE_RET] = 0x1c, - - [Q_KEY_CODE_A] = 0x1e, - [Q_KEY_CODE_S] = 0x1f, - [Q_KEY_CODE_D] = 0x20, - [Q_KEY_CODE_F] = 0x21, - [Q_KEY_CODE_G] = 0x22, - [Q_KEY_CODE_H] = 0x23, - [Q_KEY_CODE_J] = 0x24, - [Q_KEY_CODE_K] = 0x25, - [Q_KEY_CODE_L] = 0x26, - [Q_KEY_CODE_SEMICOLON] = 0x27, - [Q_KEY_CODE_APOSTROPHE] = 0x28, - [Q_KEY_CODE_GRAVE_ACCENT] = 0x29, - - [Q_KEY_CODE_BACKSLASH] = 0x2b, - [Q_KEY_CODE_Z] = 0x2c, - [Q_KEY_CODE_X] = 0x2d, - [Q_KEY_CODE_C] = 0x2e, - [Q_KEY_CODE_V] = 0x2f, - [Q_KEY_CODE_B] = 0x30, - [Q_KEY_CODE_N] = 0x31, - [Q_KEY_CODE_M] = 0x32, - [Q_KEY_CODE_COMMA] = 0x33, - [Q_KEY_CODE_DOT] = 0x34, - [Q_KEY_CODE_SLASH] = 0x35, - - [Q_KEY_CODE_ASTERISK] = 0x37, - - [Q_KEY_CODE_SPC] = 0x39, - [Q_KEY_CODE_CAPS_LOCK] = 0x3a, - [Q_KEY_CODE_F1] = 0x3b, - [Q_KEY_CODE_F2] = 0x3c, - [Q_KEY_CODE_F3] = 0x3d, - [Q_KEY_CODE_F4] = 0x3e, - [Q_KEY_CODE_F5] = 0x3f, - [Q_KEY_CODE_F6] = 0x40, - [Q_KEY_CODE_F7] = 0x41, - [Q_KEY_CODE_F8] = 0x42, - [Q_KEY_CODE_F9] = 0x43, - [Q_KEY_CODE_F10] = 0x44, - [Q_KEY_CODE_NUM_LOCK] = 0x45, - [Q_KEY_CODE_SCROLL_LOCK] = 0x46, - - [Q_KEY_CODE_KP_DIVIDE] = 0xb5, - [Q_KEY_CODE_KP_MULTIPLY] = 0x37, - [Q_KEY_CODE_KP_SUBTRACT] = 0x4a, - [Q_KEY_CODE_KP_ADD] = 0x4e, - [Q_KEY_CODE_KP_ENTER] = 0x9c, - [Q_KEY_CODE_KP_DECIMAL] = 0x53, - [Q_KEY_CODE_SYSRQ] = 0x54, - - [Q_KEY_CODE_KP_0] = 0x52, - [Q_KEY_CODE_KP_1] = 0x4f, - [Q_KEY_CODE_KP_2] = 0x50, - [Q_KEY_CODE_KP_3] = 0x51, - [Q_KEY_CODE_KP_4] = 0x4b, - [Q_KEY_CODE_KP_5] = 0x4c, - [Q_KEY_CODE_KP_6] = 0x4d, - [Q_KEY_CODE_KP_7] = 0x47, - [Q_KEY_CODE_KP_8] = 0x48, - [Q_KEY_CODE_KP_9] = 0x49, - - [Q_KEY_CODE_LESS] = 0x56, - - [Q_KEY_CODE_F11] = 0x57, - [Q_KEY_CODE_F12] = 0x58, - - [Q_KEY_CODE_PRINT] = 0xb7, - - [Q_KEY_CODE_HOME] = 0xc7, - [Q_KEY_CODE_PGUP] = 0xc9, - [Q_KEY_CODE_PGDN] = 0xd1, - [Q_KEY_CODE_END] = 0xcf, - - [Q_KEY_CODE_LEFT] = 0xcb, - [Q_KEY_CODE_UP] = 0xc8, - [Q_KEY_CODE_DOWN] = 0xd0, - [Q_KEY_CODE_RIGHT] = 0xcd, - - [Q_KEY_CODE_INSERT] = 0xd2, - [Q_KEY_CODE_DELETE] = 0xd3, -#ifdef NEED_CPU_H -#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64) - [Q_KEY_CODE_STOP] = 0xf0, - [Q_KEY_CODE_AGAIN] = 0xf1, - [Q_KEY_CODE_PROPS] = 0xf2, - [Q_KEY_CODE_UNDO] = 0xf3, - [Q_KEY_CODE_FRONT] = 0xf4, - [Q_KEY_CODE_COPY] = 0xf5, - [Q_KEY_CODE_OPEN] = 0xf6, - [Q_KEY_CODE_PASTE] = 0xf7, - [Q_KEY_CODE_FIND] = 0xf8, - [Q_KEY_CODE_CUT] = 0xf9, - [Q_KEY_CODE_LF] = 0xfa, - [Q_KEY_CODE_HELP] = 0xfb, - [Q_KEY_CODE_META_L] = 0xfc, - [Q_KEY_CODE_META_R] = 0xfd, - [Q_KEY_CODE_COMPOSE] = 0xfe, -#endif -#endif - [Q_KEY_CODE_MAX] = 0, -}; - -int index_from_key(const char *key) +QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, + QemuInputHandler *handler) { - int i; + QemuInputHandlerState *s = g_new0(QemuInputHandlerState, 1); + static int id = 1; - for (i = 0; QKeyCode_lookup[i] != NULL; i++) { - if (!strcmp(key, QKeyCode_lookup[i])) { - break; - } - } + s->dev = dev; + s->handler = handler; + s->id = id++; + QTAILQ_INSERT_TAIL(&handlers, s, node); - /* Return Q_KEY_CODE_MAX if the key is invalid */ - return i; + qemu_input_check_mode_change(); + return s; } -int index_from_keycode(int code) +void qemu_input_handler_activate(QemuInputHandlerState *s) { - int i; - - for (i = 0; i < Q_KEY_CODE_MAX; i++) { - if (key_defs[i] == code) { - break; - } - } - - /* Return Q_KEY_CODE_MAX if the code is invalid */ - return i; + QTAILQ_REMOVE(&handlers, s, node); + QTAILQ_INSERT_HEAD(&handlers, s, node); + qemu_input_check_mode_change(); } -static int *keycodes; -static int keycodes_size; -static QEMUTimer *key_timer; - -static int keycode_from_keyvalue(const KeyValue *value) +void qemu_input_handler_unregister(QemuInputHandlerState *s) { - if (value->kind == KEY_VALUE_KIND_QCODE) { - return key_defs[value->qcode]; - } else { - assert(value->kind == KEY_VALUE_KIND_NUMBER); - return value->number; - } + QTAILQ_REMOVE(&handlers, s, node); + g_free(s); + qemu_input_check_mode_change(); } -static void free_keycodes(void) +static QemuInputHandlerState* +qemu_input_find_handler(uint32_t mask) { - g_free(keycodes); - keycodes = NULL; - keycodes_size = 0; -} + QemuInputHandlerState *s; -static void release_keys(void *opaque) -{ - while (keycodes_size > 0) { - if (keycodes[--keycodes_size] & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); + QTAILQ_FOREACH(s, &handlers, node) { + if (mask & s->handler->mask) { + return s; } - kbd_put_keycode(keycodes[keycodes_size] | SCANCODE_UP); } - - free_keycodes(); + return NULL; } -void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, - Error **errp) +static void qemu_input_transform_abs_rotate(InputEvent *evt) { - int keycode; - KeyValueList *p; - - if (!key_timer) { - key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL); - } - - if (keycodes != NULL) { - timer_del(key_timer); - release_keys(NULL); - } - - if (!has_hold_time) { - hold_time = 100; - } - - for (p = keys; p != NULL; p = p->next) { - /* key down events */ - keycode = keycode_from_keyvalue(p->value); - if (keycode < 0x01 || keycode > 0xff) { - error_setg(errp, "invalid hex keycode 0x%x", keycode); - free_keycodes(); - return; + switch (graphic_rotate) { + case 90: + if (evt->abs->axis == INPUT_AXIS_X) { + evt->abs->axis = INPUT_AXIS_Y; + } else if (evt->abs->axis == INPUT_AXIS_Y) { + evt->abs->axis = INPUT_AXIS_X; + evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; } - - if (keycode & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); + break; + case 180: + evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + break; + case 270: + if (evt->abs->axis == INPUT_AXIS_X) { + evt->abs->axis = INPUT_AXIS_Y; + evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + } else if (evt->abs->axis == INPUT_AXIS_Y) { + evt->abs->axis = INPUT_AXIS_X; } - kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); - - keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1)); - keycodes[keycodes_size++] = keycode; + break; } - - /* delayed key up events */ - timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(get_ticks_per_sec(), hold_time, 1000)); } -QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) +static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) { - QEMUPutKbdEntry *entry; + const char *name; + int idx = -1; - entry = g_malloc0(sizeof(QEMUPutKbdEntry)); - entry->put_kbd = func; - entry->opaque = opaque; - QTAILQ_INSERT_HEAD(&kbd_handlers, entry, next); - return entry; + if (src) { + idx = qemu_console_get_index(src); + } + switch (evt->kind) { + case INPUT_EVENT_KIND_KEY: + switch (evt->key->key->kind) { + case KEY_VALUE_KIND_NUMBER: + trace_input_event_key_number(idx, evt->key->key->number, + evt->key->down); + break; + case KEY_VALUE_KIND_QCODE: + name = QKeyCode_lookup[evt->key->key->qcode]; + trace_input_event_key_qcode(idx, name, evt->key->down); + break; + case KEY_VALUE_KIND_MAX: + /* keep gcc happy */ + break; + } + break; + case INPUT_EVENT_KIND_BTN: + name = InputButton_lookup[evt->btn->button]; + trace_input_event_btn(idx, name, evt->btn->down); + break; + case INPUT_EVENT_KIND_REL: + name = InputAxis_lookup[evt->rel->axis]; + trace_input_event_rel(idx, name, evt->rel->value); + break; + case INPUT_EVENT_KIND_ABS: + name = InputAxis_lookup[evt->abs->axis]; + trace_input_event_abs(idx, name, evt->abs->value); + break; + case INPUT_EVENT_KIND_MAX: + /* keep gcc happy */ + break; + } } -void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry) +void qemu_input_event_send(QemuConsole *src, InputEvent *evt) { - QTAILQ_REMOVE(&kbd_handlers, entry, next); -} + QemuInputHandlerState *s; -static void check_mode_change(void) -{ - static int current_is_absolute, current_has_absolute; - int is_absolute; - int has_absolute; + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { + return; + } - is_absolute = kbd_mouse_is_absolute(); - has_absolute = kbd_mouse_has_absolute(); + qemu_input_event_trace(src, evt); - if (is_absolute != current_is_absolute || - has_absolute != current_has_absolute) { - notifier_list_notify(&mouse_mode_notifiers, NULL); + /* pre processing */ + if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) { + qemu_input_transform_abs_rotate(evt); } - current_is_absolute = is_absolute; - current_has_absolute = has_absolute; + /* send event */ + s = qemu_input_find_handler(1 << evt->kind); + s->handler->event(s->dev, src, evt); + s->events++; } -QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, - void *opaque, int absolute, - const char *name) +void qemu_input_event_sync(void) { - QEMUPutMouseEntry *s; - static int mouse_index = 0; - - s = g_malloc0(sizeof(QEMUPutMouseEntry)); + QemuInputHandlerState *s; - s->qemu_put_mouse_event = func; - s->qemu_put_mouse_event_opaque = opaque; - s->qemu_put_mouse_event_absolute = absolute; - s->qemu_put_mouse_event_name = g_strdup(name); - s->index = mouse_index++; - - QTAILQ_INSERT_TAIL(&mouse_handlers, s, node); + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { + return; + } - check_mode_change(); + trace_input_event_sync(); - return s; + QTAILQ_FOREACH(s, &handlers, node) { + if (!s->events) { + continue; + } + if (s->handler->sync) { + s->handler->sync(s->dev); + } + s->events = 0; + } } -void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry) +InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) { - QTAILQ_REMOVE(&mouse_handlers, entry, node); - QTAILQ_INSERT_HEAD(&mouse_handlers, entry, node); - - check_mode_change(); + InputEvent *evt = g_new0(InputEvent, 1); + evt->key = g_new0(InputKeyEvent, 1); + evt->kind = INPUT_EVENT_KIND_KEY; + evt->key->key = key; + evt->key->down = down; + return evt; } -void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry) +void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) { - QTAILQ_REMOVE(&mouse_handlers, entry, node); - - g_free(entry->qemu_put_mouse_event_name); - g_free(entry); - - check_mode_change(); + InputEvent *evt; + evt = qemu_input_event_new_key(key, down); + qemu_input_event_send(src, evt); + qemu_input_event_sync(); + qapi_free_InputEvent(evt); } -QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, - void *opaque) +void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) { - QEMUPutLEDEntry *s; + KeyValue *key = g_new0(KeyValue, 1); + key->kind = KEY_VALUE_KIND_NUMBER; + key->number = num; + qemu_input_event_send_key(src, key, down); +} - s = g_malloc0(sizeof(QEMUPutLEDEntry)); +void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) +{ + KeyValue *key = g_new0(KeyValue, 1); + key->kind = KEY_VALUE_KIND_QCODE; + key->qcode = q; + qemu_input_event_send_key(src, key, down); +} - s->put_led = func; - s->opaque = opaque; - QTAILQ_INSERT_TAIL(&led_handlers, s, next); - return s; +InputEvent *qemu_input_event_new_btn(InputButton btn, bool down) +{ + InputEvent *evt = g_new0(InputEvent, 1); + evt->btn = g_new0(InputBtnEvent, 1); + evt->kind = INPUT_EVENT_KIND_BTN; + evt->btn->button = btn; + evt->btn->down = down; + return evt; } -void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry) +void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down) { - if (entry == NULL) - return; - QTAILQ_REMOVE(&led_handlers, entry, next); - g_free(entry); + InputEvent *evt; + evt = qemu_input_event_new_btn(btn, down); + qemu_input_event_send(src, evt); + qapi_free_InputEvent(evt); } -void kbd_put_keycode(int keycode) +void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, + uint32_t button_old, uint32_t button_new) { - QEMUPutKbdEntry *entry = QTAILQ_FIRST(&kbd_handlers); + InputButton btn; + uint32_t mask; - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { - return; - } - if (entry && entry->put_kbd) { - entry->put_kbd(entry->opaque, keycode); + for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) { + mask = button_map[btn]; + if ((button_old & mask) == (button_new & mask)) { + continue; + } + qemu_input_queue_btn(src, btn, button_new & mask); } } -void kbd_put_ledstate(int ledstate) +bool qemu_input_is_absolute(void) { - QEMUPutLEDEntry *cursor; + QemuInputHandlerState *s; - QTAILQ_FOREACH(cursor, &led_handlers, next) { - cursor->put_led(cursor->opaque, ledstate); - } + s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS); + return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS); } -void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) +int qemu_input_scale_axis(int value, int size_in, int size_out) { - QEMUPutMouseEntry *entry; - QEMUPutMouseEvent *mouse_event; - void *mouse_event_opaque; - int width, height; - - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { - return; + if (size_in < 2) { + return size_out / 2; } - if (QTAILQ_EMPTY(&mouse_handlers)) { - return; - } - - entry = QTAILQ_FIRST(&mouse_handlers); + return (int64_t)value * (size_out - 1) / (size_in - 1); +} - mouse_event = entry->qemu_put_mouse_event; - mouse_event_opaque = entry->qemu_put_mouse_event_opaque; +InputEvent *qemu_input_event_new_move(InputEventKind kind, + InputAxis axis, int value) +{ + InputEvent *evt = g_new0(InputEvent, 1); + InputMoveEvent *move = g_new0(InputMoveEvent, 1); + + evt->kind = kind; + evt->data = move; + move->axis = axis; + move->value = value; + return evt; +} - if (mouse_event) { - if (entry->qemu_put_mouse_event_absolute) { - width = 0x7fff; - height = 0x7fff; - } else { - width = graphic_width - 1; - height = graphic_height - 1; - } +void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value) +{ + InputEvent *evt; + evt = qemu_input_event_new_move(INPUT_EVENT_KIND_REL, axis, value); + qemu_input_event_send(src, evt); + qapi_free_InputEvent(evt); +} - switch (graphic_rotate) { - case 0: - mouse_event(mouse_event_opaque, - dx, dy, dz, buttons_state); - break; - case 90: - mouse_event(mouse_event_opaque, - width - dy, dx, dz, buttons_state); - break; - case 180: - mouse_event(mouse_event_opaque, - width - dx, height - dy, dz, buttons_state); - break; - case 270: - mouse_event(mouse_event_opaque, - dy, height - dx, dz, buttons_state); - break; - } - } +void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int size) +{ + InputEvent *evt; + int scaled = qemu_input_scale_axis(value, size, INPUT_EVENT_ABS_SIZE); + evt = qemu_input_event_new_move(INPUT_EVENT_KIND_ABS, axis, scaled); + qemu_input_event_send(src, evt); + qapi_free_InputEvent(evt); } -int kbd_mouse_is_absolute(void) +void qemu_input_check_mode_change(void) { - if (QTAILQ_EMPTY(&mouse_handlers)) { - return 0; + static int current_is_absolute; + int is_absolute; + + is_absolute = qemu_input_is_absolute(); + + if (is_absolute != current_is_absolute) { + trace_input_mouse_mode(is_absolute); + notifier_list_notify(&mouse_mode_notifiers, NULL); } - return QTAILQ_FIRST(&mouse_handlers)->qemu_put_mouse_event_absolute; + current_is_absolute = is_absolute; } -int kbd_mouse_has_absolute(void) +void qemu_add_mouse_mode_change_notifier(Notifier *notify) { - QEMUPutMouseEntry *entry; - - QTAILQ_FOREACH(entry, &mouse_handlers, node) { - if (entry->qemu_put_mouse_event_absolute) { - return 1; - } - } + notifier_list_add(&mouse_mode_notifiers, notify); +} - return 0; +void qemu_remove_mouse_mode_change_notifier(Notifier *notify) +{ + notifier_remove(notify); } MouseInfoList *qmp_query_mice(Error **errp) { MouseInfoList *mice_list = NULL; - QEMUPutMouseEntry *cursor; + MouseInfoList *info; + QemuInputHandlerState *s; bool current = true; - QTAILQ_FOREACH(cursor, &mouse_handlers, node) { - MouseInfoList *info = g_malloc0(sizeof(*info)); - info->value = g_malloc0(sizeof(*info->value)); - info->value->name = g_strdup(cursor->qemu_put_mouse_event_name); - info->value->index = cursor->index; - info->value->absolute = !!cursor->qemu_put_mouse_event_absolute; + QTAILQ_FOREACH(s, &handlers, node) { + if (!(s->handler->mask & + (INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS))) { + continue; + } + + info = g_new0(MouseInfoList, 1); + info->value = g_new0(MouseInfo, 1); + info->value->index = s->id; + info->value->name = g_strdup(s->handler->name); + info->value->absolute = s->handler->mask & INPUT_EVENT_MASK_ABS; info->value->current = current; current = false; - info->next = mice_list; mice_list = info; } @@ -524,19 +337,14 @@ MouseInfoList *qmp_query_mice(Error **errp) void do_mouse_set(Monitor *mon, const QDict *qdict) { - QEMUPutMouseEntry *cursor; + QemuInputHandlerState *s; int index = qdict_get_int(qdict, "index"); int found = 0; - if (QTAILQ_EMPTY(&mouse_handlers)) { - monitor_printf(mon, "No mouse devices connected\n"); - return; - } - - QTAILQ_FOREACH(cursor, &mouse_handlers, node) { - if (cursor->index == index) { + QTAILQ_FOREACH(s, &handlers, node) { + if (s->id == index) { found = 1; - qemu_activate_mouse_event_handler(cursor); + qemu_input_handler_activate(s); break; } } @@ -545,15 +353,5 @@ void do_mouse_set(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Mouse at given index not found\n"); } - check_mode_change(); -} - -void qemu_add_mouse_mode_change_notifier(Notifier *notify) -{ - notifier_list_add(&mouse_mode_notifiers, notify); -} - -void qemu_remove_mouse_mode_change_notifier(Notifier *notify) -{ - notifier_remove(notify); + qemu_input_check_mode_change(); } @@ -26,10 +26,13 @@ #undef WIN32_LEAN_AND_MEAN #include <SDL.h> + +#if SDL_MAJOR_VERSION == 1 #include <SDL_syswm.h> #include "qemu-common.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #include "x_keymap.h" #include "sdl_zoom.h" @@ -261,9 +264,7 @@ static void reset_keys(void) int i; for(i = 0; i < 256; i++) { if (modifiers_state[i]) { - if (i & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(i | SCANCODE_UP); + qemu_input_event_send_key_number(dcl->con, i, false); modifiers_state[i] = 0; } } @@ -271,16 +272,12 @@ static void reset_keys(void) static void sdl_process_key(SDL_KeyboardEvent *ev) { - int keycode, v; + int keycode; if (ev->keysym.sym == SDLK_PAUSE) { /* specific case */ - v = 0; - if (ev->type == SDL_KEYUP) - v |= SCANCODE_UP; - kbd_put_keycode(0xe1); - kbd_put_keycode(0x1d | v); - kbd_put_keycode(0x45 | v); + qemu_input_event_send_key_qcode(dcl->con, Q_KEY_CODE_PAUSE, + ev->type == SDL_KEYDOWN); return; } @@ -312,19 +309,15 @@ static void sdl_process_key(SDL_KeyboardEvent *ev) case 0x45: /* num lock */ case 0x3a: /* caps lock */ /* SDL does not send the key up event, so we generate it */ - kbd_put_keycode(keycode); - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(dcl->con, keycode, true); + qemu_input_event_send_key_number(dcl->con, keycode, false); return; #endif } /* now send the key code */ - if (keycode & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - if (ev->type == SDL_KEYUP) - kbd_put_keycode(keycode | SCANCODE_UP); - else - kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); + qemu_input_event_send_key_number(dcl->con, keycode, + ev->type == SDL_KEYDOWN); } static void sdl_update_caption(void) @@ -360,7 +353,7 @@ static void sdl_hide_cursor(void) if (!cursor_hide) return; - if (kbd_mouse_is_absolute()) { + if (qemu_input_is_absolute()) { SDL_ShowCursor(1); SDL_SetCursor(sdl_cursor_hidden); } else { @@ -373,10 +366,10 @@ static void sdl_show_cursor(void) if (!cursor_hide) return; - if (!kbd_mouse_is_absolute() || !qemu_console_is_graphic(NULL)) { + if (!qemu_input_is_absolute() || !qemu_console_is_graphic(NULL)) { SDL_ShowCursor(1); if (guest_cursor && - (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) + (gui_grab || qemu_input_is_absolute() || absolute_enabled)) SDL_SetCursor(guest_sprite); else SDL_SetCursor(sdl_cursor_normal); @@ -395,8 +388,9 @@ static void sdl_grab_start(void) } if (guest_cursor) { SDL_SetCursor(guest_sprite); - if (!kbd_mouse_is_absolute() && !absolute_enabled) + if (!qemu_input_is_absolute() && !absolute_enabled) { SDL_WarpMouse(guest_x, guest_y); + } } else sdl_hide_cursor(); SDL_WM_GrabInput(SDL_GRAB_ON); @@ -425,7 +419,7 @@ static void absolute_mouse_grab(void) static void sdl_mouse_mode_change(Notifier *notify, void *data) { - if (kbd_mouse_is_absolute()) { + if (qemu_input_is_absolute()) { if (!absolute_enabled) { absolute_enabled = 1; if (qemu_console_is_graphic(NULL)) { @@ -440,33 +434,36 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data) } } -static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state) +static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state) { - int buttons = 0; - - if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) { - buttons |= MOUSE_EVENT_LBUTTON; - } - if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) { - buttons |= MOUSE_EVENT_RBUTTON; - } - if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) { - buttons |= MOUSE_EVENT_MBUTTON; - } - - if (kbd_mouse_is_absolute()) { - dx = x * 0x7FFF / (real_screen->w - 1); - dy = y * 0x7FFF / (real_screen->h - 1); + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT), + [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE), + [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT), + [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP), + [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN), + }; + static uint32_t prev_state; + + if (prev_state != state) { + qemu_input_update_buttons(dcl->con, bmap, prev_state, state); + prev_state = state; + } + + if (qemu_input_is_absolute()) { + qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x, + real_screen->w); + qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y, + real_screen->h); } else if (guest_cursor) { x -= guest_x; y -= guest_y; guest_x += x; guest_y += y; - dx = x; - dy = y; + qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, x); + qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, y); } - - kbd_mouse_event(dx, dy, dz, buttons); + qemu_input_event_sync(); } static void sdl_scale(int width, int height) @@ -694,7 +691,7 @@ static void handle_mousemotion(SDL_Event *ev) int max_x, max_y; if (qemu_console_is_graphic(NULL) && - (kbd_mouse_is_absolute() || absolute_enabled)) { + (qemu_input_is_absolute() || absolute_enabled)) { max_x = real_screen->w - 1; max_y = real_screen->h - 1; if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 || @@ -707,8 +704,8 @@ static void handle_mousemotion(SDL_Event *ev) sdl_grab_start(); } } - if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { - sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0, + if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, ev->motion.x, ev->motion.y, ev->motion.state); } } @@ -717,35 +714,24 @@ static void handle_mousebutton(SDL_Event *ev) { int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; - int dz; if (!qemu_console_is_graphic(NULL)) { return; } bev = &ev->button; - if (!gui_grab && !kbd_mouse_is_absolute()) { + if (!gui_grab && !qemu_input_is_absolute()) { if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { /* start grabbing all events */ sdl_grab_start(); } } else { - dz = 0; if (ev->type == SDL_MOUSEBUTTONDOWN) { buttonstate |= SDL_BUTTON(bev->button); } else { buttonstate &= ~SDL_BUTTON(bev->button); } -#ifdef SDL_BUTTON_WHEELUP - if (bev->button == SDL_BUTTON_WHEELUP && - ev->type == SDL_MOUSEBUTTONDOWN) { - dz = -1; - } else if (bev->button == SDL_BUTTON_WHEELDOWN && - ev->type == SDL_MOUSEBUTTONDOWN) { - dz = 1; - } -#endif - sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate); + sdl_send_mouse_event(0, 0, bev->x, bev->y, buttonstate); } } @@ -760,7 +746,7 @@ static void handle_activation(SDL_Event *ev) } #endif if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) && - (kbd_mouse_is_absolute() || absolute_enabled)) { + (qemu_input_is_absolute() || absolute_enabled)) { absolute_mouse_grab(); } if (ev->active.state & SDL_APPACTIVE) { @@ -832,10 +818,11 @@ static void sdl_mouse_warp(DisplayChangeListener *dcl, if (on) { if (!guest_cursor) sdl_show_cursor(); - if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { + if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { SDL_SetCursor(guest_sprite); - if (!kbd_mouse_is_absolute() && !absolute_enabled) + if (!qemu_input_is_absolute() && !absolute_enabled) { SDL_WarpMouse(x, y); + } } } else if (gui_grab) sdl_hide_cursor(); @@ -863,7 +850,7 @@ static void sdl_mouse_define(DisplayChangeListener *dcl, g_free(mask); if (guest_cursor && - (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) + (gui_grab || qemu_input_is_absolute() || absolute_enabled)) SDL_SetCursor(guest_sprite); } @@ -966,3 +953,4 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) atexit(sdl_cleanup); } +#endif diff --git a/ui/sdl2-keymap.h b/ui/sdl2-keymap.h new file mode 100644 index 0000000000..5a12f4543a --- /dev/null +++ b/ui/sdl2-keymap.h @@ -0,0 +1,266 @@ + +/* map SDL2 scancodes to QKeyCode */ + +static const int sdl2_scancode_to_qcode[SDL_NUM_SCANCODES] = { + [SDL_SCANCODE_A] = Q_KEY_CODE_A, + [SDL_SCANCODE_B] = Q_KEY_CODE_B, + [SDL_SCANCODE_C] = Q_KEY_CODE_C, + [SDL_SCANCODE_D] = Q_KEY_CODE_D, + [SDL_SCANCODE_E] = Q_KEY_CODE_E, + [SDL_SCANCODE_F] = Q_KEY_CODE_F, + [SDL_SCANCODE_G] = Q_KEY_CODE_G, + [SDL_SCANCODE_H] = Q_KEY_CODE_H, + [SDL_SCANCODE_I] = Q_KEY_CODE_I, + [SDL_SCANCODE_J] = Q_KEY_CODE_J, + [SDL_SCANCODE_K] = Q_KEY_CODE_K, + [SDL_SCANCODE_L] = Q_KEY_CODE_L, + [SDL_SCANCODE_M] = Q_KEY_CODE_M, + [SDL_SCANCODE_N] = Q_KEY_CODE_N, + [SDL_SCANCODE_O] = Q_KEY_CODE_O, + [SDL_SCANCODE_P] = Q_KEY_CODE_P, + [SDL_SCANCODE_Q] = Q_KEY_CODE_Q, + [SDL_SCANCODE_R] = Q_KEY_CODE_R, + [SDL_SCANCODE_S] = Q_KEY_CODE_S, + [SDL_SCANCODE_T] = Q_KEY_CODE_T, + [SDL_SCANCODE_U] = Q_KEY_CODE_U, + [SDL_SCANCODE_V] = Q_KEY_CODE_V, + [SDL_SCANCODE_W] = Q_KEY_CODE_W, + [SDL_SCANCODE_X] = Q_KEY_CODE_X, + [SDL_SCANCODE_Y] = Q_KEY_CODE_Y, + [SDL_SCANCODE_Z] = Q_KEY_CODE_Z, + + [SDL_SCANCODE_1] = Q_KEY_CODE_1, + [SDL_SCANCODE_2] = Q_KEY_CODE_2, + [SDL_SCANCODE_3] = Q_KEY_CODE_3, + [SDL_SCANCODE_4] = Q_KEY_CODE_4, + [SDL_SCANCODE_5] = Q_KEY_CODE_5, + [SDL_SCANCODE_6] = Q_KEY_CODE_6, + [SDL_SCANCODE_7] = Q_KEY_CODE_7, + [SDL_SCANCODE_8] = Q_KEY_CODE_8, + [SDL_SCANCODE_9] = Q_KEY_CODE_9, + [SDL_SCANCODE_0] = Q_KEY_CODE_0, + + [SDL_SCANCODE_RETURN] = Q_KEY_CODE_RET, + [SDL_SCANCODE_ESCAPE] = Q_KEY_CODE_ESC, + [SDL_SCANCODE_BACKSPACE] = Q_KEY_CODE_BACKSPACE, + [SDL_SCANCODE_TAB] = Q_KEY_CODE_TAB, + [SDL_SCANCODE_SPACE] = Q_KEY_CODE_SPC, + [SDL_SCANCODE_MINUS] = Q_KEY_CODE_MINUS, + [SDL_SCANCODE_EQUALS] = Q_KEY_CODE_EQUAL, + [SDL_SCANCODE_LEFTBRACKET] = Q_KEY_CODE_BRACKET_LEFT, + [SDL_SCANCODE_RIGHTBRACKET] = Q_KEY_CODE_BRACKET_RIGHT, + [SDL_SCANCODE_BACKSLASH] = Q_KEY_CODE_BACKSLASH, +#if 0 + [SDL_SCANCODE_NONUSHASH] = Q_KEY_CODE_NONUSHASH, +#endif + [SDL_SCANCODE_SEMICOLON] = Q_KEY_CODE_SEMICOLON, + [SDL_SCANCODE_APOSTROPHE] = Q_KEY_CODE_APOSTROPHE, + [SDL_SCANCODE_GRAVE] = Q_KEY_CODE_GRAVE_ACCENT, + [SDL_SCANCODE_COMMA] = Q_KEY_CODE_COMMA, + [SDL_SCANCODE_PERIOD] = Q_KEY_CODE_DOT, + [SDL_SCANCODE_SLASH] = Q_KEY_CODE_SLASH, + [SDL_SCANCODE_CAPSLOCK] = Q_KEY_CODE_CAPS_LOCK, + + [SDL_SCANCODE_F1] = Q_KEY_CODE_F1, + [SDL_SCANCODE_F2] = Q_KEY_CODE_F2, + [SDL_SCANCODE_F3] = Q_KEY_CODE_F3, + [SDL_SCANCODE_F4] = Q_KEY_CODE_F4, + [SDL_SCANCODE_F5] = Q_KEY_CODE_F5, + [SDL_SCANCODE_F6] = Q_KEY_CODE_F6, + [SDL_SCANCODE_F7] = Q_KEY_CODE_F7, + [SDL_SCANCODE_F8] = Q_KEY_CODE_F8, + [SDL_SCANCODE_F9] = Q_KEY_CODE_F9, + [SDL_SCANCODE_F10] = Q_KEY_CODE_F10, + [SDL_SCANCODE_F11] = Q_KEY_CODE_F11, + [SDL_SCANCODE_F12] = Q_KEY_CODE_F12, + + [SDL_SCANCODE_PRINTSCREEN] = Q_KEY_CODE_PRINT, + [SDL_SCANCODE_SCROLLLOCK] = Q_KEY_CODE_SCROLL_LOCK, + [SDL_SCANCODE_PAUSE] = Q_KEY_CODE_PAUSE, + [SDL_SCANCODE_INSERT] = Q_KEY_CODE_INSERT, + [SDL_SCANCODE_HOME] = Q_KEY_CODE_HOME, + [SDL_SCANCODE_PAGEUP] = Q_KEY_CODE_PGUP, + [SDL_SCANCODE_DELETE] = Q_KEY_CODE_DELETE, + [SDL_SCANCODE_END] = Q_KEY_CODE_END, + [SDL_SCANCODE_PAGEDOWN] = Q_KEY_CODE_PGDN, + [SDL_SCANCODE_RIGHT] = Q_KEY_CODE_RIGHT, + [SDL_SCANCODE_LEFT] = Q_KEY_CODE_LEFT, + [SDL_SCANCODE_DOWN] = Q_KEY_CODE_DOWN, + [SDL_SCANCODE_UP] = Q_KEY_CODE_UP, + [SDL_SCANCODE_NUMLOCKCLEAR] = Q_KEY_CODE_NUM_LOCK, + + [SDL_SCANCODE_KP_DIVIDE] = Q_KEY_CODE_KP_DIVIDE, + [SDL_SCANCODE_KP_MULTIPLY] = Q_KEY_CODE_KP_MULTIPLY, + [SDL_SCANCODE_KP_MINUS] = Q_KEY_CODE_KP_SUBTRACT, + [SDL_SCANCODE_KP_PLUS] = Q_KEY_CODE_KP_ADD, + [SDL_SCANCODE_KP_ENTER] = Q_KEY_CODE_KP_ENTER, + [SDL_SCANCODE_KP_1] = Q_KEY_CODE_KP_1, + [SDL_SCANCODE_KP_2] = Q_KEY_CODE_KP_2, + [SDL_SCANCODE_KP_3] = Q_KEY_CODE_KP_3, + [SDL_SCANCODE_KP_4] = Q_KEY_CODE_KP_4, + [SDL_SCANCODE_KP_5] = Q_KEY_CODE_KP_5, + [SDL_SCANCODE_KP_6] = Q_KEY_CODE_KP_6, + [SDL_SCANCODE_KP_7] = Q_KEY_CODE_KP_7, + [SDL_SCANCODE_KP_8] = Q_KEY_CODE_KP_8, + [SDL_SCANCODE_KP_9] = Q_KEY_CODE_KP_9, + [SDL_SCANCODE_KP_0] = Q_KEY_CODE_KP_0, + [SDL_SCANCODE_KP_PERIOD] = Q_KEY_CODE_KP_DECIMAL, +#if 0 + [SDL_SCANCODE_NONUSBACKSLASH] = Q_KEY_CODE_NONUSBACKSLASH, + [SDL_SCANCODE_APPLICATION] = Q_KEY_CODE_APPLICATION, + [SDL_SCANCODE_POWER] = Q_KEY_CODE_POWER, + [SDL_SCANCODE_KP_EQUALS] = Q_KEY_CODE_KP_EQUALS, + + [SDL_SCANCODE_F13] = Q_KEY_CODE_F13, + [SDL_SCANCODE_F14] = Q_KEY_CODE_F14, + [SDL_SCANCODE_F15] = Q_KEY_CODE_F15, + [SDL_SCANCODE_F16] = Q_KEY_CODE_F16, + [SDL_SCANCODE_F17] = Q_KEY_CODE_F17, + [SDL_SCANCODE_F18] = Q_KEY_CODE_F18, + [SDL_SCANCODE_F19] = Q_KEY_CODE_F19, + [SDL_SCANCODE_F20] = Q_KEY_CODE_F20, + [SDL_SCANCODE_F21] = Q_KEY_CODE_F21, + [SDL_SCANCODE_F22] = Q_KEY_CODE_F22, + [SDL_SCANCODE_F23] = Q_KEY_CODE_F23, + [SDL_SCANCODE_F24] = Q_KEY_CODE_F24, + + [SDL_SCANCODE_EXECUTE] = Q_KEY_CODE_EXECUTE, +#endif + [SDL_SCANCODE_HELP] = Q_KEY_CODE_HELP, + [SDL_SCANCODE_MENU] = Q_KEY_CODE_MENU, +#if 0 + [SDL_SCANCODE_SELECT] = Q_KEY_CODE_SELECT, +#endif + [SDL_SCANCODE_STOP] = Q_KEY_CODE_STOP, + [SDL_SCANCODE_AGAIN] = Q_KEY_CODE_AGAIN, + [SDL_SCANCODE_UNDO] = Q_KEY_CODE_UNDO, + [SDL_SCANCODE_CUT] = Q_KEY_CODE_CUT, + [SDL_SCANCODE_COPY] = Q_KEY_CODE_COPY, + [SDL_SCANCODE_PASTE] = Q_KEY_CODE_PASTE, + [SDL_SCANCODE_FIND] = Q_KEY_CODE_FIND, +#if 0 + [SDL_SCANCODE_MUTE] = Q_KEY_CODE_MUTE, + [SDL_SCANCODE_VOLUMEUP] = Q_KEY_CODE_VOLUMEUP, + [SDL_SCANCODE_VOLUMEDOWN] = Q_KEY_CODE_VOLUMEDOWN, + + [SDL_SCANCODE_KP_COMMA] = Q_KEY_CODE_KP_COMMA, + [SDL_SCANCODE_KP_EQUALSAS400] = Q_KEY_CODE_KP_EQUALSAS400, + + [SDL_SCANCODE_INTERNATIONAL1] = Q_KEY_CODE_INTERNATIONAL1, + [SDL_SCANCODE_INTERNATIONAL2] = Q_KEY_CODE_INTERNATIONAL2, + [SDL_SCANCODE_INTERNATIONAL3] = Q_KEY_CODE_INTERNATIONAL3, + [SDL_SCANCODE_INTERNATIONAL4] = Q_KEY_CODE_INTERNATIONAL4, + [SDL_SCANCODE_INTERNATIONAL5] = Q_KEY_CODE_INTERNATIONAL5, + [SDL_SCANCODE_INTERNATIONAL6] = Q_KEY_CODE_INTERNATIONAL6, + [SDL_SCANCODE_INTERNATIONAL7] = Q_KEY_CODE_INTERNATIONAL7, + [SDL_SCANCODE_INTERNATIONAL8] = Q_KEY_CODE_INTERNATIONAL8, + [SDL_SCANCODE_INTERNATIONAL9] = Q_KEY_CODE_INTERNATIONAL9, + [SDL_SCANCODE_LANG1] = Q_KEY_CODE_LANG1, + [SDL_SCANCODE_LANG2] = Q_KEY_CODE_LANG2, + [SDL_SCANCODE_LANG3] = Q_KEY_CODE_LANG3, + [SDL_SCANCODE_LANG4] = Q_KEY_CODE_LANG4, + [SDL_SCANCODE_LANG5] = Q_KEY_CODE_LANG5, + [SDL_SCANCODE_LANG6] = Q_KEY_CODE_LANG6, + [SDL_SCANCODE_LANG7] = Q_KEY_CODE_LANG7, + [SDL_SCANCODE_LANG8] = Q_KEY_CODE_LANG8, + [SDL_SCANCODE_LANG9] = Q_KEY_CODE_LANG9, + [SDL_SCANCODE_ALTERASE] = Q_KEY_CODE_ALTERASE, +#endif + [SDL_SCANCODE_SYSREQ] = Q_KEY_CODE_SYSRQ, +#if 0 + [SDL_SCANCODE_CANCEL] = Q_KEY_CODE_CANCEL, + [SDL_SCANCODE_CLEAR] = Q_KEY_CODE_CLEAR, + [SDL_SCANCODE_PRIOR] = Q_KEY_CODE_PRIOR, + [SDL_SCANCODE_RETURN2] = Q_KEY_CODE_RETURN2, + [SDL_SCANCODE_SEPARATOR] = Q_KEY_CODE_SEPARATOR, + [SDL_SCANCODE_OUT] = Q_KEY_CODE_OUT, + [SDL_SCANCODE_OPER] = Q_KEY_CODE_OPER, + [SDL_SCANCODE_CLEARAGAIN] = Q_KEY_CODE_CLEARAGAIN, + [SDL_SCANCODE_CRSEL] = Q_KEY_CODE_CRSEL, + [SDL_SCANCODE_EXSEL] = Q_KEY_CODE_EXSEL, + [SDL_SCANCODE_KP_00] = Q_KEY_CODE_KP_00, + [SDL_SCANCODE_KP_000] = Q_KEY_CODE_KP_000, + [SDL_SCANCODE_THOUSANDSSEPARATOR] = Q_KEY_CODE_THOUSANDSSEPARATOR, + [SDL_SCANCODE_DECIMALSEPARATOR] = Q_KEY_CODE_DECIMALSEPARATOR, + [SDL_SCANCODE_CURRENCYUNIT] = Q_KEY_CODE_CURRENCYUNIT, + [SDL_SCANCODE_CURRENCYSUBUNIT] = Q_KEY_CODE_CURRENCYSUBUNIT, + [SDL_SCANCODE_KP_LEFTPAREN] = Q_KEY_CODE_KP_LEFTPAREN, + [SDL_SCANCODE_KP_RIGHTPAREN] = Q_KEY_CODE_KP_RIGHTPAREN, + [SDL_SCANCODE_KP_LEFTBRACE] = Q_KEY_CODE_KP_LEFTBRACE, + [SDL_SCANCODE_KP_RIGHTBRACE] = Q_KEY_CODE_KP_RIGHTBRACE, + [SDL_SCANCODE_KP_TAB] = Q_KEY_CODE_KP_TAB, + [SDL_SCANCODE_KP_BACKSPACE] = Q_KEY_CODE_KP_BACKSPACE, + [SDL_SCANCODE_KP_A] = Q_KEY_CODE_KP_A, + [SDL_SCANCODE_KP_B] = Q_KEY_CODE_KP_B, + [SDL_SCANCODE_KP_C] = Q_KEY_CODE_KP_C, + [SDL_SCANCODE_KP_D] = Q_KEY_CODE_KP_D, + [SDL_SCANCODE_KP_E] = Q_KEY_CODE_KP_E, + [SDL_SCANCODE_KP_F] = Q_KEY_CODE_KP_F, + [SDL_SCANCODE_KP_XOR] = Q_KEY_CODE_KP_XOR, + [SDL_SCANCODE_KP_POWER] = Q_KEY_CODE_KP_POWER, + [SDL_SCANCODE_KP_PERCENT] = Q_KEY_CODE_KP_PERCENT, + [SDL_SCANCODE_KP_LESS] = Q_KEY_CODE_KP_LESS, + [SDL_SCANCODE_KP_GREATER] = Q_KEY_CODE_KP_GREATER, + [SDL_SCANCODE_KP_AMPERSAND] = Q_KEY_CODE_KP_AMPERSAND, + [SDL_SCANCODE_KP_DBLAMPERSAND] = Q_KEY_CODE_KP_DBLAMPERSAND, + [SDL_SCANCODE_KP_VERTICALBAR] = Q_KEY_CODE_KP_VERTICALBAR, + [SDL_SCANCODE_KP_DBLVERTICALBAR] = Q_KEY_CODE_KP_DBLVERTICALBAR, + [SDL_SCANCODE_KP_COLON] = Q_KEY_CODE_KP_COLON, + [SDL_SCANCODE_KP_HASH] = Q_KEY_CODE_KP_HASH, + [SDL_SCANCODE_KP_SPACE] = Q_KEY_CODE_KP_SPACE, + [SDL_SCANCODE_KP_AT] = Q_KEY_CODE_KP_AT, + [SDL_SCANCODE_KP_EXCLAM] = Q_KEY_CODE_KP_EXCLAM, + [SDL_SCANCODE_KP_MEMSTORE] = Q_KEY_CODE_KP_MEMSTORE, + [SDL_SCANCODE_KP_MEMRECALL] = Q_KEY_CODE_KP_MEMRECALL, + [SDL_SCANCODE_KP_MEMCLEAR] = Q_KEY_CODE_KP_MEMCLEAR, + [SDL_SCANCODE_KP_MEMADD] = Q_KEY_CODE_KP_MEMADD, + [SDL_SCANCODE_KP_MEMSUBTRACT] = Q_KEY_CODE_KP_MEMSUBTRACT, + [SDL_SCANCODE_KP_MEMMULTIPLY] = Q_KEY_CODE_KP_MEMMULTIPLY, + [SDL_SCANCODE_KP_MEMDIVIDE] = Q_KEY_CODE_KP_MEMDIVIDE, + [SDL_SCANCODE_KP_PLUSMINUS] = Q_KEY_CODE_KP_PLUSMINUS, + [SDL_SCANCODE_KP_CLEAR] = Q_KEY_CODE_KP_CLEAR, + [SDL_SCANCODE_KP_CLEARENTRY] = Q_KEY_CODE_KP_CLEARENTRY, + [SDL_SCANCODE_KP_BINARY] = Q_KEY_CODE_KP_BINARY, + [SDL_SCANCODE_KP_OCTAL] = Q_KEY_CODE_KP_OCTAL, + [SDL_SCANCODE_KP_DECIMAL] = Q_KEY_CODE_KP_DECIMAL, + [SDL_SCANCODE_KP_HEXADECIMAL] = Q_KEY_CODE_KP_HEXADECIMAL, +#endif + [SDL_SCANCODE_LCTRL] = Q_KEY_CODE_CTRL, + [SDL_SCANCODE_LSHIFT] = Q_KEY_CODE_SHIFT, + [SDL_SCANCODE_LALT] = Q_KEY_CODE_ALT, + [SDL_SCANCODE_LGUI] = Q_KEY_CODE_META_L, + [SDL_SCANCODE_RCTRL] = Q_KEY_CODE_CTRL_R, + [SDL_SCANCODE_RSHIFT] = Q_KEY_CODE_SHIFT_R, + [SDL_SCANCODE_RALT] = Q_KEY_CODE_ALTGR, + [SDL_SCANCODE_RGUI] = Q_KEY_CODE_META_R, +#if 0 + [SDL_SCANCODE_MODE] = Q_KEY_CODE_MODE, + [SDL_SCANCODE_AUDIONEXT] = Q_KEY_CODE_AUDIONEXT, + [SDL_SCANCODE_AUDIOPREV] = Q_KEY_CODE_AUDIOPREV, + [SDL_SCANCODE_AUDIOSTOP] = Q_KEY_CODE_AUDIOSTOP, + [SDL_SCANCODE_AUDIOPLAY] = Q_KEY_CODE_AUDIOPLAY, + [SDL_SCANCODE_AUDIOMUTE] = Q_KEY_CODE_AUDIOMUTE, + [SDL_SCANCODE_MEDIASELECT] = Q_KEY_CODE_MEDIASELECT, + [SDL_SCANCODE_WWW] = Q_KEY_CODE_WWW, + [SDL_SCANCODE_MAIL] = Q_KEY_CODE_MAIL, + [SDL_SCANCODE_CALCULATOR] = Q_KEY_CODE_CALCULATOR, + [SDL_SCANCODE_COMPUTER] = Q_KEY_CODE_COMPUTER, + [SDL_SCANCODE_AC_SEARCH] = Q_KEY_CODE_AC_SEARCH, + [SDL_SCANCODE_AC_HOME] = Q_KEY_CODE_AC_HOME, + [SDL_SCANCODE_AC_BACK] = Q_KEY_CODE_AC_BACK, + [SDL_SCANCODE_AC_FORWARD] = Q_KEY_CODE_AC_FORWARD, + [SDL_SCANCODE_AC_STOP] = Q_KEY_CODE_AC_STOP, + [SDL_SCANCODE_AC_REFRESH] = Q_KEY_CODE_AC_REFRESH, + [SDL_SCANCODE_AC_BOOKMARKS] = Q_KEY_CODE_AC_BOOKMARKS, + [SDL_SCANCODE_BRIGHTNESSDOWN] = Q_KEY_CODE_BRIGHTNESSDOWN, + [SDL_SCANCODE_BRIGHTNESSUP] = Q_KEY_CODE_BRIGHTNESSUP, + [SDL_SCANCODE_DISPLAYSWITCH] = Q_KEY_CODE_DISPLAYSWITCH, + [SDL_SCANCODE_KBDILLUMTOGGLE] = Q_KEY_CODE_KBDILLUMTOGGLE, + [SDL_SCANCODE_KBDILLUMDOWN] = Q_KEY_CODE_KBDILLUMDOWN, + [SDL_SCANCODE_KBDILLUMUP] = Q_KEY_CODE_KBDILLUMUP, + [SDL_SCANCODE_EJECT] = Q_KEY_CODE_EJECT, + [SDL_SCANCODE_SLEEP] = Q_KEY_CODE_SLEEP, + [SDL_SCANCODE_APP1] = Q_KEY_CODE_APP1, + [SDL_SCANCODE_APP2] = Q_KEY_CODE_APP2, +#endif +}; diff --git a/ui/sdl2.c b/ui/sdl2.c new file mode 100644 index 0000000000..f1532e9d2c --- /dev/null +++ b/ui/sdl2.c @@ -0,0 +1,829 @@ +/* + * QEMU SDL display driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ + +/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ +#undef WIN32_LEAN_AND_MEAN + +#include <SDL.h> + +#if SDL_MAJOR_VERSION == 2 +#include <SDL_syswm.h> + +#include "qemu-common.h" +#include "ui/console.h" +#include "ui/input.h" +#include "sysemu/sysemu.h" +#include "sdl_zoom.h" + +#include "sdl2-keymap.h" + +static int sdl2_num_outputs; +static struct sdl2_state { + DisplayChangeListener dcl; + DisplaySurface *surface; + SDL_Texture *texture; + SDL_Window *real_window; + SDL_Renderer *real_renderer; + int idx; + int last_vm_running; /* per console for caption reasons */ + int x, y; +} *sdl2_console; + +static SDL_Surface *guest_sprite_surface; +static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ + +static bool gui_saved_scaling; +static int gui_saved_width; +static int gui_saved_height; +static int gui_saved_grab; +static int gui_fullscreen; +static int gui_noframe; +static int gui_key_modifier_pressed; +static int gui_keysym; +static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; +static uint8_t modifiers_state[SDL_NUM_SCANCODES]; +static SDL_Cursor *sdl_cursor_normal; +static SDL_Cursor *sdl_cursor_hidden; +static int absolute_enabled; +static int guest_cursor; +static int guest_x, guest_y; +static SDL_Cursor *guest_sprite; +static int scaling_active; +static Notifier mouse_mode_notifier; + +static void sdl_update_caption(struct sdl2_state *scon); + +static struct sdl2_state *get_scon_from_window(uint32_t window_id) +{ + int i; + for (i = 0; i < sdl2_num_outputs; i++) { + if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) { + return &sdl2_console[i]; + } + } + return NULL; +} + +static void sdl_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + SDL_Rect rect; + DisplaySurface *surf = qemu_console_surface(dcl->con); + + if (!surf) { + return; + } + if (!scon->texture) { + return; + } + + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + + SDL_UpdateTexture(scon->texture, NULL, surface_data(surf), + surface_stride(surf)); + SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect); + SDL_RenderPresent(scon->real_renderer); +} + +static void do_sdl_resize(struct sdl2_state *scon, int width, int height, + int bpp) +{ + int flags; + + if (scon->real_window && scon->real_renderer) { + if (width && height) { + SDL_RenderSetLogicalSize(scon->real_renderer, width, height); + SDL_SetWindowSize(scon->real_window, width, height); + } else { + SDL_DestroyRenderer(scon->real_renderer); + SDL_DestroyWindow(scon->real_window); + scon->real_renderer = NULL; + scon->real_window = NULL; + } + } else { + if (!width || !height) { + return; + } + flags = 0; + if (gui_fullscreen) { + flags |= SDL_WINDOW_FULLSCREEN; + } else { + flags |= SDL_WINDOW_RESIZABLE; + } + + scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, height, flags); + scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); + sdl_update_caption(scon); + } +} + +static void sdl_switch(DisplayChangeListener *dcl, + DisplaySurface *new_surface) +{ + struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + int format = 0; + int idx = scon->idx; + DisplaySurface *old_surface = scon->surface; + + /* temporary hack: allows to call sdl_switch to handle scaling changes */ + if (new_surface) { + scon->surface = new_surface; + } + + if (!new_surface && idx > 0) { + scon->surface = NULL; + } + + if (new_surface == NULL) { + do_sdl_resize(scon, 0, 0, 0); + } else { + do_sdl_resize(scon, surface_width(scon->surface), + surface_height(scon->surface), 0); + } + + if (old_surface && scon->texture) { + SDL_DestroyTexture(scon->texture); + scon->texture = NULL; + } + + if (new_surface) { + if (!scon->texture) { + if (surface_bits_per_pixel(scon->surface) == 16) { + format = SDL_PIXELFORMAT_RGB565; + } else if (surface_bits_per_pixel(scon->surface) == 32) { + format = SDL_PIXELFORMAT_ARGB8888; + } + + scon->texture = SDL_CreateTexture(scon->real_renderer, format, + SDL_TEXTUREACCESS_STREAMING, + surface_width(new_surface), + surface_height(new_surface)); + } + } +} + +static void reset_keys(void) +{ + int i; + + for (i = 0; i < 256; i++) { + if (modifiers_state[i]) { + int qcode = sdl2_scancode_to_qcode[i]; + qemu_input_event_send_key_qcode(NULL, qcode, false); + modifiers_state[i] = 0; + } + } +} + +static void sdl_process_key(SDL_KeyboardEvent *ev) +{ + int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; + + switch (ev->keysym.scancode) { +#if 0 + case SDL_SCANCODE_NUMLOCKCLEAR: + case SDL_SCANCODE_CAPSLOCK: + /* SDL does not send the key up event, so we generate it */ + qemu_input_event_send_key_qcode(NULL, qcode, true); + qemu_input_event_send_key_qcode(NULL, qcode, false); + return; +#endif + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_LGUI: + case SDL_SCANCODE_RCTRL: + case SDL_SCANCODE_RSHIFT: + case SDL_SCANCODE_RALT: + case SDL_SCANCODE_RGUI: + if (ev->type == SDL_KEYUP) { + modifiers_state[ev->keysym.scancode] = 0; + } else { + modifiers_state[ev->keysym.scancode] = 1; + } + /* fall though */ + default: + qemu_input_event_send_key_qcode(NULL, qcode, + ev->type == SDL_KEYDOWN); + } +} + +static void sdl_update_caption(struct sdl2_state *scon) +{ + char win_title[1024]; + char icon_title[1024]; + const char *status = ""; + + if (!runstate_is_running()) { + status = " [Stopped]"; + } else if (gui_grab) { + if (alt_grab) { + status = " - Press Ctrl-Alt-Shift to exit mouse grab"; + } else if (ctrl_grab) { + status = " - Press Right-Ctrl to exit mouse grab"; + } else { + status = " - Press Ctrl-Alt to exit mouse grab"; + } + } + + if (qemu_name) { + snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name, + scon->idx, status); + snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); + } else { + snprintf(win_title, sizeof(win_title), "QEMU%s", status); + snprintf(icon_title, sizeof(icon_title), "QEMU"); + } + + if (scon->real_window) { + SDL_SetWindowTitle(scon->real_window, win_title); + } +} + +static void sdl_hide_cursor(void) +{ + if (!cursor_hide) { + return; + } + + if (qemu_input_is_absolute()) { + SDL_ShowCursor(1); + SDL_SetCursor(sdl_cursor_hidden); + } else { + SDL_ShowCursor(0); + } +} + +static void sdl_show_cursor(void) +{ + if (!cursor_hide) { + return; + } + + if (!qemu_input_is_absolute()) { + SDL_ShowCursor(1); + if (guest_cursor && + (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { + SDL_SetCursor(guest_sprite); + } else { + SDL_SetCursor(sdl_cursor_normal); + } + } +} + +static void sdl_grab_start(struct sdl2_state *scon) +{ + /* + * If the application is not active, do not try to enter grab state. This + * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the + * application (SDL bug). + */ + if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) { + return; + } + if (guest_cursor) { + SDL_SetCursor(guest_sprite); + if (!qemu_input_is_absolute() && !absolute_enabled) { + SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); + } + } else { + sdl_hide_cursor(); + } + SDL_SetWindowGrab(scon->real_window, SDL_TRUE); + gui_grab = 1; + sdl_update_caption(scon); +} + +static void sdl_grab_end(struct sdl2_state *scon) +{ + SDL_SetWindowGrab(scon->real_window, SDL_FALSE); + gui_grab = 0; + sdl_show_cursor(); + sdl_update_caption(scon); +} + +static void absolute_mouse_grab(struct sdl2_state *scon) +{ + int mouse_x, mouse_y; + int scr_w, scr_h; + SDL_GetMouseState(&mouse_x, &mouse_y); + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + if (mouse_x > 0 && mouse_x < scr_w - 1 && + mouse_y > 0 && mouse_y < scr_h - 1) { + sdl_grab_start(scon); + } +} + +static void sdl_mouse_mode_change(Notifier *notify, void *data) +{ + if (qemu_input_is_absolute()) { + if (!absolute_enabled) { + absolute_enabled = 1; + absolute_mouse_grab(&sdl2_console[0]); + } + } else if (absolute_enabled) { + if (!gui_fullscreen) { + sdl_grab_end(&sdl2_console[0]); + } + absolute_enabled = 0; + } +} + +static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy, + int dz, int x, int y, int state) +{ + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT), + [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE), + [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT), +#if 0 + [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP), + [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN), +#endif + }; + static uint32_t prev_state; + + if (prev_state != state) { + qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state); + prev_state = state; + } + + if (qemu_input_is_absolute()) { + int scr_w, scr_h; + int max_w = 0, max_h = 0; + int off_x = 0, off_y = 0; + int cur_off_x = 0, cur_off_y = 0; + int i; + + for (i = 0; i < sdl2_num_outputs; i++) { + struct sdl2_state *thiscon = &sdl2_console[i]; + if (thiscon->real_window && thiscon->surface) { + SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h); + cur_off_x = thiscon->x; + cur_off_y = thiscon->y; + if (scr_w + cur_off_x > max_w) { + max_w = scr_w + cur_off_x; + } + if (scr_h + cur_off_y > max_h) { + max_h = scr_h + cur_off_y; + } + if (i == scon->idx) { + off_x = cur_off_x; + off_y = cur_off_y; + } + } + } + qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w); + qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h); + } else if (guest_cursor) { + x -= guest_x; + y -= guest_y; + guest_x += x; + guest_y += y; + qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, x); + qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, y); + } + qemu_input_event_sync(); +} + +static void sdl_scale(struct sdl2_state *scon, int width, int height) +{ + int bpp = 0; + do_sdl_resize(scon, width, height, bpp); + scaling_active = 1; +} + +static void toggle_full_screen(struct sdl2_state *scon) +{ + int width = surface_width(scon->surface); + int height = surface_height(scon->surface); + int bpp = surface_bits_per_pixel(scon->surface); + + gui_fullscreen = !gui_fullscreen; + if (gui_fullscreen) { + SDL_GetWindowSize(scon->real_window, + &gui_saved_width, &gui_saved_height); + gui_saved_scaling = scaling_active; + + do_sdl_resize(scon, width, height, bpp); + scaling_active = 0; + + gui_saved_grab = gui_grab; + sdl_grab_start(scon); + } else { + if (gui_saved_scaling) { + sdl_scale(scon, gui_saved_width, gui_saved_height); + } else { + do_sdl_resize(scon, width, height, 0); + } + if (!gui_saved_grab) { + sdl_grab_end(scon); + } + } + graphic_hw_invalidate(scon->dcl.con); + graphic_hw_update(scon->dcl.con); +} + +static void handle_keydown(SDL_Event *ev) +{ + int mod_state; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + + if (alt_grab) { + mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == + (gui_grab_code | KMOD_LSHIFT); + } else if (ctrl_grab) { + mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; + } else { + mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code; + } + gui_key_modifier_pressed = mod_state; + + if (gui_key_modifier_pressed) { + switch (ev->key.keysym.scancode) { + case SDL_SCANCODE_F: + toggle_full_screen(scon); + gui_keysym = 1; + break; + case SDL_SCANCODE_U: + if (scaling_active) { + scaling_active = 0; + sdl_switch(&scon->dcl, NULL); + graphic_hw_invalidate(scon->dcl.con); + graphic_hw_update(scon->dcl.con); + } + gui_keysym = 1; + break; + case SDL_SCANCODE_KP_PLUS: + case SDL_SCANCODE_KP_MINUS: + if (!gui_fullscreen) { + int scr_w, scr_h; + int width, height; + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + + width = MAX(scr_w + (ev->key.keysym.scancode == + SDL_SCANCODE_KP_PLUS ? 50 : -50), + 160); + height = (surface_height(scon->surface) * width) / + surface_width(scon->surface); + + sdl_scale(scon, width, height); + graphic_hw_invalidate(NULL); + graphic_hw_update(NULL); + gui_keysym = 1; + } + default: + break; + } + } + if (!gui_keysym) { + sdl_process_key(&ev->key); + } +} + +static void handle_keyup(SDL_Event *ev) +{ + int mod_state; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + + if (!alt_grab) { + mod_state = (ev->key.keysym.mod & gui_grab_code); + } else { + mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT)); + } + if (!mod_state && gui_key_modifier_pressed) { + gui_key_modifier_pressed = 0; + if (gui_keysym == 0) { + /* exit/enter grab if pressing Ctrl-Alt */ + if (!gui_grab) { + sdl_grab_start(scon); + } else if (!gui_fullscreen) { + sdl_grab_end(scon); + } + /* SDL does not send back all the modifiers key, so we must + * correct it. */ + reset_keys(); + return; + } + gui_keysym = 0; + } + if (!gui_keysym) { + sdl_process_key(&ev->key); + } +} + +static void handle_mousemotion(SDL_Event *ev) +{ + int max_x, max_y; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + + if (qemu_input_is_absolute() || absolute_enabled) { + int scr_w, scr_h; + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + max_x = scr_w - 1; + max_y = scr_h - 1; + if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 || + ev->motion.x == max_x || ev->motion.y == max_y)) { + sdl_grab_end(scon); + } + if (!gui_grab && + (ev->motion.x > 0 && ev->motion.x < max_x && + ev->motion.y > 0 && ev->motion.y < max_y)) { + sdl_grab_start(scon); + } + } + if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, 0, + ev->motion.x, ev->motion.y, ev->motion.state); + } +} + +static void handle_mousebutton(SDL_Event *ev) +{ + int buttonstate = SDL_GetMouseState(NULL, NULL); + SDL_MouseButtonEvent *bev; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + int dz; + + bev = &ev->button; + if (!gui_grab && !qemu_input_is_absolute()) { + if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { + /* start grabbing all events */ + sdl_grab_start(scon); + } + } else { + dz = 0; + if (ev->type == SDL_MOUSEBUTTONDOWN) { + buttonstate |= SDL_BUTTON(bev->button); + } else { + buttonstate &= ~SDL_BUTTON(bev->button); + } +#ifdef SDL_BUTTON_WHEELUP + if (bev->button == SDL_BUTTON_WHEELUP && + ev->type == SDL_MOUSEBUTTONDOWN) { + dz = -1; + } else if (bev->button == SDL_BUTTON_WHEELDOWN && + ev->type == SDL_MOUSEBUTTONDOWN) { + dz = 1; + } +#endif + sdl_send_mouse_event(scon, 0, 0, dz, bev->x, bev->y, buttonstate); + } +} + +static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) +{ + int w, h; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + + switch (ev->window.event) { + case SDL_WINDOWEVENT_RESIZED: + sdl_scale(scon, ev->window.data1, ev->window.data2); + graphic_hw_invalidate(scon->dcl.con); + graphic_hw_update(scon->dcl.con); + break; + case SDL_WINDOWEVENT_EXPOSED: + SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h); + sdl_update(dcl, 0, 0, w, h); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_ENTER: + if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) { + absolute_mouse_grab(scon); + } + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + if (gui_grab && !gui_fullscreen) { + sdl_grab_end(scon); + } + break; + case SDL_WINDOWEVENT_RESTORED: + update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT); + break; + case SDL_WINDOWEVENT_MINIMIZED: + update_displaychangelistener(dcl, 500); + break; + case SDL_WINDOWEVENT_CLOSE: + if (!no_quit) { + no_shutdown = 0; + qemu_system_shutdown_request(); + } + break; + } +} + +static void sdl_refresh(DisplayChangeListener *dcl) +{ + struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + SDL_Event ev1, *ev = &ev1; + + if (scon->last_vm_running != runstate_is_running()) { + scon->last_vm_running = runstate_is_running(); + sdl_update_caption(scon); + } + + graphic_hw_update(dcl->con); + + while (SDL_PollEvent(ev)) { + switch (ev->type) { + case SDL_KEYDOWN: + handle_keydown(ev); + break; + case SDL_KEYUP: + handle_keyup(ev); + break; + case SDL_QUIT: + if (!no_quit) { + no_shutdown = 0; + qemu_system_shutdown_request(); + } + break; + case SDL_MOUSEMOTION: + handle_mousemotion(ev); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + handle_mousebutton(ev); + break; + case SDL_WINDOWEVENT: + handle_windowevent(dcl, ev); + break; + default: + break; + } + } +} + +static void sdl_mouse_warp(DisplayChangeListener *dcl, + int x, int y, int on) +{ + struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + if (on) { + if (!guest_cursor) { + sdl_show_cursor(); + } + if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + SDL_SetCursor(guest_sprite); + if (!qemu_input_is_absolute() && !absolute_enabled) { + SDL_WarpMouseInWindow(scon->real_window, x, y); + } + } + } else if (gui_grab) { + sdl_hide_cursor(); + } + guest_cursor = on; + guest_x = x, guest_y = y; +} + +static void sdl_mouse_define(DisplayChangeListener *dcl, + QEMUCursor *c) +{ + + if (guest_sprite) { + SDL_FreeCursor(guest_sprite); + } + + if (guest_sprite_surface) { + SDL_FreeSurface(guest_sprite_surface); + } + + guest_sprite_surface = + SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4, + 0xff0000, 0x00ff00, 0xff, 0xff000000); + + if (!guest_sprite_surface) { + fprintf(stderr, "Failed to make rgb surface from %p\n", c); + return; + } + guest_sprite = SDL_CreateColorCursor(guest_sprite_surface, + c->hot_x, c->hot_y); + if (!guest_sprite) { + fprintf(stderr, "Failed to make color cursor from %p\n", c); + return; + } + if (guest_cursor && + (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { + SDL_SetCursor(guest_sprite); + } +} + +static void sdl_cleanup(void) +{ + if (guest_sprite) { + SDL_FreeCursor(guest_sprite); + } + SDL_QuitSubSystem(SDL_INIT_VIDEO); +} + +static const DisplayChangeListenerOps dcl_ops = { + .dpy_name = "sdl", + .dpy_gfx_update = sdl_update, + .dpy_gfx_switch = sdl_switch, + .dpy_refresh = sdl_refresh, + .dpy_mouse_set = sdl_mouse_warp, + .dpy_cursor_define = sdl_mouse_define, +}; + +void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) +{ + int flags; + uint8_t data = 0; + char *filename; + int i; + + if (no_frame) { + gui_noframe = 1; + } + +#ifdef __linux__ + /* on Linux, SDL may use fbcon|directfb|svgalib when run without + * accessible $DISPLAY to open X11 window. This is often the case + * when qemu is run using sudo. But in this case, and when actually + * run in X11 environment, SDL fights with X11 for the video card, + * making current display unavailable, often until reboot. + * So make x11 the default SDL video driver if this variable is unset. + * This is a bit hackish but saves us from bigger problem. + * Maybe it's a good idea to fix this in SDL instead. + */ + setenv("SDL_VIDEODRIVER", "x11", 0); +#endif + + flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; + if (SDL_Init(flags)) { + fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", + SDL_GetError()); + exit(1); + } + + for (i = 0;; i++) { + QemuConsole *con = qemu_console_lookup_by_index(i); + if (!con || !qemu_console_is_graphic(con)) { + break; + } + } + sdl2_num_outputs = i; + sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs); + for (i = 0; i < sdl2_num_outputs; i++) { + QemuConsole *con = qemu_console_lookup_by_index(i); + sdl2_console[i].dcl.ops = &dcl_ops; + sdl2_console[i].dcl.con = con; + register_displaychangelistener(&sdl2_console[i].dcl); + sdl2_console[i].idx = i; + } + + /* Load a 32x32x4 image. White pixels are transparent. */ + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); + if (filename) { + SDL_Surface *image = SDL_LoadBMP(filename); + if (image) { + uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); + SDL_SetColorKey(image, SDL_TRUE, colorkey); + SDL_SetWindowIcon(sdl2_console[0].real_window, image); + } + g_free(filename); + } + + if (full_screen) { + gui_fullscreen = 1; + sdl_grab_start(0); + } + + mouse_mode_notifier.notify = sdl_mouse_mode_change; + qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); + + gui_grab = 0; + + sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); + sdl_cursor_normal = SDL_GetCursor(); + + atexit(sdl_cleanup); +} +#endif diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h index ee904805da..599d9fc64d 100644 --- a/ui/sdl_keysym.h +++ b/ui/sdl_keysym.h @@ -200,6 +200,7 @@ static const name2keysym_t name2keysym[]={ { "yacute", 0x0fd}, { "thorn", 0x0fe}, { "ydiaeresis", 0x0ff}, +#if SDL_MAJOR_VERSION == 1 {"EuroSign", SDLK_EURO}, /* modifiers */ @@ -272,6 +273,6 @@ static const name2keysym_t name2keysym[]={ {"Num_Lock", SDLK_NUMLOCK}, {"Pause", SDLK_PAUSE}, {"Escape", SDLK_ESCAPE}, - +#endif {NULL, 0}, }; diff --git a/ui/spice-input.c b/ui/spice-input.c index 3beb8deadb..6dab23b75c 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -26,12 +26,15 @@ #include "qemu-common.h" #include "ui/qemu-spice.h" #include "ui/console.h" +#include "ui/keymaps.h" +#include "ui/input.h" /* keyboard bits */ typedef struct QemuSpiceKbd { SpiceKbdInstance sin; int ledstate; + bool emul0; } QemuSpiceKbd; static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag); @@ -47,9 +50,24 @@ static const SpiceKbdInterface kbd_interface = { .get_leds = kbd_get_leds, }; -static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) +static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode) { - kbd_put_keycode(frag); + QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); + int keycode; + bool up; + + if (scancode == SCANCODE_EMUL0) { + kbd->emul0 = true; + return; + } + keycode = scancode & ~SCANCODE_UP; + up = scancode & SCANCODE_UP; + if (kbd->emul0) { + kbd->emul0 = false; + keycode |= SCANCODE_GREY; + } + + qemu_input_event_send_key_number(NULL, keycode, !up); } static uint8_t kbd_get_leds(SpiceKbdInstance *sin) @@ -80,41 +98,52 @@ static void kbd_leds(void *opaque, int ledstate) typedef struct QemuSpicePointer { SpiceMouseInstance mouse; SpiceTabletInstance tablet; - int width, height, x, y; + int width, height; + uint32_t last_bmask; Notifier mouse_mode; bool absolute; } QemuSpicePointer; -static int map_buttons(int spice_buttons) +static void spice_update_buttons(QemuSpicePointer *pointer, + int wheel, uint32_t button_mask) { - int qemu_buttons = 0; - - /* - * Note: SPICE_MOUSE_BUTTON_* specifies the wire protocol but this - * isn't what we get passed in via interface callbacks for the - * middle and right button ... - */ - if (spice_buttons & SPICE_MOUSE_BUTTON_MASK_LEFT) { - qemu_buttons |= MOUSE_EVENT_LBUTTON; + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = 0x01, + [INPUT_BUTTON_MIDDLE] = 0x04, + [INPUT_BUTTON_RIGHT] = 0x02, + [INPUT_BUTTON_WHEEL_UP] = 0x10, + [INPUT_BUTTON_WHEEL_DOWN] = 0x20, + }; + + if (wheel < 0) { + button_mask |= 0x10; } - if (spice_buttons & 0x04 /* SPICE_MOUSE_BUTTON_MASK_MIDDLE */) { - qemu_buttons |= MOUSE_EVENT_MBUTTON; + if (wheel > 0) { + button_mask |= 0x20; } - if (spice_buttons & 0x02 /* SPICE_MOUSE_BUTTON_MASK_RIGHT */) { - qemu_buttons |= MOUSE_EVENT_RBUTTON; + + if (pointer->last_bmask == button_mask) { + return; } - return qemu_buttons; + qemu_input_update_buttons(NULL, bmap, pointer->last_bmask, button_mask); + pointer->last_bmask = button_mask; } static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz, uint32_t buttons_state) { - kbd_mouse_event(dx, dy, dz, map_buttons(buttons_state)); + QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); + spice_update_buttons(pointer, dz, buttons_state); + qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); + qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); + qemu_input_event_sync(); } static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state) { - kbd_mouse_event(0, 0, 0, map_buttons(buttons_state)); + QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); + spice_update_buttons(pointer, 0, buttons_state); + qemu_input_event_sync(); } static const SpiceMouseInterface mouse_interface = { @@ -145,9 +174,10 @@ static void tablet_position(SpiceTabletInstance* sin, int x, int y, { QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); - pointer->x = x * 0x7FFF / (pointer->width - 1); - pointer->y = y * 0x7FFF / (pointer->height - 1); - kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state)); + spice_update_buttons(pointer, 0, buttons_state); + qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, pointer->width); + qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, pointer->width); + qemu_input_event_sync(); } @@ -156,7 +186,8 @@ static void tablet_wheel(SpiceTabletInstance* sin, int wheel, { QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); - kbd_mouse_event(pointer->x, pointer->y, wheel, map_buttons(buttons_state)); + spice_update_buttons(pointer, wheel, buttons_state); + qemu_input_event_sync(); } static void tablet_buttons(SpiceTabletInstance *sin, @@ -164,7 +195,8 @@ static void tablet_buttons(SpiceTabletInstance *sin, { QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); - kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state)); + spice_update_buttons(pointer, 0, buttons_state); + qemu_input_event_sync(); } static const SpiceTabletInterface tablet_interface = { @@ -181,7 +213,7 @@ static const SpiceTabletInterface tablet_interface = { static void mouse_mode_notifier(Notifier *notifier, void *data) { QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); - bool is_absolute = kbd_mouse_is_absolute(); + bool is_absolute = qemu_input_is_absolute(); if (pointer->absolute == is_absolute) { return; @@ -33,6 +33,7 @@ #include "qapi/qmp/types.h" #include "qmp-commands.h" #include "qemu/osdep.h" +#include "ui/input.h" #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 @@ -1483,7 +1484,7 @@ static void client_cut_text(VncState *vs, size_t len, uint8_t *text) static void check_pointer_type_change(Notifier *notifier, void *data) { VncState *vs = container_of(notifier, VncState, mouse_mode_notifier); - int absolute = kbd_mouse_is_absolute(); + int absolute = qemu_input_is_absolute(); if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) { vnc_lock_output(vs); @@ -1502,39 +1503,37 @@ static void check_pointer_type_change(Notifier *notifier, void *data) static void pointer_event(VncState *vs, int button_mask, int x, int y) { - int buttons = 0; - int dz = 0; + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = 0x01, + [INPUT_BUTTON_MIDDLE] = 0x02, + [INPUT_BUTTON_RIGHT] = 0x04, + [INPUT_BUTTON_WHEEL_UP] = 0x08, + [INPUT_BUTTON_WHEEL_DOWN] = 0x10, + }; + QemuConsole *con = vs->vd->dcl.con; int width = surface_width(vs->vd->ds); int height = surface_height(vs->vd->ds); - if (button_mask & 0x01) - buttons |= MOUSE_EVENT_LBUTTON; - if (button_mask & 0x02) - buttons |= MOUSE_EVENT_MBUTTON; - if (button_mask & 0x04) - buttons |= MOUSE_EVENT_RBUTTON; - if (button_mask & 0x08) - dz = -1; - if (button_mask & 0x10) - dz = 1; + if (vs->last_bmask != button_mask) { + qemu_input_update_buttons(con, bmap, vs->last_bmask, button_mask); + vs->last_bmask = button_mask; + } if (vs->absolute) { - kbd_mouse_event(width > 1 ? x * 0x7FFF / (width - 1) : 0x4000, - height > 1 ? y * 0x7FFF / (height - 1) : 0x4000, - dz, buttons); + qemu_input_queue_abs(con, INPUT_AXIS_X, x, width); + qemu_input_queue_abs(con, INPUT_AXIS_Y, y, height); } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) { - x -= 0x7FFF; - y -= 0x7FFF; - - kbd_mouse_event(x, y, dz, buttons); + qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF); + qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF); } else { - if (vs->last_x != -1) - kbd_mouse_event(x - vs->last_x, - y - vs->last_y, - dz, buttons); + if (vs->last_x != -1) { + qemu_input_queue_rel(con, INPUT_AXIS_X, x - vs->last_x); + qemu_input_queue_rel(con, INPUT_AXIS_Y, y - vs->last_y); + } vs->last_x = x; vs->last_y = y; } + qemu_input_event_sync(); } static void reset_keys(VncState *vs) @@ -1542,9 +1541,7 @@ static void reset_keys(VncState *vs) int i; for(i = 0; i < 256; i++) { if (vs->modifiers_state[i]) { - if (i & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(i | SCANCODE_UP); + qemu_input_event_send_key_number(vs->vd->dcl.con, i, false); vs->modifiers_state[i] = 0; } } @@ -1553,12 +1550,8 @@ static void reset_keys(VncState *vs) static void press_key(VncState *vs, int keysym) { int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK; - if (keycode & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); - if (keycode & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true); + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); } static int current_led_state(VncState *vs) @@ -1700,12 +1693,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } if (qemu_console_is_graphic(NULL)) { - if (keycode & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - if (down) - kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); - else - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down); } else { bool numlock = vs->modifiers_state[0x45]; bool control = (vs->modifiers_state[0x1d] || @@ -1826,10 +1814,7 @@ static void vnc_release_modifiers(VncState *vs) if (!vs->modifiers_state[keycode]) { continue; } - if (keycode & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); - } - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); } } @@ -257,6 +257,7 @@ struct VncState int absolute; int last_x; int last_y; + uint32_t last_bmask; int client_width; int client_height; VncShareMode share_mode; diff --git a/util/host-utils.c b/util/host-utils.c index f0784d6335..ee57ef55f6 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -86,4 +86,79 @@ void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) } *phigh = rh; } + +/* Unsigned 128x64 division. Returns 1 if overflow (divide by zero or */ +/* quotient exceeds 64 bits). Otherwise returns quotient via plow and */ +/* remainder via phigh. */ +int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) +{ + uint64_t dhi = *phigh; + uint64_t dlo = *plow; + unsigned i; + uint64_t carry = 0; + + if (divisor == 0) { + return 1; + } else if (dhi == 0) { + *plow = dlo / divisor; + *phigh = dlo % divisor; + return 0; + } else if (dhi > divisor) { + return 1; + } else { + + for (i = 0; i < 64; i++) { + carry = dhi >> 63; + dhi = (dhi << 1) | (dlo >> 63); + if (carry || (dhi >= divisor)) { + dhi -= divisor; + carry = 1; + } else { + carry = 0; + } + dlo = (dlo << 1) | carry; + } + + *plow = dlo; + *phigh = dhi; + return 0; + } +} + +int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) +{ + int sgn_dvdnd = *phigh < 0; + int sgn_divsr = divisor < 0; + int overflow = 0; + + if (sgn_dvdnd) { + *plow = ~(*plow); + *phigh = ~(*phigh); + if (*plow == (int64_t)-1) { + *plow = 0; + (*phigh)++; + } else { + (*plow)++; + } + } + + if (sgn_divsr) { + divisor = 0 - divisor; + } + + overflow = divu128((uint64_t *)plow, (uint64_t *)phigh, (uint64_t)divisor); + + if (sgn_dvdnd ^ sgn_divsr) { + *plow = 0 - *plow; + } + + if (!overflow) { + if ((*plow < 0) ^ (sgn_dvdnd ^ sgn_divsr)) { + overflow = 1; + } + } + + return overflow; +} + #endif /* !CONFIG_INT128 */ @@ -374,6 +374,10 @@ static QemuOptsList qemu_machine_opts = { .name = "firmware", .type = QEMU_OPT_STRING, .help = "firmware image", + },{ + .name = "kvm-type", + .type = QEMU_OPT_STRING, + .help = "Specifies the KVM virtualization mode (HV, PR)", }, { /* End of list */ } }, @@ -2578,7 +2582,7 @@ static QEMUMachine *machine_parse(const char *name) exit(!name || !is_help_option(name)); } -static int tcg_init(void) +static int tcg_init(QEMUMachine *machine) { tcg_exec_init(tcg_tb_size * 1024 * 1024); return 0; @@ -2588,7 +2592,7 @@ static struct { const char *opt_name; const char *name; int (*available)(void); - int (*init)(void); + int (*init)(QEMUMachine *); bool *allowed; } accel_list[] = { { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, @@ -2597,7 +2601,7 @@ static struct { { "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed }, }; -static int configure_accelerator(void) +static int configure_accelerator(QEMUMachine *machine) { const char *p; char buf[10]; @@ -2624,7 +2628,7 @@ static int configure_accelerator(void) continue; } *(accel_list[i].allowed) = true; - ret = accel_list[i].init(); + ret = accel_list[i].init(machine); if (ret < 0) { init_failed = true; fprintf(stderr, "failed to initialize %s: %s\n", @@ -4053,7 +4057,7 @@ int main(int argc, char **argv, char **envp) exit(0); } - configure_accelerator(); + configure_accelerator(machine); if (qtest_chrdev) { Error *local_err = NULL; @@ -1001,7 +1001,7 @@ static void xen_exit_notifier(Notifier *n, void *data) xs_daemon_close(state->xenstore); } -int xen_init(void) +int xen_init(QEMUMachine *machine) { xen_xc = xen_xc_interface_open(0, 0, 0); if (xen_xc == XC_HANDLER_INITIAL_VALUE) { diff --git a/xen-stub.c b/xen-stub.c index ad189a6df8..59927cb5d6 100644 --- a/xen-stub.c +++ b/xen-stub.c @@ -47,7 +47,7 @@ qemu_irq *xen_interrupt_controller_init(void) return NULL; } -int xen_init(void) +int xen_init(QEMUMachine *machine) { return -ENOSYS; } |