Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block patches

# gpg: Signature made Fri 07 Mar 2014 13:30:04 GMT using RSA key ID C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"

* remotes/kevin/tags/for-upstream:
  block: qemu-iotests 085 - live snapshots tests
  hw/ide/ahci.h: Avoid shifting left into sign bit
  block: Fix error path segfault in bdrv_open()
  qemu-iotests: Test a few blockdev-add error cases
  blockdev: Fix NULL pointer dereference in blockdev-add
  blockdev: Fail blockdev-add with encrypted images
  block/raw-win32: Strip "file:" prefix on creation
  block/raw-win32: Implement bdrv_parse_filename()
  block/raw-posix: Strip "file:" prefix on creation
  block/raw-posix: Implement bdrv_parse_filename()
  block: Keep "filename" option after parsing
  block: mirror - remove code cruft that has no function
  block: make bdrv_swap rebuild the bs graph node list field.
  block: Fix bs->request_alignment assertion for bs->sg=1
  iscsi: Use bs->sg for everything else than disks
  qemu-iotests: Test progress output for conversion
  qemu-img convert: Fix progress output
  gluster: Remove unused defines and header include
  gluster: Change licence to GPLv2+

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/Makefile b/Makefile
index ea6c712..bd9cd4f 100644
--- a/Makefile
+++ b/Makefile
@@ -399,7 +399,7 @@
 		$(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 1132899..665107f 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -566,7 +566,7 @@
     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 @@
         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))
diff --git a/configure b/configure
index 6ccadc3..8689435 100755
--- a/configure
+++ b/configure
@@ -14,13 +14,14 @@
 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 @@
     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 @@
            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 @@
 netmap="no"
 pixman=""
 sdl=""
+sdlabi="1.2"
 virtfs=""
 vnc="yes"
 sparse="no"
@@ -366,12 +396,13 @@
 }
 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 @@
   ;;
   --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 @@
   --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 @@
     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 @@
 
     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 @@
     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 @@
 
 # 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 $pkg_config sdl --exists; then
-  sdlconfig="$pkg_config sdl"
+if test "`basename $sdl_config`" != $sdlconfigname && ! has ${sdl_config}; then
+  sdl_config=$sdlconfigname
+fi
+
+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 e5f9d36..07c51ce 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -47,4 +47,5 @@
 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 cce7127..d10b5db 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -630,7 +630,7 @@
                           "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 e1c3f3a..b323c0c 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -284,12 +284,30 @@
 #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 @@
     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 @@
     }
 
     if (target_data_order != e_ident[EI_DATA]) {
+        ret = ELF_LOAD_WRONG_ENDIAN;
         goto fail;
     }
 
@@ -329,12 +350,9 @@
                          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 c0b857f..380976a 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -440,27 +440,33 @@
 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 4a466c8..55c0ddf 100644
--- a/hw/display/blizzard.c
+++ b/hw/display/blizzard.c
@@ -956,7 +956,7 @@
 
     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 6db8ca3..a042b9e 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -306,7 +306,7 @@
 
     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 3a8fc0b..0d3127d 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -2917,7 +2917,7 @@
     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 @@
      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 65cca1d..9750330 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1917,7 +1917,7 @@
     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 bc909bb..5c6a2d3 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -484,7 +484,7 @@
 {
     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 8407e6c..f9e7d7c 100644
--- a/hw/display/jazz_led.c
+++ b/hw/display/jazz_led.c
@@ -271,7 +271,7 @@
     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 5150cb4..603537a 100644
--- a/hw/display/milkymist-vgafb.c
+++ b/hw/display/milkymist-vgafb.c
@@ -290,7 +290,7 @@
             "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 c3b9b68..fda81ba 100644
--- a/hw/display/omap_lcdc.c
+++ b/hw/display/omap_lcdc.c
@@ -406,7 +406,7 @@
     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 ab689e9..c574cf1 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -464,7 +464,7 @@
     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 990931a..09cdf17 100644
--- a/hw/display/pxa2xx_lcd.c
+++ b/hw/display/pxa2xx_lcd.c
@@ -1013,7 +1013,7 @@
                           "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 2a559eb..47bbf1f 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -2069,7 +2069,7 @@
     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 @@
                            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 0b5f993..eedf2d4 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1449,5 +1449,5 @@
     }
 
     /* 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 89804e1..c2eea04 100644
--- a/hw/display/ssd0303.c
+++ b/hw/display/ssd0303.c
@@ -299,7 +299,7 @@
 {
     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 c3231c6..46c3b40 100644
--- a/hw/display/ssd0323.c
+++ b/hw/display/ssd0323.c
@@ -342,7 +342,7 @@
 
     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 3dd9b98..f4011d2 100644
--- a/hw/display/tc6393xb.c
+++ b/hw/display/tc6393xb.c
@@ -587,7 +587,7 @@
     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 e60769c..2b37ffa 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -602,14 +602,14 @@
                                  &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 8b514cc..afc46b8 100644
--- a/hw/display/vga-isa-mm.c
+++ b/hw/display/vga-isa-mm.c
@@ -135,7 +135,7 @@
     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 c2a19ad..1d9ea6b 100644
--- a/hw/display/vga-isa.c
+++ b/hw/display/vga-isa.c
@@ -67,7 +67,7 @@
                                         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 f74fc43..574ea0e 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -151,7 +151,7 @@
     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 334e718..bd2c108 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -1199,7 +1199,7 @@
     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 cb9d456..032eb7a 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -992,7 +992,7 @@
 
     /* 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 d5dc1ef..ae1699d 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -221,10 +221,16 @@
     } 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 c7f7b84..87fdb12 100644
--- a/hw/intc/openpic_kvm.c
+++ b/hw/intc/openpic_kvm.c
@@ -228,7 +228,7 @@
 
     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 b437563..64aabe7 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_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 @@
     }
 
     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 c203646..a5bbc24 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -65,7 +65,7 @@
     ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, &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 @@
     ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, &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 @@
         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 ef4f3a8..a87ca6d 100644
--- a/hw/moxie/moxiesim.c
+++ b/hw/moxie/moxiesim.c
@@ -55,7 +55,7 @@
                            &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 75e80c2..ea93293 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -32,3 +32,6 @@
 
 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 0000000..d4b4429
--- /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 0000000..78d2c57
--- /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 0000000..1931b74
--- /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 0000000..a7bbfa1
--- /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 0000000..7ad7686
--- /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 0000000..7760272
--- /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 1bd6f50..f6fbcb5 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -405,6 +405,8 @@
 
     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 b37ce9d..8a08752 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -238,6 +238,7 @@
        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 @@
             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 114be64..0e82719 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 @@
 
     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 93d02c1..bf46c38 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 @@
 
     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 @@
         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 @@
     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 @@
     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 @@
 
         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 @@
     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 @@
     .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 3ffcc65..d918780 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -40,6 +40,17 @@
     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 @@
     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 @@
 
     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 @@
                                 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 @@
     }
     *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 @@
     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 @@
     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 @@
     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 @@
     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 ef45f4f..d9fe946 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -243,6 +243,42 @@
     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 @@
 
     /* 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 4c7c3ae..cea9469 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -469,6 +469,8 @@
 
 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 @@
      * 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 @@
     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 1cb276d..73860d0 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -131,7 +131,7 @@
                                          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 @@
     }
 
     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 @@
                            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 @@
     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 85a0e53..ce8ea91 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -174,6 +174,19 @@
     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 @@
     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 @@
 
         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 75b04b4..7074d2b 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -116,6 +116,15 @@
     }
 }
 
+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 @@
     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 b536ab5..e9b4405 100644
--- a/hw/s390x/css.h
+++ b/hw/s390x/css.h
@@ -77,6 +77,7 @@
     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_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 04fb1a8..32d38a0 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -98,10 +98,10 @@
         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 f6e0e3e..a01801e 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 @@
     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 @@
     bool check_len;
     int len;
     hwaddr hw_len;
+    VirtioThinintInfo *thinint;
 
     if (!dev) {
         return -EINVAL;
@@ -428,6 +436,11 @@
             ret = -EINVAL;
             break;
         }
+        if (sch->thinint_active) {
+            /* Trigger a command reject. */
+            ret = -ENOSYS;
+            break;
+        }
         if (!ccw.cda) {
             ret = -EFAULT;
         } else {
@@ -480,6 +493,42 @@
             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 @@
     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 @@
     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 @@
         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 @@
         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 @@
     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 00932c7..4393e44 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 @@
     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 e8bca39..b3835c8 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_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 e05cbc1..42913b6 100644
--- a/hw/unicore32/puv3.c
+++ b/hw/unicore32/puv3.c
@@ -98,7 +98,7 @@
     }
 
     /* 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 2151460..c2096e6 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 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 @@
     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 acc701e..c6b5129 100644
--- a/include/hw/elf_ops.h
+++ b/include/hw/elf_ops.h
@@ -201,6 +201,7 @@
     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 @@
     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 @@
  fail:
     g_free(data);
     g_free(phdr);
-    return -1;
+    return ret;
 }
diff --git a/include/hw/loader.h b/include/hw/loader.h
index 91b0122..aaf08c3 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -15,6 +15,12 @@
 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 276b336..1ed0691 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -176,6 +176,8 @@
     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 e1f88bf..e181821 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 @@
 
 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 285c5fb..d4f21c9 100644
--- a/include/qemu/host-utils.h
+++ b/include/qemu/host-utils.h
@@ -44,9 +44,37 @@
     *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 a02d67c..ed01998 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 @@
 
 /* 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 0000000..4cefd56
--- /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 28f4875..e62281d 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 @@
 
 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 4156a87..08a38ea 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 @@
 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 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 @@
                                   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 @@
     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_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 @@
 
 /* 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 0000000..4976f3d
--- /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 */
diff --git a/kvm-all.c b/kvm-all.c
index fd8157a..87fe482 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -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 @@
     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 @@
     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 @@
         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 e979f76..4ef084e 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 @@
     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 9192977..be9491b 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -1492,7 +1492,7 @@
 {
     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 @@
             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 @@
                 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();
                 }
diff --git a/monitor.c b/monitor.c
index 3863d83..342e83b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -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 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 193e7e4..6c381b7 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' } }
diff --git a/qtest.c b/qtest.c
index ae941d6..0ac9f42 100644
--- a/qtest.c
+++ b/qtest.c
@@ -500,7 +500,7 @@
     }
 }
 
-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 c8e9018..a4d48a7 100644
--- a/target-ppc/STATUS
+++ b/target-ppc/STATUS
@@ -377,15 +377,6 @@
 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 7c9466f..f6c9b3a 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 49ba4a4..644a126 100644
--- a/target-ppc/cpu-models.h
+++ b/target-ppc/cpu-models.h
@@ -570,7 +570,6 @@
     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 72b2232..b17c024 100644
--- a/target-ppc/cpu-qom.h
+++ b/target-ppc/cpu-qom.h
@@ -79,6 +79,7 @@
 /**
  * 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 @@
     /*< 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 bb84767..afab267 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -352,6 +352,10 @@
     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 @@
     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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
     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 @@
 
 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 4f60218..e7f3295 100644
--- a/target-ppc/fpu_helper.c
+++ b/target-ppc/fpu_helper.c
@@ -50,6 +50,16 @@
     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 @@
 }
 
 /* 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 @@
     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 @@
         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 @@
         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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     return farg1.ll;
 }
 
-/* fctiw - fctiw. */
-uint64_t helper_fctiw(CPUPPCState *env, uint64_t arg)
-{
-    CPU_DoubleU farg;
 
-    farg.ll = arg;
+#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;                                                    \
+ }
 
-    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;
-}
+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)
+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
 
 #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;
+#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;                                        \
 }
 
-/* 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;
-
-    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_round_to_zero(farg.d, &env->fp_status);
-    }
-    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 @@
 
     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 @@
     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 @@
                      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 @@
                  (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 @@
                      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 @@
     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 @@
                      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 @@
                  (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 @@
                      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 @@
 
     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 @@
 
     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 @@
 
     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 @@
 
     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 @@
 
     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 @@
     }
 }
 
+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 @@
                  && (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 @@
             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 @@
     /* 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 6d282bb..99f10de 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -31,7 +31,11 @@
 
 #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 @@
 #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(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(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_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(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(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(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(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(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(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(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 e50bdd2..63dde94 100644
--- a/target-ppc/int_helper.c
+++ b/target-ppc/int_helper.c
@@ -41,6 +41,119 @@
 }
 #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 @@
 }
 #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 @@
 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 @@
     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 @@
 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 @@
 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 @@
     }
 }
 
-#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 @@
 }
 
 #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(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(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 @@
 #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(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 @@
     }
 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(&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(&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 7af3fe2..32e7a8c 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 @@
 
 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 @@
 
     ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &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 @@
 
     ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &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 @@
 #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 @@
             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,24 +1770,16 @@
     }
 }
 
-int kvmppc_fixup_cpu(PowerPCCPU *cpu)
-{
-    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;
-}
-
 bool kvmppc_has_cap_epr(void)
 {
     return cap_epr;
 }
 
+bool kvmppc_has_cap_htab_fd(void)
+{
+    return cap_htab_fd;
+}
+
 static int kvm_ppc_register_host_cpu_type(void)
 {
     TypeInfo type_info = {
@@ -1934,3 +1930,88 @@
 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 5f78e4b..ff077ec 100644
--- a/target-ppc/kvm_ppc.h
+++ b/target-ppc/kvm_ppc.h
@@ -36,13 +36,18 @@
 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 @@
 
 #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 @@
     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 @@
     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 12c174f..2d46cec 100644
--- a/target-ppc/machine.c
+++ b/target-ppc/machine.c
@@ -70,7 +70,9 @@
         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 @@
         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 616aab6..dc2ebfc 100644
--- a/target-ppc/misc_helper.c
+++ b/target-ppc/misc_helper.c
@@ -38,7 +38,9 @@
 
 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 67fc1b5..f2af4fb 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_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 @@
     /* 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 @@
                                      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 @@
             " 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 @@
                 " 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 @@
     }
 
     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 @@
 
     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 a8da558..1746b3e 100644
--- a/target-ppc/mmu-hash64.h
+++ b/target-ppc/mmu-hash64.h
@@ -9,6 +9,8 @@
 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 @@
 #define HPTE64_V_1TB_SEG        0x4000000000000000ULL
 #define HPTE64_V_VRMA_MASK      0x4001ffffff000000ULL
 
+
+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 target_ulong ppc_hash64_load_hpte0(CPUPPCState *env,
-                                                 hwaddr pte_offset)
+                                                 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) {
-        return  ldq_p(env->external_htab + pte_offset);
+        return  ldq_p((const void *)(uintptr_t)addr);
     } else {
-        return ldq_phys(cs->as, env->htab_base + pte_offset);
+        return ldq_phys(cs->as, addr);
     }
 }
 
 static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env,
-                                                 hwaddr pte_offset)
+                                                 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) {
-        return ldq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2);
+        return  ldq_p((const void *)(uintptr_t)addr);
     } else {
-        return ldq_phys(cs->as,
-                        env->htab_base + pte_offset + HASH_PTE_SIZE_64/2);
-    }
-}
-
-static inline void ppc_hash64_store_hpte0(CPUPPCState *env,
-                                          hwaddr pte_offset, target_ulong pte0)
-{
-    CPUState *cs = ENV_GET_CPU(env);
-    if (env->external_htab) {
-        stq_p(env->external_htab + pte_offset, pte0);
-    } else {
-        stq_phys(cs->as, env->htab_base + pte_offset, pte0);
-    }
-}
-
-static inline void ppc_hash64_store_hpte1(CPUPPCState *env,
-                                          hwaddr pte_offset, target_ulong pte1)
-{
-    CPUState *cs = ENV_GET_CPU(env);
-    if (env->external_htab) {
-        stq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2, pte1);
-    } 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 04a840b..8e2f8e7 100644
--- a/target-ppc/mmu_helper.c
+++ b/target-ppc/mmu_helper.c
@@ -2014,6 +2014,7 @@
 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 @@
                         " 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 c5c1108..91c33dc 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -387,6 +387,8 @@
 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 @@
     .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 @@
 /* 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 @@
 /* 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 @@
 #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 @@
 /***                     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 @@
 /* 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 @@
     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 @@
         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 @@
 /* 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 @@
     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 @@
     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 @@
     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
-    tcg_temp_free(t0);
+    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
+
+#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)
+LARX(ldarx, 8, ld64);
+
+/* lqarx */
+static void gen_lqarx(DisasContext *ctx)
 {
-    TCGv t0;
-    TCGv gpr = cpu_gpr[rD(ctx->opcode)];
+    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);
-    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);
+    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];
+    }
+    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. */
-static void gen_stdcx_(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, 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);
-    }
-#endif
-    tcg_temp_free(t0);
-}
+STCX(stdcx_, 8);
+STCX(stqcx_, 16);
 #endif /* defined(TARGET_PPC64) */
 
 /* sync */
@@ -3415,6 +3597,20 @@
     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 @@
 #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 @@
     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 @@
     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(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 @@
     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(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(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 @@
     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 @@
 
 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(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 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 @@
     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 @@
     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 @@
         return;
     }
 
-    if ((DM(ctx->opcode) & 2) == 0) {
-        tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(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_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)));
+        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 @@
 #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(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(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(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(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(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(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(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(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(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(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(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_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 @@
 #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(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(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(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_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(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 | 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(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(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 @@
     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 445c360..3eafbb0 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -628,6 +628,9 @@
     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 @@
     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 @@
  * 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 @@
                        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 @@
                  &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 @@
                  &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_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 @@
                  &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 @@
                  &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 @@
                      &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 @@
                  &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 @@
     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 @@
     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 @@
     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 @@
     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 @@
                    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 @@
         }
         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 @@
 
     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 613da49..5bbc67d 100644
--- a/target-s390x/ioinst.h
+++ b/target-s390x/ioinst.h
@@ -212,6 +212,8 @@
 #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 11feda9..56b9af7 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -891,8 +891,12 @@
 {
     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 d86f98c..aec4202 100644
--- a/trace-events
+++ b/trace-events
@@ -1020,6 +1020,15 @@
 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 @@
 
 # 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_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_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 f33be47..6f2294e 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -7,14 +7,14 @@
 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 8661777..f20fd1f 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 @@
 
 NSWindow *normalWindow;
 static DisplayChangeListener *dcl;
+static int last_buttons;
 
 int gArgc;
 char **gArgv;
@@ -501,6 +495,7 @@
 
     int buttons = 0;
     int keycode;
+    bool mouse_event = false;
     NSPoint p = [event locationInWindow];
 
     switch ([event type]) {
@@ -514,16 +509,14 @@
 
             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 @@
 
             // 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 @@
             }
 
             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 @@
                     }
                 }
             }
-            COCOA_MOUSE_EVENT
+            mouse_event = true;
             break;
         case NSLeftMouseDown:
             if ([event modifierFlags] & NSCommandKeyMask) {
@@ -634,15 +623,15 @@
             } 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 @@
             } 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 @@
                     [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 @@
         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 @@
 
     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 502e160..4df251d 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -124,6 +124,8 @@
 
     /* Graphic console state.  */
     Object *device;
+    uint32_t head;
+    QemuUIInfo ui_info;
     const GraphicHwOps *hw_ops;
     void *hw;
 
@@ -1179,6 +1181,8 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
         }
         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 @@
     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 dbc3d5e..b044790 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 @@
         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 & ALTGR) {
-                kbd_put_keycode(SCANCODE_EMUL0);
-                kbd_put_keycode(ALT_CODE);
+            if (keycode & SHIFT) {
+                qemu_input_event_send_key_number(NULL, SHIFT_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);
-            if (keycode & ALTGR) {
-                kbd_put_keycode(SCANCODE_EMUL0);
-                kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+            if (keycode & CNTRL) {
+                qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
             }
-            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);
+            if (keycode & ALT) {
+                qemu_input_event_send_key_number(NULL, ALT_CODE, true);
+            }
+            if (keycode & ALTGR) {
+                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
+            }
+
+            qemu_input_event_send_key_number(NULL, keycode, true);
+            qemu_input_event_send_key_number(NULL, keycode, false);
+
+            if (keycode & ALTGR) {
+                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);
+            }
         } else {
             keysym = curses2qemu[chr];
             if (keysym == -1)
diff --git a/ui/gtk.c b/ui/gtk.c
index a633d89..1851495 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -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 @@
     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 @@
         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 @@
                                 void *opaque)
 {
     GtkDisplayState *s = opaque;
-    int dx, dy;
     int x, y;
     int mx, my;
     int fbh, fbw;
@@ -610,25 +607,21 @@
         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 @@
                                 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;
+        return TRUE;
     }
 
-    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;
-    }
-
-    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 @@
         }
     }
 
-    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 0000000..f38984b
--- /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 1c70f60..2761911 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -1,551 +1,300 @@
-/*
- * 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 "qmp-commands.h"
+#include "trace.h"
+#include "ui/input.h"
+#include "ui/console.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 QemuInputHandlerState {
+    DeviceState       *dev;
+    QemuInputHandler  *handler;
+    int               id;
+    int               events;
+    QTAILQ_ENTRY(QemuInputHandlerState) node;
 };
-
-struct QEMUPutKbdEntry {
-    QEMUPutKBDEvent *put_kbd;
-    void *opaque;
-    QTAILQ_ENTRY(QEMUPutKbdEntry) next;
-};
-
-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(, 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])) {
+    s->dev = dev;
+    s->handler = handler;
+    s->id = id++;
+    QTAILQ_INSERT_TAIL(&handlers, s, node);
+
+    qemu_input_check_mode_change();
+    return s;
+}
+
+void qemu_input_handler_activate(QemuInputHandlerState *s)
+{
+    QTAILQ_REMOVE(&handlers, s, node);
+    QTAILQ_INSERT_HEAD(&handlers, s, node);
+    qemu_input_check_mode_change();
+}
+
+void qemu_input_handler_unregister(QemuInputHandlerState *s)
+{
+    QTAILQ_REMOVE(&handlers, s, node);
+    g_free(s);
+    qemu_input_check_mode_change();
+}
+
+static QemuInputHandlerState*
+qemu_input_find_handler(uint32_t mask)
+{
+    QemuInputHandlerState *s;
+
+    QTAILQ_FOREACH(s, &handlers, node) {
+        if (mask & s->handler->mask) {
+            return s;
+        }
+    }
+    return NULL;
+}
+
+static void qemu_input_transform_abs_rotate(InputEvent *evt)
+{
+    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;
+        }
+        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;
+        }
+        break;
+    }
+}
+
+static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
+{
+    const char *name;
+    int idx = -1;
+
+    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;
     }
-
-    /* Return Q_KEY_CODE_MAX if the key is invalid */
-    return i;
 }
 
-int index_from_keycode(int code)
+void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
 {
-    int i;
+    QemuInputHandlerState *s;
 
-    for (i = 0; i < Q_KEY_CODE_MAX; i++) {
-        if (key_defs[i] == code) {
-            break;
+    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
+        return;
+    }
+
+    qemu_input_event_trace(src, evt);
+
+    /* pre processing */
+    if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) {
+            qemu_input_transform_abs_rotate(evt);
+    }
+
+    /* send event */
+    s = qemu_input_find_handler(1 << evt->kind);
+    s->handler->event(s->dev, src, evt);
+    s->events++;
+}
+
+void qemu_input_event_sync(void)
+{
+    QemuInputHandlerState *s;
+
+    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
+        return;
+    }
+
+    trace_input_event_sync();
+
+    QTAILQ_FOREACH(s, &handlers, node) {
+        if (!s->events) {
+            continue;
         }
-    }
-
-    /* Return Q_KEY_CODE_MAX if the code 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) {
-        if (keycodes[--keycodes_size] & SCANCODE_GREY) {
-            kbd_put_keycode(SCANCODE_EMUL0);
+        if (s->handler->sync) {
+            s->handler->sync(s->dev);
         }
-        kbd_put_keycode(keycodes[keycodes_size] | SCANCODE_UP);
+        s->events = 0;
     }
-
-    free_keycodes();
 }
 
-void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time,
-                  Error **errp)
+InputEvent *qemu_input_event_new_key(KeyValue *key, bool down)
 {
-    int keycode;
-    KeyValueList *p;
+    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;
+}
 
-    if (!key_timer) {
-        key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL);
-    }
+void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down)
+{
+    InputEvent *evt;
+    evt = qemu_input_event_new_key(key, down);
+    qemu_input_event_send(src, evt);
+    qemu_input_event_sync();
+    qapi_free_InputEvent(evt);
+}
 
-    if (keycodes != NULL) {
-        timer_del(key_timer);
-        release_keys(NULL);
-    }
+void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down)
+{
+    KeyValue *key = g_new0(KeyValue, 1);
+    key->kind = KEY_VALUE_KIND_NUMBER;
+    key->number = num;
+    qemu_input_event_send_key(src, key, down);
+}
 
-    if (!has_hold_time) {
-        hold_time = 100;
-    }
+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);
+}
 
-    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;
+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_input_queue_btn(QemuConsole *src, InputButton btn, bool down)
+{
+    InputEvent *evt;
+    evt = qemu_input_event_new_btn(btn, down);
+    qemu_input_event_send(src, evt);
+    qapi_free_InputEvent(evt);
+}
+
+void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map,
+                               uint32_t button_old, uint32_t button_new)
+{
+    InputButton btn;
+    uint32_t mask;
+
+    for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) {
+        mask = button_map[btn];
+        if ((button_old & mask) == (button_new & mask)) {
+            continue;
         }
-
-        if (keycode & SCANCODE_GREY) {
-            kbd_put_keycode(SCANCODE_EMUL0);
-        }
-        kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
-
-        keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1));
-        keycodes[keycodes_size++] = keycode;
+        qemu_input_queue_btn(src, btn, button_new & mask);
     }
-
-    /* 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)
+bool qemu_input_is_absolute(void)
 {
-    QEMUPutKbdEntry *entry;
+    QemuInputHandlerState *s;
 
-    entry = g_malloc0(sizeof(QEMUPutKbdEntry));
-    entry->put_kbd = func;
-    entry->opaque = opaque;
-    QTAILQ_INSERT_HEAD(&kbd_handlers, entry, next);
-    return entry;
+    s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS);
+    return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
 }
 
-void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry)
+int qemu_input_scale_axis(int value, int size_in, int size_out)
 {
-    QTAILQ_REMOVE(&kbd_handlers, entry, next);
+    if (size_in < 2) {
+        return size_out / 2;
+    }
+    return (int64_t)value * (size_out - 1) / (size_in - 1);
 }
 
-static void check_mode_change(void)
+InputEvent *qemu_input_event_new_move(InputEventKind kind,
+                                      InputAxis axis, int value)
 {
-    static int current_is_absolute, current_has_absolute;
+    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;
+}
+
+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);
+}
+
+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);
+}
+
+void qemu_input_check_mode_change(void)
+{
+    static int current_is_absolute;
     int is_absolute;
-    int has_absolute;
 
-    is_absolute = kbd_mouse_is_absolute();
-    has_absolute = kbd_mouse_has_absolute();
+    is_absolute = qemu_input_is_absolute();
 
-    if (is_absolute != current_is_absolute ||
-        has_absolute != current_has_absolute) {
+    if (is_absolute != current_is_absolute) {
+        trace_input_mouse_mode(is_absolute);
         notifier_list_notify(&mouse_mode_notifiers, NULL);
     }
 
     current_is_absolute = is_absolute;
-    current_has_absolute = has_absolute;
-}
-
-QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
-                                                void *opaque, int absolute,
-                                                const char *name)
-{
-    QEMUPutMouseEntry *s;
-    static int mouse_index = 0;
-
-    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->qemu_put_mouse_event_name = g_strdup(name);
-    s->index = mouse_index++;
-
-    QTAILQ_INSERT_TAIL(&mouse_handlers, s, node);
-
-    check_mode_change();
-
-    return s;
-}
-
-void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry)
-{
-    QTAILQ_REMOVE(&mouse_handlers, entry, node);
-    QTAILQ_INSERT_HEAD(&mouse_handlers, entry, node);
-
-    check_mode_change();
-}
-
-void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry)
-{
-    QTAILQ_REMOVE(&mouse_handlers, entry, node);
-
-    g_free(entry->qemu_put_mouse_event_name);
-    g_free(entry);
-
-    check_mode_change();
-}
-
-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_keycode(int keycode)
-{
-    QEMUPutKbdEntry *entry = QTAILQ_FIRST(&kbd_handlers);
-
-    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
-        return;
-    }
-    if (entry && entry->put_kbd) {
-        entry->put_kbd(entry->opaque, keycode);
-    }
-}
-
-void kbd_put_ledstate(int ledstate)
-{
-    QEMUPutLEDEntry *cursor;
-
-    QTAILQ_FOREACH(cursor, &led_handlers, next) {
-        cursor->put_led(cursor->opaque, ledstate);
-    }
-}
-
-void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
-{
-    QEMUPutMouseEntry *entry;
-    QEMUPutMouseEvent *mouse_event;
-    void *mouse_event_opaque;
-    int width, height;
-
-    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
-        return;
-    }
-    if (QTAILQ_EMPTY(&mouse_handlers)) {
-        return;
-    }
-
-    entry = QTAILQ_FIRST(&mouse_handlers);
-
-    mouse_event = entry->qemu_put_mouse_event;
-    mouse_event_opaque = entry->qemu_put_mouse_event_opaque;
-
-    if (mouse_event) {
-        if (entry->qemu_put_mouse_event_absolute) {
-            width = 0x7fff;
-            height = 0x7fff;
-        } else {
-            width = graphic_width - 1;
-            height = graphic_height - 1;
-        }
-
-        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;
-        }
-    }
-}
-
-int kbd_mouse_is_absolute(void)
-{
-    if (QTAILQ_EMPTY(&mouse_handlers)) {
-        return 0;
-    }
-
-    return QTAILQ_FIRST(&mouse_handlers)->qemu_put_mouse_event_absolute;
-}
-
-int kbd_mouse_has_absolute(void)
-{
-    QEMUPutMouseEntry *entry;
-
-    QTAILQ_FOREACH(entry, &mouse_handlers, node) {
-        if (entry->qemu_put_mouse_event_absolute) {
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-MouseInfoList *qmp_query_mice(Error **errp)
-{
-    MouseInfoList *mice_list = NULL;
-    QEMUPutMouseEntry *cursor;
-    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;
-        info->value->current = current;
-
-        current = false;
-
-        info->next = mice_list;
-        mice_list = info;
-    }
-
-    return mice_list;
-}
-
-void do_mouse_set(Monitor *mon, const QDict *qdict)
-{
-    QEMUPutMouseEntry *cursor;
-    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) {
-            found = 1;
-            qemu_activate_mouse_event_handler(cursor);
-            break;
-        }
-    }
-
-    if (!found) {
-        monitor_printf(mon, "Mouse at given index not found\n");
-    }
-
-    check_mode_change();
 }
 
 void qemu_add_mouse_mode_change_notifier(Notifier *notify)
@@ -557,3 +306,52 @@
 {
     notifier_remove(notify);
 }
+
+MouseInfoList *qmp_query_mice(Error **errp)
+{
+    MouseInfoList *mice_list = NULL;
+    MouseInfoList *info;
+    QemuInputHandlerState *s;
+    bool current = true;
+
+    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;
+    }
+
+    return mice_list;
+}
+
+void do_mouse_set(Monitor *mon, const QDict *qdict)
+{
+    QemuInputHandlerState *s;
+    int index = qdict_get_int(qdict, "index");
+    int found = 0;
+
+    QTAILQ_FOREACH(s, &handlers, node) {
+        if (s->id == index) {
+            found = 1;
+            qemu_input_handler_activate(s);
+            break;
+        }
+    }
+
+    if (!found) {
+        monitor_printf(mon, "Mouse at given index not found\n");
+    }
+
+    qemu_input_check_mode_change();
+}
diff --git a/ui/sdl.c b/ui/sdl.c
index 9d8583c..c1a16be 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -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 @@
     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 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 @@
     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 @@
     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 @@
     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 @@
     }
     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 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_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;
+    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 (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 (prev_state != state) {
+        qemu_input_update_buttons(dcl->con, bmap, prev_state, state);
+        prev_state = state;
     }
 
-    if (kbd_mouse_is_absolute()) {
-        dx = x * 0x7FFF / (real_screen->w - 1);
-        dy = y * 0x7FFF / (real_screen->h - 1);
+    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 @@
     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 @@
             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 @@
 {
     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 @@
     }
 #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 @@
     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 @@
     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 @@
 
     atexit(sdl_cleanup);
 }
+#endif
diff --git a/ui/sdl2-keymap.h b/ui/sdl2-keymap.h
new file mode 100644
index 0000000..5a12f45
--- /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 0000000..f1532e9
--- /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 ee90480..599d9fc 100644
--- a/ui/sdl_keysym.h
+++ b/ui/sdl_keysym.h
@@ -200,6 +200,7 @@
 { "yacute",               0x0fd},
 { "thorn",                0x0fe},
 { "ydiaeresis",           0x0ff},
+#if SDL_MAJOR_VERSION == 1
 {"EuroSign", SDLK_EURO},
 
     /* modifiers */
@@ -272,6 +273,6 @@
 {"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 3beb8de..6dab23b 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 @@
     .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 @@
 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;
+    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,
+    };
 
-    /*
-     * 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;
+    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 @@
 {
     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 @@
 {
     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 @@
 {
     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 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;
diff --git a/ui/vnc.c b/ui/vnc.c
index 5601cc3..7dfc94a 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -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 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 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 @@
     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 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 @@
     }
 
     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 @@
         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);
     }
 }
 
diff --git a/ui/vnc.h b/ui/vnc.h
index 6e99213..e63c142 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -257,6 +257,7 @@
     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 f0784d6..ee57ef5 100644
--- a/util/host-utils.c
+++ b/util/host-utils.c
@@ -86,4 +86,79 @@
     }
     *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 */
diff --git a/vl.c b/vl.c
index 685a7a9..41581c1 100644
--- a/vl.c
+++ b/vl.c
@@ -374,6 +374,10 @@
             .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 @@
     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 @@
     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 @@
     { "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 @@
                     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 @@
         exit(0);
     }
 
-    configure_accelerator();
+    configure_accelerator(machine);
 
     if (qtest_chrdev) {
         Error *local_err = NULL;
diff --git a/xen-all.c b/xen-all.c
index 4a594bd..ba34739 100644
--- a/xen-all.c
+++ b/xen-all.c
@@ -1001,7 +1001,7 @@
     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 ad189a6..59927cb 100644
--- a/xen-stub.c
+++ b/xen-stub.c
@@ -47,7 +47,7 @@
     return NULL;
 }
 
-int xen_init(void)
+int xen_init(QEMUMachine *machine)
 {
     return -ENOSYS;
 }