summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile38
-rw-r--r--Makefile.objs29
-rw-r--r--Makefile.target2
-rw-r--r--VERSION2
-rwxr-xr-xconfigure85
-rw-r--r--cpu-all.h2
-rw-r--r--docs/migration.txt303
-rw-r--r--elf.h44
-rw-r--r--hw/e1000.c12
-rw-r--r--hw/hw.h6
-rw-r--r--hw/ide/core.c71
-rw-r--r--hw/ide/pci.c38
-rw-r--r--hw/vhost.c21
-rw-r--r--linux-user/elfload.c1799
-rw-r--r--linux-user/linuxload.c17
-rw-r--r--linux-user/mmap.c14
-rw-r--r--linux-user/qemu.h7
-rw-r--r--qemu-options.hx7
-rw-r--r--qemu-thread.c22
-rw-r--r--qemu-thread.h4
-rw-r--r--savevm.c86
-rw-r--r--tests/cris/check_addo.c4
-rw-r--r--tests/cris/check_addoq.c2
-rw-r--r--tests/cris/check_settls1.c10
-rw-r--r--ui/cocoa.m (renamed from cocoa.m)0
-rw-r--r--ui/curses.c (renamed from curses.c)0
-rw-r--r--ui/curses_keys.h (renamed from curses_keys.h)0
-rw-r--r--ui/d3des.c (renamed from d3des.c)0
-rw-r--r--ui/d3des.h (renamed from d3des.h)0
-rw-r--r--ui/keymaps.c (renamed from keymaps.c)0
-rw-r--r--ui/keymaps.h (renamed from keymaps.h)0
-rw-r--r--ui/sdl.c (renamed from sdl.c)0
-rw-r--r--ui/sdl_keysym.h (renamed from sdl_keysym.h)0
-rw-r--r--ui/sdl_zoom.c (renamed from sdl_zoom.c)0
-rw-r--r--ui/sdl_zoom.h (renamed from sdl_zoom.h)0
-rw-r--r--ui/sdl_zoom_template.h (renamed from sdl_zoom_template.h)0
-rw-r--r--ui/vnc-auth-sasl.c (renamed from vnc-auth-sasl.c)0
-rw-r--r--ui/vnc-auth-sasl.h (renamed from vnc-auth-sasl.h)0
-rw-r--r--ui/vnc-auth-vencrypt.c (renamed from vnc-auth-vencrypt.c)0
-rw-r--r--ui/vnc-auth-vencrypt.h (renamed from vnc-auth-vencrypt.h)0
-rw-r--r--ui/vnc-enc-hextile-template.h (renamed from vnchextile.h)0
-rw-r--r--ui/vnc-enc-hextile.c (renamed from vnc-encoding-hextile.c)26
-rw-r--r--ui/vnc-enc-tight.c1717
-rw-r--r--ui/vnc-enc-tight.h (renamed from vnc-encoding-tight.h)21
-rw-r--r--ui/vnc-enc-zlib.c (renamed from vnc-encoding-zlib.c)34
-rw-r--r--ui/vnc-jobs-async.c331
-rw-r--r--ui/vnc-jobs-sync.c73
-rw-r--r--ui/vnc-jobs.h87
-rw-r--r--ui/vnc-palette.c136
-rw-r--r--ui/vnc-palette.h63
-rw-r--r--ui/vnc-tls.c (renamed from vnc-tls.c)0
-rw-r--r--ui/vnc-tls.h (renamed from vnc-tls.h)0
-rw-r--r--ui/vnc.c (renamed from vnc.c)167
-rw-r--r--ui/vnc.h (renamed from vnc.h)115
-rw-r--r--ui/vnc_keysym.h (renamed from vnc_keysym.h)0
-rw-r--r--ui/x_keymap.c (renamed from x_keymap.c)0
-rw-r--r--ui/x_keymap.h (renamed from x_keymap.h)0
-rw-r--r--vnc-encoding-tight.c959
58 files changed, 4145 insertions, 2209 deletions
diff --git a/Makefile b/Makefile
index 6fc1b2c89b..f95cc2fd01 100644
--- a/Makefile
+++ b/Makefile
@@ -96,42 +96,14 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
 
 QEMU_CFLAGS+=$(CURL_CFLAGS)
 
-cocoa.o: cocoa.m
+ui/cocoa.o: ui/cocoa.m
 
-keymaps.o: keymaps.c keymaps.h
+ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
 
-sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h
-
-sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h
-
-sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
-
-acl.o: acl.h acl.c
-
-vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
-
-vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h
-
-vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
-
-vnc-tls.o: vnc-tls.c vnc.h
-
-vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h
-
-vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h
-
-vnc-encoding-zlib.o: vnc-encoding-zlib.c vnc.h
-
-vnc-encoding-hextile.o: vnc-encoding-hextile.c vnc.h
-
-vnc-encoding-tight.o: vnc-encoding-tight.c vnc.h vnc-encoding-tight.h
-
-curses.o: curses.c keymaps.h curses_keys.h
+ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
 
 bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
 
-iov.o: iov.c iov.h
-
 ######################################################################
 
 qemu-img.o: qemu-img-cmds.h
@@ -159,7 +131,7 @@ clean:
 # avoid old build problems by removing potentially incorrect old files
 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 	rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
-	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d
+	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
 	rm -f qemu-img-cmds.h
 	$(MAKE) -C tests clean
 	for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
@@ -345,4 +317,4 @@ tarbin:
 	$(mandir)/man8/qemu-nbd.8
 
 # Include automatically generated dependency files
--include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d)
+-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d)
diff --git a/Makefile.objs b/Makefile.objs
index 67f1b215b1..4a1eaa1b07 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -102,17 +102,24 @@ audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
 audio-obj-y += wavcapture.o
 common-obj-y += $(addprefix audio/, $(audio-obj-y))
 
-common-obj-y += keymaps.o
-common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
-common-obj-$(CONFIG_CURSES) += curses.o
-common-obj-y += vnc.o acl.o d3des.o
-common-obj-y += vnc-encoding-zlib.o vnc-encoding-hextile.o
-common-obj-y += vnc-encoding-tight.o
-common-obj-y += iov.o
-common-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
-common-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
-common-obj-$(CONFIG_COCOA) += cocoa.o
-common-obj-$(CONFIG_IOTHREAD) += qemu-thread.o
+ui-obj-y += keymaps.o
+ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
+ui-obj-$(CONFIG_CURSES) += curses.o
+ui-obj-y += vnc.o d3des.o
+ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
+ui-obj-y += vnc-enc-tight.o vnc-palette.o
+ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
+ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
+ui-obj-$(CONFIG_COCOA) += cocoa.o
+ifdef CONFIG_VNC_THREAD
+ui-obj-y += vnc-jobs-async.o
+else
+ui-obj-y += vnc-jobs-sync.o
+endif
+common-obj-y += $(addprefix ui/, $(ui-obj-y))
+
+common-obj-y += iov.o acl.o
+common-obj-$(CONFIG_THREAD) += qemu-thread.o
 common-obj-y += notify.o event_notifier.o
 common-obj-y += qemu-timer.o
 
diff --git a/Makefile.target b/Makefile.target
index 3ef4666d2c..8a9c427b55 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -177,6 +177,8 @@ LIBS+=-lz
 
 QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
 QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
+QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
+QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
 
 # xen backend driver support
 obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
diff --git a/VERSION b/VERSION
index d9e30b8d37..d82f77b7c9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.12.50
+0.13.50
diff --git a/configure b/configure
index b68f01a665..a20371ca71 100755
--- a/configure
+++ b/configure
@@ -268,6 +268,9 @@ uuid=""
 vde=""
 vnc_tls=""
 vnc_sasl=""
+vnc_jpeg=""
+vnc_png=""
+vnc_thread="no"
 xen=""
 linux_aio=""
 attr=""
@@ -575,6 +578,18 @@ for opt do
   ;;
   --enable-vnc-sasl) vnc_sasl="yes"
   ;;
+  --disable-vnc-jpeg) vnc_jpeg="no"
+  ;;
+  --enable-vnc-jpeg) vnc_jpeg="yes"
+  ;;
+  --disable-vnc-png) vnc_png="no"
+  ;;
+  --enable-vnc-png) vnc_png="yes"
+  ;;
+  --disable-vnc-thread) vnc_thread="no"
+  ;;
+  --enable-vnc-thread) vnc_thread="yes"
+  ;;
   --disable-slirp) slirp="no"
   ;;
   --disable-uuid) uuid="no"
@@ -825,6 +840,12 @@ echo "  --disable-vnc-tls        disable TLS encryption for VNC server"
 echo "  --enable-vnc-tls         enable TLS encryption for VNC server"
 echo "  --disable-vnc-sasl       disable SASL encryption for VNC server"
 echo "  --enable-vnc-sasl        enable SASL encryption for VNC server"
+echo "  --disable-vnc-jpeg       disable JPEG lossy compression for VNC server"
+echo "  --enable-vnc-jpeg        enable JPEG lossy compression for VNC server"
+echo "  --disable-vnc-png        disable PNG compression for VNC server (default)"
+echo "  --enable-vnc-png         enable PNG compression for VNC server"
+echo "  --disable-vnc-thread     disable threaded VNC server"
+echo "  --enable-vnc-thread      enable threaded VNC server"
 echo "  --disable-curses         disable curses output"
 echo "  --enable-curses          enable curses output"
 echo "  --disable-curl           disable curl connectivity"
@@ -1246,6 +1267,52 @@ EOF
 fi
 
 ##########################################
+# VNC JPEG detection
+if test "$vnc_jpeg" != "no" ; then
+cat > $TMPC <<EOF
+#include <stdio.h>
+#include <jpeglib.h>
+int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; }
+EOF
+    vnc_jpeg_cflags=""
+    vnc_jpeg_libs="-ljpeg"
+  if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then
+    vnc_jpeg=yes
+    libs_softmmu="$vnc_jpeg_libs $libs_softmmu"
+  else
+    if test "$vnc_jpeg" = "yes" ; then
+      feature_not_found "vnc-jpeg"
+    fi
+    vnc_jpeg=no
+  fi
+fi
+
+##########################################
+# VNC PNG detection
+if test "$vnc_png" != "no" ; then
+cat > $TMPC <<EOF
+//#include <stdio.h>
+#include <png.h>
+int main(void) {
+    png_structp png_ptr;
+    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    return 0;
+}
+EOF
+    vnc_png_cflags=""
+    vnc_png_libs="-lpng"
+  if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then
+    vnc_png=yes
+    libs_softmmu="$vnc_png_libs $libs_softmmu"
+  else
+    if test "$vnc_png" = "yes" ; then
+      feature_not_found "vnc-png"
+    fi
+    vnc_png=no
+  fi
+fi
+
+##########################################
 # fnmatch() probe, used for ACL routines
 fnmatch="no"
 cat > $TMPC << EOF
@@ -2094,6 +2161,9 @@ echo "Block whitelist   $block_drv_whitelist"
 echo "Mixer emulation   $mixemu"
 echo "VNC TLS support   $vnc_tls"
 echo "VNC SASL support  $vnc_sasl"
+echo "VNC JPEG support  $vnc_jpeg"
+echo "VNC PNG support   $vnc_png"
+echo "VNC thread        $vnc_thread"
 if test -n "$sparc_cpu"; then
     echo "Target Sparc Arch $sparc_cpu"
 fi
@@ -2231,6 +2301,18 @@ if test "$vnc_sasl" = "yes" ; then
   echo "CONFIG_VNC_SASL=y" >> $config_host_mak
   echo "VNC_SASL_CFLAGS=$vnc_sasl_cflags" >> $config_host_mak
 fi
+if test "$vnc_jpeg" != "no" ; then
+  echo "CONFIG_VNC_JPEG=y" >> $config_host_mak
+  echo "VNC_JPEG_CFLAGS=$vnc_jpeg_cflags" >> $config_host_mak
+fi
+if test "$vnc_png" != "no" ; then
+  echo "CONFIG_VNC_PNG=y" >> $config_host_mak
+  echo "VNC_PNG_CFLAGS=$vnc_png_cflags" >> $config_host_mak
+fi
+if test "$vnc_thread" != "no" ; then
+  echo "CONFIG_VNC_THREAD=y" >> $config_host_mak
+  echo "CONFIG_THREAD=y" >> $config_host_mak
+fi
 if test "$fnmatch" = "yes" ; then
   echo "CONFIG_FNMATCH=y" >> $config_host_mak
 fi
@@ -2307,6 +2389,7 @@ if test "$xen" = "yes" ; then
 fi
 if test "$io_thread" = "yes" ; then
   echo "CONFIG_IOTHREAD=y" >> $config_host_mak
+  echo "CONFIG_THREAD=y" >> $config_host_mak
 fi
 if test "$linux_aio" = "yes" ; then
   echo "CONFIG_LINUX_AIO=y" >> $config_host_mak
@@ -2831,7 +2914,7 @@ done # for target in $targets
 if test "$source_path_used" = "yes" ; then
     DIRS="tests tests/cris slirp audio block net pc-bios/optionrom"
     DIRS="$DIRS roms/seabios roms/vgabios"
-    DIRS="$DIRS fsdev"
+    DIRS="$DIRS fsdev ui"
     FILES="Makefile tests/Makefile"
     FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
     FILES="$FILES tests/test-mmap.c"
diff --git a/cpu-all.h b/cpu-all.h
index 224ca40c1d..67a32664d5 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -629,8 +629,10 @@ extern unsigned long guest_base;
 extern int have_guest_base;
 extern unsigned long reserved_va;
 #define GUEST_BASE guest_base
+#define RESERVED_VA reserved_va
 #else
 #define GUEST_BASE 0ul
+#define RESERVED_VA 0ul
 #endif
 
 /* All direct uses of g2h and h2g need to go away for usermode softmmu.  */
diff --git a/docs/migration.txt b/docs/migration.txt
new file mode 100644
index 0000000000..69d53837c3
--- /dev/null
+++ b/docs/migration.txt
@@ -0,0 +1,303 @@
+= Migration =
+
+QEMU has code to load/save the state of the guest that it is running.
+This are two complementary operations.  Saving the state just does
+that, saves the state for each device that the guest is running.
+Restoring a guest is just the opposite operation: we need to load the
+state of each device.
+
+For this to work, QEMU has to be launch with the same arguments the
+two times.  I.e. it can only restore the state in one guest that has
+the same devices that the one it was saved (this last requirement can
+be relaxed a bit, but for now we can consider that configuration have
+to be exactly the same).
+
+Once that we are able to save/restore a guest, a new functionality is
+requested: migration.  This means that QEMU is able to start in one
+machine and being "migrated" to other machine.  I.e. being moved to
+other machine.
+
+Next was the "live migration" functionality.  This is important
+because some guests run with a lot of state (specially RAM), and it
+can take a while to move all state from one machine to another.  Live
+migration allows the guest to continue running while the state is
+transferred.  Only while the last part of the state is transferred has
+the guest to be stopped.  Typically the time that the guest is
+unresponsive during live migration is the low hundred of milliseconds
+(notice that this depends on lot of things).
+
+=== Types of migration ===
+
+Now that we have talked about live migration, there are several ways
+to do migration:
+
+- tcp migration: do the migration using tcp sockets
+- unix migration: do the migration using unix sockets
+- exec migration: do the migration using the stdin/stdout through a process.
+- fd migration: do the migration using an file descriptor that is
+  passed to QEMU.  QEMU don't cares how this file descriptor is opened.
+
+All this four migration protocols use the same infrastructure to
+save/restore state devices.  This infrastructure is shared with the
+savevm/loadvm functionality.
+
+=== State Live Migration ==
+
+This is used for RAM and block devices.  It is not yet ported to vmstate.
+<Fill more information here>
+
+=== What is the common infrastructure ===
+
+QEMU uses a QEMUFile abstraction to be able to do migration.  Any type
+of migration that what to use QEMU infrastructure has to create a
+QEMUFile with:
+
+QEMUFile *qemu_fopen_ops(void *opaque,
+	 		 QEMUFilePutBufferFunc *put_buffer,
+                         QEMUFileGetBufferFunc *get_buffer,
+                         QEMUFileCloseFunc *close,
+                         QEMUFileRateLimit *rate_limit,
+                         QEMUFileSetRateLimit *set_rate_limit,
+			 QEMUFileGetRateLimit *get_rate_limit);
+
+The functions have the following functionality:
+
+This function writes a chunk of data to a file at the given position.
+The pos argument can be ignored if the file is only being used for
+streaming.  The handler should try to write all of the data it can.
+
+typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf,
+                                    int64_t pos, int size);
+
+Read a chunk of data from a file at the given position.  The pos argument
+can be ignored if the file is only be used for streaming.  The number of
+bytes actually read should be returned.
+
+typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
+                                    int64_t pos, int size);
+
+Close a file and return an error code
+
+typedef int (QEMUFileCloseFunc)(void *opaque);
+
+Called to determine if the file has exceeded it's bandwidth allocation.  The
+bandwidth capping is a soft limit, not a hard limit.
+
+typedef int (QEMUFileRateLimit)(void *opaque);
+
+Called to change the current bandwidth allocation. This function must return
+the new actual bandwidth. It should be new_rate if everything goes OK, and
+the old rate otherwise
+
+typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate);
+typedef size_t (QEMUFileGetRateLimit)(void *opaque);
+
+You can use any internal state that you need using the opaque void *
+pointer that is passed to all functions.
+
+The rate limiting functions are used to limit the bandwidth used by
+QEMU migration.
+
+The important functions for us are put_buffer()/get_buffer() that
+allow to write/read a buffer into the QEMUFile.
+
+=== How to save the state of one device ==
+
+The state of a device is saved using intermediate buffers.  There are
+some helper functions to assist this saving.
+
+There is a new concept that we have to explain here: device state
+version.  When we migrate a device, we save/load the state as a series
+of fields.  Some times, due to bugs or new functionality, we need to
+change the state to store more/different information.  We use the
+version to identify each time that we do a change.  Each version is
+associated with a series of fields saved.  The save_state always save
+the state as the newer version.  But load_state some times is able to
+load state from an older version.
+
+ === Legacy way ===
+
+This way is going to disappear as soon as all current users are ported to VMSTATE.
+
+Each device has to register two functions, one to save the state and
+another to load the state back.
+
+int register_savevm(DeviceState *dev,
+                    const char *idstr,
+                    int instance_id,
+                    int version_id,
+                    SaveStateHandler *save_state,
+                    LoadStateHandler *load_state,
+                    void *opaque);
+
+typedef void SaveStateHandler(QEMUFile *f, void *opaque);
+typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
+
+The important functions for the device state format are the save_state
+and load_state.  Notice that load_state receives a version_id
+parameter to know what state format is receiving.  save_state don't
+have a version_id parameter because it uses always the latest version.
+
+=== VMState ===
+
+The legacy way of saving/loading state of the device had the problem
+that we have to maintain in sync two functions.  If we did one change
+in one of them and not on the other, we got a failed migration.
+
+VMState changed the way that state is saved/loaded.  Instead of using
+a function to save the state and another to load it, it was changed to
+a declarative way of what the state consisted of.  Now VMState is able
+to interpret that definition to be able to load/save the state.  As
+the state is declared only once, it can't go out of sync in the
+save/load functions.
+
+An example (from hw/pckbd.c)
+
+static const VMStateDescription vmstate_kbd = {
+    .name = "pckbd",
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 3,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(write_cmd, KBDState),
+        VMSTATE_UINT8(status, KBDState),
+        VMSTATE_UINT8(mode, KBDState),
+        VMSTATE_UINT8(pending, KBDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+We are declaring the state with name "pckbd".
+The version_id is 3, and the fields are 4 uint8_t in a KBDState structure.
+We registered this with:
+
+    vmstate_register(NULL, 0, &vmstate_kbd, s);
+
+Note: talk about how vmstate <-> qdev interact, and what the instance id's mean.
+
+You can search for VMSTATE_* macros for lots of types used in QEMU in
+hw/hw.h.
+
+=== More about versions ==
+
+You can see that there are several version fields:
+
+- version_id: the maximum version_id supported by VMState for that device
+- minimum_version_id: the minimum version_id that VMState is able to understand
+  for that device.
+- minimum_version_id_old: For devices that were not able to port to vmstate, we can
+  assign a function that knows how to read this old state.
+
+So, VMState is able to read versions from minimum_version_id to
+version_id.  And the function load_state_old() is able to load state
+from minimum_version_id_old to minimum_version_id.  This function is
+deprecated and will be removed when no more users are left.
+
+===  Massaging functions ===
+
+Some times, it is not enough to be able to save the state directly
+from one structure, we need to fill the correct values there.  One
+example is when we are using kvm.  Before saving the cpu state, we
+need to ask kvm to copy to QEMU the state that it is using.  And the
+opposite when we are loading the state, we need a way to tell kvm to
+load the state for the cpu that we have just loaded from the QEMUFile.
+
+The functions to do that are inside a vmstate definition, and are called:
+
+- int (*pre_load)(void *opaque);
+
+  This function is called before we load the state of one device.
+
+- int (*post_load)(void *opaque, int version_id);
+
+  This function is called after we load the state of one device.
+
+- void (*pre_save)(void *opaque);
+
+  This function is called before we save the state of one device.
+
+Example: You can look at hpet.c, that uses the three function to
+         massage the state that is transferred.
+
+=== Subsections ===
+
+The use of version_id allows to be able to migrate from older versions
+to newer versions of a device.  But not the other way around.  This
+makes very complicated to fix bugs in stable branches.  If we need to
+add anything to the state to fix a bug, we have to disable migration
+to older versions that don't have that bug-fix (i.e. a new field).
+
+But some time, that bug-fix is only needed sometimes, not always.  For
+instance, if the device is in the middle of a DMA operation, it is
+using a specific functionality, ....
+
+It is impossible to create a way to make migration from any version to
+any other version to work.  But we can do better that only allowing
+migration from older versions no newer ones.  For that fields that are
+only needed sometimes, we add the idea of subsections.  a subsection
+is "like" a device vmstate, but with a particularity, it has a Boolean
+function that tells if that values are needed to be sent or not.  If
+this functions returns false, the subsection is not sent.
+
+On the receiving side, if we found a subsection for a device that we
+don't understand, we just fail the migration.  If we understand all
+the subsections, then we load the state with success.
+
+One important note is that the post_load() function is called "after"
+loading all subsections, because a newer subsection could change same
+value that it uses.
+
+Example:
+
+static bool ide_drive_pio_state_needed(void *opaque)
+{
+    IDEState *s = opaque;
+
+    return (s->status & DRQ_STAT) != 0;
+}
+
+const VMStateDescription vmstate_ide_drive_pio_state = {
+    .name = "ide_drive/pio_state",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = ide_drive_pio_pre_save,
+    .post_load = ide_drive_pio_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(req_nb_sectors, IDEState),
+        VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
+			     vmstate_info_uint8, uint8_t),
+        VMSTATE_INT32(cur_io_buffer_offset, IDEState),
+        VMSTATE_INT32(cur_io_buffer_len, IDEState),
+        VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
+        VMSTATE_INT32(elementary_transfer_size, IDEState),
+        VMSTATE_INT32(packet_transfer_size, IDEState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+const VMStateDescription vmstate_ide_drive = {
+    .name = "ide_drive",
+    .version_id = 3,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = ide_drive_post_load,
+    .fields      = (VMStateField []) {
+        .... several fields ....
+        VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection []) {
+        {
+            .vmsd = &vmstate_ide_drive_pio_state,
+            .needed = ide_drive_pio_state_needed,
+        }, {
+            /* empty */
+        }
+    }
+};
+
+Here we have a subsection for the pio state.  We only need to
+save/send this state when we are in the middle of a pio operation
+(that is what ide_drive_pio_state_needed() checks).  If DRQ_STAT is
+not enabled, the values on that fields are garbage and don't need to
+be sent.
diff --git a/elf.h b/elf.h
index eb9e3bece7..7067c90fb0 100644
--- a/elf.h
+++ b/elf.h
@@ -147,8 +147,37 @@ typedef int64_t  Elf64_Sxword;
 #define DT_DEBUG	21
 #define DT_TEXTREL	22
 #define DT_JMPREL	23
+#define DT_BINDNOW	24
+#define DT_INIT_ARRAY	25
+#define DT_FINI_ARRAY	26
+#define DT_INIT_ARRAYSZ	27
+#define DT_FINI_ARRAYSZ	28
+#define DT_RUNPATH	29
+#define DT_FLAGS	30
+#define DT_LOOS		0x6000000d
+#define DT_HIOS		0x6ffff000
 #define DT_LOPROC	0x70000000
 #define DT_HIPROC	0x7fffffff
+
+/* DT_ entries which fall between DT_VALRNGLO and DT_VALRNDHI use
+   the d_val field of the Elf*_Dyn structure.  I.e. they contain scalars.  */
+#define DT_VALRNGLO	0x6ffffd00
+#define DT_VALRNGHI	0x6ffffdff
+
+/* DT_ entries which fall between DT_ADDRRNGLO and DT_ADDRRNGHI use
+   the d_ptr field of the Elf*_Dyn structure.  I.e. they contain pointers.  */
+#define DT_ADDRRNGLO	0x6ffffe00
+#define DT_ADDRRNGHI	0x6ffffeff
+
+#define	DT_VERSYM	0x6ffffff0
+#define DT_RELACOUNT	0x6ffffff9
+#define DT_RELCOUNT	0x6ffffffa
+#define DT_FLAGS_1	0x6ffffffb
+#define DT_VERDEF	0x6ffffffc
+#define DT_VERDEFNUM	0x6ffffffd
+#define DT_VERNEED	0x6ffffffe
+#define DT_VERNEEDNUM	0x6fffffff
+
 #define DT_MIPS_RLD_VERSION	0x70000001
 #define DT_MIPS_TIME_STAMP	0x70000002
 #define DT_MIPS_ICHECKSUM	0x70000003
@@ -207,6 +236,21 @@ typedef int64_t  Elf64_Sxword;
 #define AT_PLATFORM 15  /* string identifying CPU for optimizations */
 #define AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
 #define AT_CLKTCK 17	/* frequency at which times() increments */
+#define AT_FPUCW  18	/* info about fpu initialization by kernel */
+#define AT_DCACHEBSIZE	19	/* data cache block size */
+#define AT_ICACHEBSIZE	20	/* instruction cache block size */
+#define AT_UCACHEBSIZE	21	/* unified cache block size */
+#define AT_IGNOREPPC	22	/* ppc only; entry should be ignored */
+#define AT_SECURE	23	/* boolean, was exec suid-like? */
+#define AT_BASE_PLATFORM 24	/* string identifying real platforms */
+#define AT_RANDOM	25	/* address of 16 random bytes */
+#define AT_EXECFN	31	/* filename of the executable */
+#define AT_SYSINFO	32	/* address of kernel entry point */
+#define AT_SYSINFO_EHDR	33	/* address of kernel vdso */
+#define AT_L1I_CACHESHAPE 34	/* shapes of the caches: */
+#define AT_L1D_CACHESHAPE 35	/*   bits 0-3: cache associativity.  */
+#define AT_L2_CACHESHAPE  36	/*   bits 4-7: log2 of line size.  */
+#define AT_L3_CACHESHAPE  37	/*   val&~255: cache size.  */
 
 typedef struct dynamic{
   Elf32_Sword d_tag;
diff --git a/hw/e1000.c b/hw/e1000.c
index db9143d2bc..80b78bc618 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -343,6 +343,15 @@ is_vlan_txd(uint32_t txd_lower)
     return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
 }
 
+/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
+ * fill it in, just pad descriptor length by 4 bytes unless guest
+ * told us to trip it off the packet. */
+static inline int
+fcs_len(E1000State *s)
+{
+    return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4;
+}
+
 static void
 xmit_seg(E1000State *s)
 {
@@ -648,7 +657,6 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
     }
 
     rdh_start = s->mac_reg[RDH];
-    size += 4; // for the header
     do {
         if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) {
             set_ics(s, 0, E1000_ICS_RXO);
@@ -662,7 +670,7 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
         if (desc.buffer_addr) {
             cpu_physical_memory_write(le64_to_cpu(desc.buffer_addr),
                                       (void *)(buf + vlan_offset), size);
-            desc.length = cpu_to_le16(size);
+            desc.length = cpu_to_le16(size + fcs_len(s));
             desc.status |= E1000_RXD_STAT_EOP|E1000_RXD_STAT_IXSM;
         } else // as per intel docs; skip descriptors with null buf addr
             DBGOUT(RX, "Null RX descriptor!!\n");
diff --git a/hw/hw.h b/hw/hw.h
index c2de6fe8c4..e3c3db27f3 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -313,6 +313,11 @@ typedef struct {
     bool (*field_exists)(void *opaque, int version_id);
 } VMStateField;
 
+typedef struct VMStateSubsection {
+    const VMStateDescription *vmsd;
+    bool (*needed)(void *opaque);
+} VMStateSubsection;
+
 struct VMStateDescription {
     const char *name;
     int version_id;
@@ -323,6 +328,7 @@ struct VMStateDescription {
     int (*post_load)(void *opaque, int version_id);
     void (*pre_save)(void *opaque);
     VMStateField *fields;
+    const VMStateSubsection *subsections;
 };
 
 extern const VMStateInfo vmstate_info_int8;
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 9e1bdd5dca..631673fc0f 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -2748,6 +2748,7 @@ static EndTransferFunc* transfer_end_table[] = {
         ide_transfer_stop,
         ide_atapi_cmd_reply_end,
         ide_atapi_cmd,
+        ide_dummy_transfer_stop,
 };
 
 static int transfer_end_table_idx(EndTransferFunc *fn)
@@ -2771,26 +2772,28 @@ static int ide_drive_post_load(void *opaque, int version_id)
             s->cdrom_changed = 1;
         }
     }
+    return 0;
+}
 
-    if (s->cur_io_buffer_len) {
-        s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
-        s->data_ptr = s->io_buffer + s->cur_io_buffer_offset;
-        s->data_end = s->data_ptr + s->cur_io_buffer_len;
+static int ide_drive_pio_post_load(void *opaque, int version_id)
+{
+    IDEState *s = opaque;
+
+    if (s->end_transfer_fn_idx > ARRAY_SIZE(transfer_end_table)) {
+        return -EINVAL;
     }
-        
+    s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
+    s->data_ptr = s->io_buffer + s->cur_io_buffer_offset;
+    s->data_end = s->data_ptr + s->cur_io_buffer_len;
+
     return 0;
 }
 
-static void ide_drive_pre_save(void *opaque)
+static void ide_drive_pio_pre_save(void *opaque)
 {
     IDEState *s = opaque;
     int idx;
 
-    s->cur_io_buffer_len = 0;
-
-    if (!(s->status & DRQ_STAT))
-        return;
-
     s->cur_io_buffer_offset = s->data_ptr - s->io_buffer;
     s->cur_io_buffer_len = s->data_end - s->data_ptr;
 
@@ -2804,12 +2807,38 @@ static void ide_drive_pre_save(void *opaque)
     }
 }
 
+static bool ide_drive_pio_state_needed(void *opaque)
+{
+    IDEState *s = opaque;
+
+    return (s->status & DRQ_STAT) != 0;
+}
+
+const VMStateDescription vmstate_ide_drive_pio_state = {
+    .name = "ide_drive/pio_state",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = ide_drive_pio_pre_save,
+    .post_load = ide_drive_pio_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(req_nb_sectors, IDEState),
+        VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
+			     vmstate_info_uint8, uint8_t),
+        VMSTATE_INT32(cur_io_buffer_offset, IDEState),
+        VMSTATE_INT32(cur_io_buffer_len, IDEState),
+        VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
+        VMSTATE_INT32(elementary_transfer_size, IDEState),
+        VMSTATE_INT32(packet_transfer_size, IDEState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 const VMStateDescription vmstate_ide_drive = {
     .name = "ide_drive",
-    .version_id = 4,
+    .version_id = 3,
     .minimum_version_id = 0,
     .minimum_version_id_old = 0,
-    .pre_save = ide_drive_pre_save,
     .post_load = ide_drive_post_load,
     .fields      = (VMStateField []) {
         VMSTATE_INT32(mult_sectors, IDEState),
@@ -2832,15 +2861,15 @@ const VMStateDescription vmstate_ide_drive = {
         VMSTATE_UINT8(sense_key, IDEState),
         VMSTATE_UINT8(asc, IDEState),
         VMSTATE_UINT8_V(cdrom_changed, IDEState, 3),
-        VMSTATE_INT32_V(req_nb_sectors, IDEState, 4),
-        VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 4,
-			     vmstate_info_uint8, uint8_t),
-        VMSTATE_INT32_V(cur_io_buffer_offset, IDEState, 4),
-        VMSTATE_INT32_V(cur_io_buffer_len, IDEState, 4),
-        VMSTATE_UINT8_V(end_transfer_fn_idx, IDEState, 4),
-        VMSTATE_INT32_V(elementary_transfer_size, IDEState, 4),
-        VMSTATE_INT32_V(packet_transfer_size, IDEState, 4),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection []) {
+        {
+            .vmsd = &vmstate_ide_drive_pio_state,
+            .needed = ide_drive_pio_state_needed,
+        }, {
+            /* empty */
+        }
     }
 };
 
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 4d95cc5e22..4331d77232 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -121,9 +121,31 @@ void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val)
     bm->cur_addr = bm->addr;
 }
 
+static bool ide_bmdma_current_needed(void *opaque)
+{
+    BMDMAState *bm = opaque;
+
+    return (bm->cur_prd_len != 0);
+}
+
+static const VMStateDescription vmstate_bmdma_current = {
+    .name = "ide bmdma_current",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(cur_addr, BMDMAState),
+        VMSTATE_UINT32(cur_prd_last, BMDMAState),
+        VMSTATE_UINT32(cur_prd_addr, BMDMAState),
+        VMSTATE_UINT32(cur_prd_len, BMDMAState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
 static const VMStateDescription vmstate_bmdma = {
     .name = "ide bmdma",
-    .version_id = 4,
+    .version_id = 3,
     .minimum_version_id = 0,
     .minimum_version_id_old = 0,
     .fields      = (VMStateField []) {
@@ -133,11 +155,15 @@ static const VMStateDescription vmstate_bmdma = {
         VMSTATE_INT64(sector_num, BMDMAState),
         VMSTATE_UINT32(nsector, BMDMAState),
         VMSTATE_UINT8(unit, BMDMAState),
-        VMSTATE_UINT32_V(cur_addr, BMDMAState, 4),
-        VMSTATE_UINT32_V(cur_prd_last, BMDMAState, 4),
-        VMSTATE_UINT32_V(cur_prd_addr, BMDMAState, 4),
-        VMSTATE_UINT32_V(cur_prd_len, BMDMAState, 4),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection []) {
+        {
+            .vmsd = &vmstate_bmdma_current,
+            .needed = ide_bmdma_current_needed,
+        }, {
+            /* empty */
+        }
     }
 };
 
@@ -156,7 +182,7 @@ static int ide_pci_post_load(void *opaque, int version_id)
 
 const VMStateDescription vmstate_ide_pci = {
     .name = "ide",
-    .version_id = 4,
+    .version_id = 3,
     .minimum_version_id = 0,
     .minimum_version_id_old = 0,
     .post_load = ide_pci_post_load,
diff --git a/hw/vhost.c b/hw/vhost.c
index d37a66e0ea..65709d005d 100644
--- a/hw/vhost.c
+++ b/hw/vhost.c
@@ -659,6 +659,16 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
         r = -errno;
         goto fail;
     }
+    for (i = 0; i < hdev->nvqs; ++i) {
+        r = vhost_virtqueue_init(hdev,
+                                 vdev,
+                                 hdev->vqs + i,
+                                 i);
+        if (r < 0) {
+            goto fail_vq;
+        }
+    }
+
     if (hdev->log_enabled) {
         hdev->log_size = vhost_get_log_size(hdev);
         hdev->log = hdev->log_size ?
@@ -667,19 +677,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
                   (uint64_t)(unsigned long)hdev->log);
         if (r < 0) {
             r = -errno;
-            goto fail;
-        }
-    }
-
-    for (i = 0; i < hdev->nvqs; ++i) {
-        r = vhost_virtqueue_init(hdev,
-                                 vdev,
-                                 hdev->vqs + i,
-                                 i);
-        if (r < 0) {
             goto fail_vq;
         }
     }
+
     hdev->started = true;
 
     return 0;
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index accb44d9de..33d776de41 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -35,18 +35,17 @@
  * These occupy the top three bytes.
  */
 enum {
-	ADDR_NO_RANDOMIZE = 	0x0040000,	/* disable randomization of VA space */
-	FDPIC_FUNCPTRS =	0x0080000,	/* userspace function ptrs point to descriptors
-						 * (signal handling)
-						 */
-	MMAP_PAGE_ZERO =	0x0100000,
-	ADDR_COMPAT_LAYOUT =	0x0200000,
-	READ_IMPLIES_EXEC =	0x0400000,
-	ADDR_LIMIT_32BIT =	0x0800000,
-	SHORT_INODE =		0x1000000,
-	WHOLE_SECONDS =		0x2000000,
-	STICKY_TIMEOUTS	=	0x4000000,
-	ADDR_LIMIT_3GB = 	0x8000000,
+    ADDR_NO_RANDOMIZE = 0x0040000,      /* disable randomization of VA space */
+    FDPIC_FUNCPTRS =    0x0080000,      /* userspace function ptrs point to
+                                           descriptors (signal handling) */
+    MMAP_PAGE_ZERO =    0x0100000,
+    ADDR_COMPAT_LAYOUT = 0x0200000,
+    READ_IMPLIES_EXEC = 0x0400000,
+    ADDR_LIMIT_32BIT =  0x0800000,
+    SHORT_INODE =       0x1000000,
+    WHOLE_SECONDS =     0x2000000,
+    STICKY_TIMEOUTS =   0x4000000,
+    ADDR_LIMIT_3GB =    0x8000000,
 };
 
 /*
@@ -56,36 +55,35 @@ enum {
  * conflict with error returns.
  */
 enum {
-	PER_LINUX =		0x0000,
-	PER_LINUX_32BIT =	0x0000 | ADDR_LIMIT_32BIT,
-	PER_LINUX_FDPIC =	0x0000 | FDPIC_FUNCPTRS,
-	PER_SVR4 =		0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
-	PER_SVR3 =		0x0002 | STICKY_TIMEOUTS | SHORT_INODE,
-	PER_SCOSVR3 =		0x0003 | STICKY_TIMEOUTS |
-					 WHOLE_SECONDS | SHORT_INODE,
-	PER_OSR5 =		0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS,
-	PER_WYSEV386 =		0x0004 | STICKY_TIMEOUTS | SHORT_INODE,
-	PER_ISCR4 =		0x0005 | STICKY_TIMEOUTS,
-	PER_BSD =		0x0006,
-	PER_SUNOS =		0x0006 | STICKY_TIMEOUTS,
-	PER_XENIX =		0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
-	PER_LINUX32 =		0x0008,
-	PER_LINUX32_3GB =	0x0008 | ADDR_LIMIT_3GB,
-	PER_IRIX32 =		0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */
-	PER_IRIXN32 =		0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */
-	PER_IRIX64 =		0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */
-	PER_RISCOS =		0x000c,
-	PER_SOLARIS =		0x000d | STICKY_TIMEOUTS,
-	PER_UW7 =		0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
-	PER_OSF4 =		0x000f,			 /* OSF/1 v4 */
-	PER_HPUX =		0x0010,
-	PER_MASK =		0x00ff,
+    PER_LINUX =         0x0000,
+    PER_LINUX_32BIT =   0x0000 | ADDR_LIMIT_32BIT,
+    PER_LINUX_FDPIC =   0x0000 | FDPIC_FUNCPTRS,
+    PER_SVR4 =          0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
+    PER_SVR3 =          0x0002 | STICKY_TIMEOUTS | SHORT_INODE,
+    PER_SCOSVR3 =       0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS | SHORT_INODE,
+    PER_OSR5 =          0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS,
+    PER_WYSEV386 =      0x0004 | STICKY_TIMEOUTS | SHORT_INODE,
+    PER_ISCR4 =         0x0005 | STICKY_TIMEOUTS,
+    PER_BSD =           0x0006,
+    PER_SUNOS =         0x0006 | STICKY_TIMEOUTS,
+    PER_XENIX =         0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
+    PER_LINUX32 =       0x0008,
+    PER_LINUX32_3GB =   0x0008 | ADDR_LIMIT_3GB,
+    PER_IRIX32 =        0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */
+    PER_IRIXN32 =       0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */
+    PER_IRIX64 =        0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */
+    PER_RISCOS =        0x000c,
+    PER_SOLARIS =       0x000d | STICKY_TIMEOUTS,
+    PER_UW7 =           0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
+    PER_OSF4 =          0x000f,                  /* OSF/1 v4 */
+    PER_HPUX =          0x0010,
+    PER_MASK =          0x00ff,
 };
 
 /*
  * Return the base personality without flags.
  */
-#define personality(pers)	(pers & PER_MASK)
+#define personality(pers)       (pers & PER_MASK)
 
 /* this flag is uneffective under linux too, should be deleted */
 #ifndef MAP_DENYWRITE
@@ -97,15 +95,21 @@ enum {
 #define ELIBBAD 80
 #endif
 
-typedef target_ulong	target_elf_greg_t;
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA        ELFDATA2MSB
+#else
+#define ELF_DATA        ELFDATA2LSB
+#endif
+
+typedef target_ulong    target_elf_greg_t;
 #ifdef USE_UID16
-typedef uint16_t	target_uid_t;
-typedef uint16_t	target_gid_t;
+typedef uint16_t        target_uid_t;
+typedef uint16_t        target_gid_t;
 #else
-typedef uint32_t	target_uid_t;
-typedef uint32_t	target_gid_t;
+typedef uint32_t        target_uid_t;
+typedef uint32_t        target_gid_t;
 #endif
-typedef int32_t		target_pid_t;
+typedef int32_t         target_pid_t;
 
 #ifdef TARGET_I386
 
@@ -126,7 +130,7 @@ static const char *get_elf_platform(void)
 
 static uint32_t get_elf_hwcap(void)
 {
-  return thread_env->cpuid_features;
+    return thread_env->cpuid_features;
 }
 
 #ifdef TARGET_X86_64
@@ -134,7 +138,6 @@ static uint32_t get_elf_hwcap(void)
 #define elf_check_arch(x) ( ((x) == ELF_ARCH) )
 
 #define ELF_CLASS      ELFCLASS64
-#define ELF_DATA       ELFDATA2LSB
 #define ELF_ARCH       EM_X86_64
 
 static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
@@ -197,11 +200,11 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 /*
  * These are used to set parameters in the core dumps.
  */
-#define ELF_CLASS	ELFCLASS32
-#define ELF_DATA	ELFDATA2LSB
-#define ELF_ARCH	EM_386
+#define ELF_CLASS       ELFCLASS32
+#define ELF_ARCH        EM_386
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
     regs->esp = infop->start_stack;
     regs->eip = infop->entry;
@@ -249,7 +252,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 #endif
 
 #define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE	4096
+#define ELF_EXEC_PAGESIZE       4096
 
 #endif
 
@@ -259,21 +262,17 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 
 #define elf_check_arch(x) ( (x) == EM_ARM )
 
-#define ELF_CLASS	ELFCLASS32
-#ifdef TARGET_WORDS_BIGENDIAN
-#define ELF_DATA	ELFDATA2MSB
-#else
-#define ELF_DATA	ELFDATA2LSB
-#endif
-#define ELF_ARCH	EM_ARM
+#define ELF_CLASS       ELFCLASS32
+#define ELF_ARCH        EM_ARM
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
     abi_long stack = infop->start_stack;
     memset(regs, 0, sizeof(*regs));
     regs->ARM_cpsr = 0x10;
     if (infop->entry & 1)
-      regs->ARM_cpsr |= CPSR_T;
+        regs->ARM_cpsr |= CPSR_T;
     regs->ARM_pc = infop->entry & 0xfffffffe;
     regs->ARM_sp = infop->start_stack;
     /* FIXME - what to for failure of get_user()? */
@@ -313,30 +312,30 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 }
 
 #define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE	4096
+#define ELF_EXEC_PAGESIZE       4096
 
 enum
 {
-  ARM_HWCAP_ARM_SWP       = 1 << 0,
-  ARM_HWCAP_ARM_HALF      = 1 << 1,
-  ARM_HWCAP_ARM_THUMB     = 1 << 2,
-  ARM_HWCAP_ARM_26BIT     = 1 << 3,
-  ARM_HWCAP_ARM_FAST_MULT = 1 << 4,
-  ARM_HWCAP_ARM_FPA       = 1 << 5,
-  ARM_HWCAP_ARM_VFP       = 1 << 6,
-  ARM_HWCAP_ARM_EDSP      = 1 << 7,
-  ARM_HWCAP_ARM_JAVA      = 1 << 8,
-  ARM_HWCAP_ARM_IWMMXT    = 1 << 9,
-  ARM_HWCAP_ARM_THUMBEE   = 1 << 10,
-  ARM_HWCAP_ARM_NEON      = 1 << 11,
-  ARM_HWCAP_ARM_VFPv3     = 1 << 12,
-  ARM_HWCAP_ARM_VFPv3D16  = 1 << 13,
+    ARM_HWCAP_ARM_SWP       = 1 << 0,
+    ARM_HWCAP_ARM_HALF      = 1 << 1,
+    ARM_HWCAP_ARM_THUMB     = 1 << 2,
+    ARM_HWCAP_ARM_26BIT     = 1 << 3,
+    ARM_HWCAP_ARM_FAST_MULT = 1 << 4,
+    ARM_HWCAP_ARM_FPA       = 1 << 5,
+    ARM_HWCAP_ARM_VFP       = 1 << 6,
+    ARM_HWCAP_ARM_EDSP      = 1 << 7,
+    ARM_HWCAP_ARM_JAVA      = 1 << 8,
+    ARM_HWCAP_ARM_IWMMXT    = 1 << 9,
+    ARM_HWCAP_ARM_THUMBEE   = 1 << 10,
+    ARM_HWCAP_ARM_NEON      = 1 << 11,
+    ARM_HWCAP_ARM_VFPv3     = 1 << 12,
+    ARM_HWCAP_ARM_VFPv3D16  = 1 << 13,
 };
 
-#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF              \
-                    | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT     \
-                    | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP \
-                    | ARM_HWCAP_ARM_NEON | ARM_HWCAP_ARM_VFPv3 )
+#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF               \
+                   | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT      \
+                   | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP              \
+                   | ARM_HWCAP_ARM_NEON | ARM_HWCAP_ARM_VFPv3 )
 
 #endif
 
@@ -352,12 +351,12 @@ enum
 #endif
 
 #define ELF_CLASS   ELFCLASS64
-#define ELF_DATA    ELFDATA2MSB
 #define ELF_ARCH    EM_SPARCV9
 
-#define STACK_BIAS		2047
+#define STACK_BIAS              2047
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
 #ifndef TARGET_ABI32
     regs->tstate = 0;
@@ -381,10 +380,10 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
 #define elf_check_arch(x) ( (x) == EM_SPARC )
 
 #define ELF_CLASS   ELFCLASS32
-#define ELF_DATA    ELFDATA2MSB
 #define ELF_ARCH    EM_SPARC
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
     regs->psr = 0;
     regs->pc = infop->entry;
@@ -404,22 +403,17 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
 
 #define elf_check_arch(x) ( (x) == EM_PPC64 )
 
-#define ELF_CLASS	ELFCLASS64
+#define ELF_CLASS       ELFCLASS64
 
 #else
 
 #define elf_check_arch(x) ( (x) == EM_PPC )
 
-#define ELF_CLASS	ELFCLASS32
+#define ELF_CLASS       ELFCLASS32
 
 #endif
 
-#ifdef TARGET_WORDS_BIGENDIAN
-#define ELF_DATA	ELFDATA2MSB
-#else
-#define ELF_DATA	ELFDATA2LSB
-#endif
-#define ELF_ARCH	EM_PPC
+#define ELF_ARCH        EM_PPC
 
 /* Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP).
    See arch/powerpc/include/asm/cputable.h.  */
@@ -464,7 +458,7 @@ static uint32_t get_elf_hwcap(void)
 
     /* We don't have to be terribly complete here; the high points are
        Altivec/FP/SPE support.  Anything else is just a bonus.  */
-#define GET_FEATURE(flag, feature)              \
+#define GET_FEATURE(flag, feature)                                      \
     do {if (e->insns_flags & flag) features |= feature; } while(0)
     GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64);
     GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU);
@@ -480,15 +474,6 @@ static uint32_t get_elf_hwcap(void)
 }
 
 /*
- * We need to put in some extra aux table entries to tell glibc what
- * the cache block size is, so it can use the dcbz instruction safely.
- */
-#define AT_DCACHEBSIZE          19
-#define AT_ICACHEBSIZE          20
-#define AT_UCACHEBSIZE          21
-/* A special ignored type value for PPC, for glibc compatibility.  */
-#define AT_IGNOREPPC            22
-/*
  * The requirements here are:
  * - keep the final alignment of sp (sp & 0xf)
  * - make sure the 32-bit value at the first 16 byte aligned position of
@@ -498,17 +483,17 @@ static uint32_t get_elf_hwcap(void)
  *   even if DLINFO_ARCH_ITEMS goes to zero or is undefined.
  */
 #define DLINFO_ARCH_ITEMS       5
-#define ARCH_DLINFO                                                     \
-do {                                                                    \
-        NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20);                              \
-        NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20);                              \
-        NEW_AUX_ENT(AT_UCACHEBSIZE, 0);                                 \
-        /*                                                              \
-         * Now handle glibc compatibility.                              \
-         */                                                             \
-	NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC);			\
-	NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC);			\
- } while (0)
+#define ARCH_DLINFO                                     \
+    do {                                                \
+        NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20);              \
+        NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20);              \
+        NEW_AUX_ENT(AT_UCACHEBSIZE, 0);                 \
+        /*                                              \
+         * Now handle glibc compatibility.              \
+         */                                             \
+        NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC);        \
+        NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC);        \
+    } while (0)
 
 static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop)
 {
@@ -546,7 +531,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 }
 
 #define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE	4096
+#define ELF_EXEC_PAGESIZE       4096
 
 #endif
 
@@ -561,14 +546,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 #else
 #define ELF_CLASS   ELFCLASS32
 #endif
-#ifdef TARGET_WORDS_BIGENDIAN
-#define ELF_DATA	ELFDATA2MSB
-#else
-#define ELF_DATA	ELFDATA2LSB
-#endif
 #define ELF_ARCH    EM_MIPS
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
     regs->cp0_status = 2 << CP0St_KSU;
     regs->cp0_epc = infop->entry;
@@ -632,10 +613,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 #define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD)
 
 #define ELF_CLASS   ELFCLASS32
-#define ELF_DATA	ELFDATA2MSB
 #define ELF_ARCH    EM_MICROBLAZE
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
     regs->pc = infop->entry;
     regs->r1 = infop->start_stack;
@@ -671,14 +652,14 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 #define elf_check_arch(x) ( (x) == EM_SH )
 
 #define ELF_CLASS ELFCLASS32
-#define ELF_DATA  ELFDATA2LSB
 #define ELF_ARCH  EM_SH
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
-  /* Check other registers XXXXX */
-  regs->pc = infop->entry;
-  regs->regs[15] = infop->start_stack;
+    /* Check other registers XXXXX */
+    regs->pc = infop->entry;
+    regs->regs[15] = infop->start_stack;
 }
 
 /* See linux kernel: arch/sh/include/asm/elf.h.  */
@@ -696,7 +677,8 @@ enum {
     TARGET_REG_SYSCALL = 22
 };
 
-static inline void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+static inline void elf_core_copy_regs(target_elf_gregset_t *regs,
+                                      const CPUState *env)
 {
     int i;
 
@@ -725,12 +707,12 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState
 #define elf_check_arch(x) ( (x) == EM_CRIS )
 
 #define ELF_CLASS ELFCLASS32
-#define ELF_DATA  ELFDATA2LSB
 #define ELF_ARCH  EM_CRIS
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
-  regs->erp = infop->entry;
+    regs->erp = infop->entry;
 }
 
 #define ELF_EXEC_PAGESIZE        8192
@@ -743,14 +725,14 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
 
 #define elf_check_arch(x) ( (x) == EM_68K )
 
-#define ELF_CLASS	ELFCLASS32
-#define ELF_DATA	ELFDATA2MSB
-#define ELF_ARCH	EM_68K
+#define ELF_CLASS       ELFCLASS32
+#define ELF_ARCH        EM_68K
 
 /* ??? Does this need to do anything?
-#define ELF_PLAT_INIT(_r) */
+   #define ELF_PLAT_INIT(_r) */
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
     regs->usp = infop->start_stack;
     regs->sr = 0;
@@ -786,7 +768,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 }
 
 #define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE	8192
+#define ELF_EXEC_PAGESIZE       8192
 
 #endif
 
@@ -797,10 +779,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
 #define elf_check_arch(x) ( (x) == ELF_ARCH )
 
 #define ELF_CLASS      ELFCLASS64
-#define ELF_DATA       ELFDATA2MSB
 #define ELF_ARCH       EM_ALPHA
 
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
 {
     regs->pc = infop->entry;
     regs->ps = 8;
@@ -830,14 +812,14 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
 
 struct exec
 {
-  unsigned int a_info;   /* Use macros N_MAGIC, etc for access */
-  unsigned int a_text;   /* length of text, in bytes */
-  unsigned int a_data;   /* length of data, in bytes */
-  unsigned int a_bss;    /* length of uninitialized data area, in bytes */
-  unsigned int a_syms;   /* length of symbol table data in file, in bytes */
-  unsigned int a_entry;  /* start address */
-  unsigned int a_trsize; /* length of relocation info for text, in bytes */
-  unsigned int a_drsize; /* length of relocation info for data, in bytes */
+    unsigned int a_info;   /* Use macros N_MAGIC, etc for access */
+    unsigned int a_text;   /* length of text, in bytes */
+    unsigned int a_data;   /* length of data, in bytes */
+    unsigned int a_bss;    /* length of uninitialized data area, in bytes */
+    unsigned int a_syms;   /* length of symbol table data in file, in bytes */
+    unsigned int a_entry;  /* start address */
+    unsigned int a_trsize; /* length of relocation info for text, in bytes */
+    unsigned int a_drsize; /* length of relocation info for data, in bytes */
 };
 
 
@@ -847,72 +829,66 @@ struct exec
 #define ZMAGIC 0413
 #define QMAGIC 0314
 
-/* max code+data+bss space allocated to elf interpreter */
-#define INTERP_MAP_SIZE (32 * 1024 * 1024)
-
-/* max code+data+bss+brk space allocated to ET_DYN executables */
-#define ET_DYN_MAP_SIZE (128 * 1024 * 1024)
-
 /* Necessary parameters */
 #define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE
 #define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1))
 #define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1))
 
-#define INTERPRETER_NONE 0
-#define INTERPRETER_AOUT 1
-#define INTERPRETER_ELF 2
-
 #define DLINFO_ITEMS 12
 
 static inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
 {
-	memcpy(to, from, n);
+    memcpy(to, from, n);
 }
 
-static int load_aout_interp(void * exptr, int interp_fd);
-
 #ifdef BSWAP_NEEDED
 static void bswap_ehdr(struct elfhdr *ehdr)
 {
-    bswap16s(&ehdr->e_type);			/* Object file type */
-    bswap16s(&ehdr->e_machine);		/* Architecture */
-    bswap32s(&ehdr->e_version);		/* Object file version */
-    bswaptls(&ehdr->e_entry);		/* Entry point virtual address */
-    bswaptls(&ehdr->e_phoff);		/* Program header table file offset */
-    bswaptls(&ehdr->e_shoff);		/* Section header table file offset */
-    bswap32s(&ehdr->e_flags);		/* Processor-specific flags */
-    bswap16s(&ehdr->e_ehsize);		/* ELF header size in bytes */
-    bswap16s(&ehdr->e_phentsize);		/* Program header table entry size */
-    bswap16s(&ehdr->e_phnum);		/* Program header table entry count */
-    bswap16s(&ehdr->e_shentsize);		/* Section header table entry size */
-    bswap16s(&ehdr->e_shnum);		/* Section header table entry count */
-    bswap16s(&ehdr->e_shstrndx);		/* Section header string table index */
-}
-
-static void bswap_phdr(struct elf_phdr *phdr)
-{
-    bswap32s(&phdr->p_type);			/* Segment type */
-    bswaptls(&phdr->p_offset);		/* Segment file offset */
-    bswaptls(&phdr->p_vaddr);		/* Segment virtual address */
-    bswaptls(&phdr->p_paddr);		/* Segment physical address */
-    bswaptls(&phdr->p_filesz);		/* Segment size in file */
-    bswaptls(&phdr->p_memsz);		/* Segment size in memory */
-    bswap32s(&phdr->p_flags);		/* Segment flags */
-    bswaptls(&phdr->p_align);		/* Segment alignment */
-}
-
-static void bswap_shdr(struct elf_shdr *shdr)
-{
-    bswap32s(&shdr->sh_name);
-    bswap32s(&shdr->sh_type);
-    bswaptls(&shdr->sh_flags);
-    bswaptls(&shdr->sh_addr);
-    bswaptls(&shdr->sh_offset);
-    bswaptls(&shdr->sh_size);
-    bswap32s(&shdr->sh_link);
-    bswap32s(&shdr->sh_info);
-    bswaptls(&shdr->sh_addralign);
-    bswaptls(&shdr->sh_entsize);
+    bswap16s(&ehdr->e_type);            /* Object file type */
+    bswap16s(&ehdr->e_machine);         /* Architecture */
+    bswap32s(&ehdr->e_version);         /* Object file version */
+    bswaptls(&ehdr->e_entry);           /* Entry point virtual address */
+    bswaptls(&ehdr->e_phoff);           /* Program header table file offset */
+    bswaptls(&ehdr->e_shoff);           /* Section header table file offset */
+    bswap32s(&ehdr->e_flags);           /* Processor-specific flags */
+    bswap16s(&ehdr->e_ehsize);          /* ELF header size in bytes */
+    bswap16s(&ehdr->e_phentsize);       /* Program header table entry size */
+    bswap16s(&ehdr->e_phnum);           /* Program header table entry count */
+    bswap16s(&ehdr->e_shentsize);       /* Section header table entry size */
+    bswap16s(&ehdr->e_shnum);           /* Section header table entry count */
+    bswap16s(&ehdr->e_shstrndx);        /* Section header string table index */
+}
+
+static void bswap_phdr(struct elf_phdr *phdr, int phnum)
+{
+    int i;
+    for (i = 0; i < phnum; ++i, ++phdr) {
+        bswap32s(&phdr->p_type);        /* Segment type */
+        bswap32s(&phdr->p_flags);       /* Segment flags */
+        bswaptls(&phdr->p_offset);      /* Segment file offset */
+        bswaptls(&phdr->p_vaddr);       /* Segment virtual address */
+        bswaptls(&phdr->p_paddr);       /* Segment physical address */
+        bswaptls(&phdr->p_filesz);      /* Segment size in file */
+        bswaptls(&phdr->p_memsz);       /* Segment size in memory */
+        bswaptls(&phdr->p_align);       /* Segment alignment */
+    }
+}
+
+static void bswap_shdr(struct elf_shdr *shdr, int shnum)
+{
+    int i;
+    for (i = 0; i < shnum; ++i, ++shdr) {
+        bswap32s(&shdr->sh_name);
+        bswap32s(&shdr->sh_type);
+        bswaptls(&shdr->sh_flags);
+        bswaptls(&shdr->sh_addr);
+        bswaptls(&shdr->sh_offset);
+        bswaptls(&shdr->sh_size);
+        bswap32s(&shdr->sh_link);
+        bswap32s(&shdr->sh_info);
+        bswaptls(&shdr->sh_addralign);
+        bswaptls(&shdr->sh_entsize);
+    }
 }
 
 static void bswap_sym(struct elf_sym *sym)
@@ -922,21 +898,41 @@ static void bswap_sym(struct elf_sym *sym)
     bswaptls(&sym->st_size);
     bswap16s(&sym->st_shndx);
 }
+#else
+static inline void bswap_ehdr(struct elfhdr *ehdr) { }
+static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { }
+static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { }
+static inline void bswap_sym(struct elf_sym *sym) { }
 #endif
 
 #ifdef USE_ELF_CORE_DUMP
 static int elf_core_dump(int, const CPUState *);
+#endif /* USE_ELF_CORE_DUMP */
+static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias);
 
-#ifdef BSWAP_NEEDED
-static void bswap_note(struct elf_note *en)
+/* Verify the portions of EHDR within E_IDENT for the target.
+   This can be performed before bswapping the entire header.  */
+static bool elf_check_ident(struct elfhdr *ehdr)
 {
-    bswap32s(&en->n_namesz);
-    bswap32s(&en->n_descsz);
-    bswap32s(&en->n_type);
+    return (ehdr->e_ident[EI_MAG0] == ELFMAG0
+            && ehdr->e_ident[EI_MAG1] == ELFMAG1
+            && ehdr->e_ident[EI_MAG2] == ELFMAG2
+            && ehdr->e_ident[EI_MAG3] == ELFMAG3
+            && ehdr->e_ident[EI_CLASS] == ELF_CLASS
+            && ehdr->e_ident[EI_DATA] == ELF_DATA
+            && ehdr->e_ident[EI_VERSION] == EV_CURRENT);
 }
-#endif /* BSWAP_NEEDED */
 
-#endif /* USE_ELF_CORE_DUMP */
+/* Verify the portions of EHDR outside of E_IDENT for the target.
+   This has to wait until after bswapping the header.  */
+static bool elf_check_ehdr(struct elfhdr *ehdr)
+{
+    return (elf_check_arch(ehdr->e_machine)
+            && ehdr->e_ehsize == sizeof(struct elfhdr)
+            && ehdr->e_phentsize == sizeof(struct elf_phdr)
+            && ehdr->e_shentsize == sizeof(struct elf_shdr)
+            && (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN));
+}
 
 /*
  * 'copy_elf_strings()' copies argument/envelope strings from user
@@ -951,24 +947,24 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
     int len, offset = 0;
 
     if (!p) {
-	return 0;       /* bullet-proofing */
+        return 0;       /* bullet-proofing */
     }
     while (argc-- > 0) {
         tmp = argv[argc];
         if (!tmp) {
-	    fprintf(stderr, "VFS: argc is wrong");
-	    exit(-1);
-	}
+            fprintf(stderr, "VFS: argc is wrong");
+            exit(-1);
+        }
         tmp1 = tmp;
-	while (*tmp++);
-	len = tmp - tmp1;
-	if (p < len) {  /* this shouldn't happen - 128kB */
-		return 0;
-	}
-	while (len) {
-	    --p; --tmp; --len;
-	    if (--offset < 0) {
-		offset = p % TARGET_PAGE_SIZE;
+        while (*tmp++);
+        len = tmp - tmp1;
+        if (p < len) {  /* this shouldn't happen - 128kB */
+            return 0;
+        }
+        while (len) {
+            --p; --tmp; --len;
+            if (--offset < 0) {
+                offset = p % TARGET_PAGE_SIZE;
                 pag = (char *)page[p/TARGET_PAGE_SIZE];
                 if (!pag) {
                     pag = (char *)malloc(TARGET_PAGE_SIZE);
@@ -976,20 +972,20 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
                     page[p/TARGET_PAGE_SIZE] = pag;
                     if (!pag)
                         return 0;
-		}
-	    }
-	    if (len == 0 || offset == 0) {
-	        *(pag + offset) = *tmp;
-	    }
-	    else {
-	      int bytes_to_copy = (len > offset) ? offset : len;
-	      tmp -= bytes_to_copy;
-	      p -= bytes_to_copy;
-	      offset -= bytes_to_copy;
-	      len -= bytes_to_copy;
-	      memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
-	    }
-	}
+                }
+            }
+            if (len == 0 || offset == 0) {
+                *(pag + offset) = *tmp;
+            }
+            else {
+                int bytes_to_copy = (len > offset) ? offset : len;
+                tmp -= bytes_to_copy;
+                p -= bytes_to_copy;
+                offset -= bytes_to_copy;
+                len -= bytes_to_copy;
+                memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
+            }
+        }
     }
     return p;
 }
@@ -997,332 +993,440 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
 static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm,
                                  struct image_info *info)
 {
-    abi_ulong stack_base, size, error;
+    abi_ulong stack_base, size, error, guard;
     int i;
 
     /* Create enough stack to hold everything.  If we don't use
-     * it for args, we'll use it for something else...
-     */
+       it for args, we'll use it for something else.  */
     size = guest_stack_size;
-    if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE)
+    if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) {
         size = MAX_ARG_PAGES*TARGET_PAGE_SIZE;
-    error = target_mmap(0,
-                        size + qemu_host_page_size,
-                        PROT_READ | PROT_WRITE,
-                        MAP_PRIVATE | MAP_ANONYMOUS,
-                        -1, 0);
+    }
+    guard = TARGET_PAGE_SIZE;
+    if (guard < qemu_real_host_page_size) {
+        guard = qemu_real_host_page_size;
+    }
+
+    error = target_mmap(0, size + guard, PROT_READ | PROT_WRITE,
+                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     if (error == -1) {
-        perror("stk mmap");
+        perror("mmap stack");
         exit(-1);
     }
-    /* we reserve one extra page at the top of the stack as guard */
-    target_mprotect(error + size, qemu_host_page_size, PROT_NONE);
 
-    info->stack_limit = error;
-    stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
+    /* We reserve one extra page at the top of the stack as guard.  */
+    target_mprotect(error, guard, PROT_NONE);
+
+    info->stack_limit = error + guard;
+    stack_base = info->stack_limit + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
     p += stack_base;
 
     for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
-	if (bprm->page[i]) {
-	    info->rss++;
+        if (bprm->page[i]) {
+            info->rss++;
             /* FIXME - check return value of memcpy_to_target() for failure */
-	    memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
-	    free(bprm->page[i]);
-	}
+            memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
+            free(bprm->page[i]);
+        }
         stack_base += TARGET_PAGE_SIZE;
     }
     return p;
 }
 
-static void set_brk(abi_ulong start, abi_ulong end)
-{
-	/* page-align the start and end addresses... */
-        start = HOST_PAGE_ALIGN(start);
-        end = HOST_PAGE_ALIGN(end);
-        if (end <= start)
-                return;
-        if(target_mmap(start, end - start,
-                       PROT_READ | PROT_WRITE | PROT_EXEC,
-                       MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) {
-	    perror("cannot mmap brk");
-	    exit(-1);
-	}
-}
-
-
-/* We need to explicitly zero any fractional pages after the data
-   section (i.e. bss).  This would contain the junk from the file that
-   should not be in memory. */
-static void padzero(abi_ulong elf_bss, abi_ulong last_bss)
-{
-        abi_ulong nbyte;
-
-	if (elf_bss >= last_bss)
-		return;
-
-        /* XXX: this is really a hack : if the real host page size is
-           smaller than the target page size, some pages after the end
-           of the file may not be mapped. A better fix would be to
-           patch target_mmap(), but it is more complicated as the file
-           size must be known */
-        if (qemu_real_host_page_size < qemu_host_page_size) {
-            abi_ulong end_addr, end_addr1;
-            end_addr1 = (elf_bss + qemu_real_host_page_size - 1) &
-                ~(qemu_real_host_page_size - 1);
-            end_addr = HOST_PAGE_ALIGN(elf_bss);
-            if (end_addr1 < end_addr) {
-                mmap((void *)g2h(end_addr1), end_addr - end_addr1,
-                     PROT_READ|PROT_WRITE|PROT_EXEC,
-                     MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
-            }
-        }
+/* Map and zero the bss.  We need to explicitly zero any fractional pages
+   after the data section (i.e. bss).  */
+static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot)
+{
+    uintptr_t host_start, host_map_start, host_end;
 
-        nbyte = elf_bss & (qemu_host_page_size-1);
-        if (nbyte) {
-	    nbyte = qemu_host_page_size - nbyte;
-	    do {
-                /* FIXME - what to do if put_user() fails? */
-		put_user_u8(0, elf_bss);
-                elf_bss++;
-	    } while (--nbyte);
+    last_bss = TARGET_PAGE_ALIGN(last_bss);
+
+    /* ??? There is confusion between qemu_real_host_page_size and
+       qemu_host_page_size here and elsewhere in target_mmap, which
+       may lead to the end of the data section mapping from the file
+       not being mapped.  At least there was an explicit test and
+       comment for that here, suggesting that "the file size must
+       be known".  The comment probably pre-dates the introduction
+       of the fstat system call in target_mmap which does in fact
+       find out the size.  What isn't clear is if the workaround
+       here is still actually needed.  For now, continue with it,
+       but merge it with the "normal" mmap that would allocate the bss.  */
+
+    host_start = (uintptr_t) g2h(elf_bss);
+    host_end = (uintptr_t) g2h(last_bss);
+    host_map_start = (host_start + qemu_real_host_page_size - 1);
+    host_map_start &= -qemu_real_host_page_size;
+
+    if (host_map_start < host_end) {
+        void *p = mmap((void *)host_map_start, host_end - host_map_start,
+                       prot, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        if (p == MAP_FAILED) {
+            perror("cannot mmap brk");
+            exit(-1);
         }
-}
 
+        /* Since we didn't use target_mmap, make sure to record
+           the validity of the pages with qemu.  */
+        page_set_flags(elf_bss & TARGET_PAGE_MASK, last_bss, prot|PAGE_VALID);
+    }
+
+    if (host_start < host_map_start) {
+        memset((void *)host_start, 0, host_map_start - host_start);
+    }
+}
 
 static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
-                                   struct elfhdr * exec,
-                                   abi_ulong load_addr,
-                                   abi_ulong load_bias,
-                                   abi_ulong interp_load_addr, int ibcs,
-                                   struct image_info *info)
-{
-        abi_ulong sp;
-        int size;
-        abi_ulong u_platform;
-        const char *k_platform;
-        const int n = sizeof(elf_addr_t);
-
-        sp = p;
-        u_platform = 0;
-        k_platform = ELF_PLATFORM;
-        if (k_platform) {
-            size_t len = strlen(k_platform) + 1;
-            sp -= (len + n - 1) & ~(n - 1);
-            u_platform = sp;
-            /* FIXME - check return value of memcpy_to_target() for failure */
-            memcpy_to_target(sp, k_platform, len);
-        }
-	/*
-	 * Force 16 byte _final_ alignment here for generality.
-	 */
-        sp = sp &~ (abi_ulong)15;
-        size = (DLINFO_ITEMS + 1) * 2;
-        if (k_platform)
-          size += 2;
+                                   struct elfhdr *exec,
+                                   struct image_info *info,
+                                   struct image_info *interp_info)
+{
+    abi_ulong sp;
+    int size;
+    abi_ulong u_platform;
+    const char *k_platform;
+    const int n = sizeof(elf_addr_t);
+
+    sp = p;
+    u_platform = 0;
+    k_platform = ELF_PLATFORM;
+    if (k_platform) {
+        size_t len = strlen(k_platform) + 1;
+        sp -= (len + n - 1) & ~(n - 1);
+        u_platform = sp;
+        /* FIXME - check return value of memcpy_to_target() for failure */
+        memcpy_to_target(sp, k_platform, len);
+    }
+    /*
+     * Force 16 byte _final_ alignment here for generality.
+     */
+    sp = sp &~ (abi_ulong)15;
+    size = (DLINFO_ITEMS + 1) * 2;
+    if (k_platform)
+        size += 2;
 #ifdef DLINFO_ARCH_ITEMS
-	size += DLINFO_ARCH_ITEMS * 2;
+    size += DLINFO_ARCH_ITEMS * 2;
 #endif
-        size += envc + argc + 2;
-	size += (!ibcs ? 3 : 1);	/* argc itself */
-        size *= n;
-        if (size & 15)
-            sp -= 16 - (size & 15);
-
-        /* This is correct because Linux defines
-         * elf_addr_t as Elf32_Off / Elf64_Off
-         */
-#define NEW_AUX_ENT(id, val) do {		\
-            sp -= n; put_user_ual(val, sp);	\
-            sp -= n; put_user_ual(id, sp);	\
-          } while(0)
-
-        NEW_AUX_ENT (AT_NULL, 0);
-
-        /* There must be exactly DLINFO_ITEMS entries here.  */
-        NEW_AUX_ENT(AT_PHDR, (abi_ulong)(load_addr + exec->e_phoff));
-        NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
-        NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum));
-        NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE));
-        NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_load_addr));
-        NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0);
-        NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry);
-        NEW_AUX_ENT(AT_UID, (abi_ulong) getuid());
-        NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid());
-        NEW_AUX_ENT(AT_GID, (abi_ulong) getgid());
-        NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid());
-        NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP);
-        NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK));
-        if (k_platform)
-            NEW_AUX_ENT(AT_PLATFORM, u_platform);
+    size += envc + argc + 2;
+    size += 1;  /* argc itself */
+    size *= n;
+    if (size & 15)
+        sp -= 16 - (size & 15);
+
+    /* This is correct because Linux defines
+     * elf_addr_t as Elf32_Off / Elf64_Off
+     */
+#define NEW_AUX_ENT(id, val) do {               \
+        sp -= n; put_user_ual(val, sp);         \
+        sp -= n; put_user_ual(id, sp);          \
+    } while(0)
+
+    NEW_AUX_ENT (AT_NULL, 0);
+
+    /* There must be exactly DLINFO_ITEMS entries here.  */
+    NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff));
+    NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
+    NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum));
+    NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE));
+    NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_info ? interp_info->load_addr : 0));
+    NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0);
+    NEW_AUX_ENT(AT_ENTRY, info->entry);
+    NEW_AUX_ENT(AT_UID, (abi_ulong) getuid());
+    NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid());
+    NEW_AUX_ENT(AT_GID, (abi_ulong) getgid());
+    NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid());
+    NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP);
+    NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK));
+    if (k_platform)
+        NEW_AUX_ENT(AT_PLATFORM, u_platform);
 #ifdef ARCH_DLINFO
-	/*
-	 * ARCH_DLINFO must come last so platform specific code can enforce
-	 * special alignment requirements on the AUXV if necessary (eg. PPC).
-	 */
-        ARCH_DLINFO;
+    /*
+     * ARCH_DLINFO must come last so platform specific code can enforce
+     * special alignment requirements on the AUXV if necessary (eg. PPC).
+     */
+    ARCH_DLINFO;
 #endif
 #undef NEW_AUX_ENT
 
-        info->saved_auxv = sp;
+    info->saved_auxv = sp;
 
-        sp = loader_build_argptr(envc, argc, sp, p, !ibcs);
-        return sp;
+    sp = loader_build_argptr(envc, argc, sp, p, 0);
+    return sp;
 }
 
+/* Load an ELF image into the address space.
 
-static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
-                                 int interpreter_fd,
-                                 abi_ulong *interp_load_addr)
+   IMAGE_NAME is the filename of the image, to use in error messages.
+   IMAGE_FD is the open file descriptor for the image.
+
+   BPRM_BUF is a copy of the beginning of the file; this of course
+   contains the elf file header at offset 0.  It is assumed that this
+   buffer is sufficiently aligned to present no problems to the host
+   in accessing data at aligned offsets within the buffer.
+
+   On return: INFO values will be filled in, as necessary or available.  */
+
+static void load_elf_image(const char *image_name, int image_fd,
+                           struct image_info *info, char **pinterp_name,
+                           char bprm_buf[BPRM_BUF_SIZE])
 {
-	struct elf_phdr *elf_phdata  =  NULL;
-	struct elf_phdr *eppnt;
-	abi_ulong load_addr = 0;
-	int load_addr_set = 0;
-	int retval;
-	abi_ulong last_bss, elf_bss;
-	abi_ulong error;
-	int i;
+    struct elfhdr *ehdr = (struct elfhdr *)bprm_buf;
+    struct elf_phdr *phdr;
+    abi_ulong load_addr, load_bias, loaddr, hiaddr, error;
+    int i, retval;
+    const char *errmsg;
 
-	elf_bss = 0;
-	last_bss = 0;
-	error = 0;
+    /* First of all, some simple consistency checks */
+    errmsg = "Invalid ELF image for this architecture";
+    if (!elf_check_ident(ehdr)) {
+        goto exit_errmsg;
+    }
+    bswap_ehdr(ehdr);
+    if (!elf_check_ehdr(ehdr)) {
+        goto exit_errmsg;
+    }
 
-#ifdef BSWAP_NEEDED
-        bswap_ehdr(interp_elf_ex);
+    i = ehdr->e_phnum * sizeof(struct elf_phdr);
+    if (ehdr->e_phoff + i <= BPRM_BUF_SIZE) {
+        phdr = (struct elf_phdr *)(bprm_buf + ehdr->e_phoff);
+    } else {
+        phdr = (struct elf_phdr *) alloca(i);
+        retval = pread(image_fd, phdr, i, ehdr->e_phoff);
+        if (retval != i) {
+            goto exit_read;
+        }
+    }
+    bswap_phdr(phdr, ehdr->e_phnum);
+
+    /* Find the maximum size of the image and allocate an appropriate
+       amount of memory to handle that.  */
+    loaddr = -1, hiaddr = 0;
+    for (i = 0; i < ehdr->e_phnum; ++i) {
+        if (phdr[i].p_type == PT_LOAD) {
+            abi_ulong a = phdr[i].p_vaddr;
+            if (a < loaddr) {
+                loaddr = a;
+            }
+            a += phdr[i].p_memsz;
+            if (a > hiaddr) {
+                hiaddr = a;
+            }
+        }
+    }
+
+    load_addr = loaddr;
+    if (ehdr->e_type == ET_DYN) {
+        /* The image indicates that it can be loaded anywhere.  Find a
+           location that can hold the memory space required.  If the
+           image is pre-linked, LOADDR will be non-zero.  Since we do
+           not supply MAP_FIXED here we'll use that address if and
+           only if it remains available.  */
+        load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE,
+                                MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+                                -1, 0);
+        if (load_addr == -1) {
+            goto exit_perror;
+        }
+    } else if (pinterp_name != NULL) {
+        /* This is the main executable.  Make sure that the low
+           address does not conflict with MMAP_MIN_ADDR or the
+           QEMU application itself.  */
+#if defined(CONFIG_USE_GUEST_BASE)
+        /*
+         * In case where user has not explicitly set the guest_base, we
+         * probe here that should we set it automatically.
+         */
+        if (!have_guest_base && !reserved_va) {
+            unsigned long host_start, real_start, host_size;
+
+            /* Round addresses to page boundaries.  */
+            loaddr &= qemu_host_page_mask;
+            hiaddr = HOST_PAGE_ALIGN(hiaddr);
+
+            if (loaddr < mmap_min_addr) {
+                host_start = HOST_PAGE_ALIGN(mmap_min_addr);
+            } else {
+                host_start = loaddr;
+                if (host_start != loaddr) {
+                    errmsg = "Address overflow loading ELF binary";
+                    goto exit_errmsg;
+                }
+            }
+            host_size = hiaddr - loaddr;
+            while (1) {
+                /* Do not use mmap_find_vma here because that is limited to the
+                   guest address space.  We are going to make the
+                   guest address space fit whatever we're given.  */
+                real_start = (unsigned long)
+                    mmap((void *)host_start, host_size, PROT_NONE,
+                         MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
+                if (real_start == (unsigned long)-1) {
+                    goto exit_perror;
+                }
+                if (real_start == host_start) {
+                    break;
+                }
+                /* That address didn't work.  Unmap and try a different one.
+                   The address the host picked because is typically right at
+                   the top of the host address space and leaves the guest with
+                   no usable address space.  Resort to a linear search.  We
+                   already compensated for mmap_min_addr, so this should not
+                   happen often.  Probably means we got unlucky and host
+                   address space randomization put a shared library somewhere
+                   inconvenient.  */
+                munmap((void *)real_start, host_size);
+                host_start += qemu_host_page_size;
+                if (host_start == loaddr) {
+                    /* Theoretically possible if host doesn't have any suitably
+                       aligned areas.  Normally the first mmap will fail.  */
+                    errmsg = "Unable to find space for application";
+                    goto exit_errmsg;
+                }
+            }
+            qemu_log("Relocating guest address space from 0x"
+                     TARGET_ABI_FMT_lx " to 0x%lx\n", loaddr, real_start);
+            guest_base = real_start - loaddr;
+        }
 #endif
-	/* First of all, some simple consistency checks */
-	if ((interp_elf_ex->e_type != ET_EXEC &&
-             interp_elf_ex->e_type != ET_DYN) ||
-	   !elf_check_arch(interp_elf_ex->e_machine)) {
-		return ~((abi_ulong)0UL);
-	}
+    }
+    load_bias = load_addr - loaddr;
 
+    info->load_bias = load_bias;
+    info->load_addr = load_addr;
+    info->entry = ehdr->e_entry + load_bias;
+    info->start_code = -1;
+    info->end_code = 0;
+    info->start_data = -1;
+    info->end_data = 0;
+    info->brk = 0;
 
-	/* Now read in all of the header information */
+    for (i = 0; i < ehdr->e_phnum; i++) {
+        struct elf_phdr *eppnt = phdr + i;
+        if (eppnt->p_type == PT_LOAD) {
+            abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em;
+            int elf_prot = 0;
 
-	if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE)
-	    return ~(abi_ulong)0UL;
+            if (eppnt->p_flags & PF_R) elf_prot =  PROT_READ;
+            if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+            if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
 
-	elf_phdata =  (struct elf_phdr *)
-		malloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
+            vaddr = load_bias + eppnt->p_vaddr;
+            vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr);
+            vaddr_ps = TARGET_ELF_PAGESTART(vaddr);
 
-	if (!elf_phdata)
-	  return ~((abi_ulong)0UL);
+            error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po,
+                                elf_prot, MAP_PRIVATE | MAP_FIXED,
+                                image_fd, eppnt->p_offset - vaddr_po);
+            if (error == -1) {
+                goto exit_perror;
+            }
 
-	/*
-	 * If the size of this structure has changed, then punt, since
-	 * we will be doing the wrong thing.
-	 */
-	if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) {
-	    free(elf_phdata);
-	    return ~((abi_ulong)0UL);
-        }
+            vaddr_ef = vaddr + eppnt->p_filesz;
+            vaddr_em = vaddr + eppnt->p_memsz;
 
-	retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET);
-	if(retval >= 0) {
-	    retval = read(interpreter_fd,
-			   (char *) elf_phdata,
-			   sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
-	}
-	if (retval < 0) {
-		perror("load_elf_interp");
-		exit(-1);
-		free (elf_phdata);
-		return retval;
- 	}
-#ifdef BSWAP_NEEDED
-	eppnt = elf_phdata;
-	for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
-            bswap_phdr(eppnt);
-        }
-#endif
+            /* If the load segment requests extra zeros (e.g. bss), map it.  */
+            if (vaddr_ef < vaddr_em) {
+                zero_bss(vaddr_ef, vaddr_em, elf_prot);
+            }
 
-        if (interp_elf_ex->e_type == ET_DYN) {
-            /* in order to avoid hardcoding the interpreter load
-               address in qemu, we allocate a big enough memory zone */
-            error = target_mmap(0, INTERP_MAP_SIZE,
-                                PROT_NONE, MAP_PRIVATE | MAP_ANON,
-                                -1, 0);
-            if (error == -1) {
-                perror("mmap");
-                exit(-1);
+            /* Find the full program boundaries.  */
+            if (elf_prot & PROT_EXEC) {
+                if (vaddr < info->start_code) {
+                    info->start_code = vaddr;
+                }
+                if (vaddr_ef > info->end_code) {
+                    info->end_code = vaddr_ef;
+                }
+            }
+            if (elf_prot & PROT_WRITE) {
+                if (vaddr < info->start_data) {
+                    info->start_data = vaddr;
+                }
+                if (vaddr_ef > info->end_data) {
+                    info->end_data = vaddr_ef;
+                }
+                if (vaddr_em > info->brk) {
+                    info->brk = vaddr_em;
+                }
+            }
+        } else if (eppnt->p_type == PT_INTERP && pinterp_name) {
+            char *interp_name;
+
+            if (*pinterp_name) {
+                errmsg = "Multiple PT_INTERP entries";
+                goto exit_errmsg;
+            }
+            interp_name = malloc(eppnt->p_filesz);
+            if (!interp_name) {
+                goto exit_perror;
             }
-            load_addr = error;
-            load_addr_set = 1;
+
+            if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
+                memcpy(interp_name, bprm_buf + eppnt->p_offset,
+                       eppnt->p_filesz);
+            } else {
+                retval = pread(image_fd, interp_name, eppnt->p_filesz,
+                               eppnt->p_offset);
+                if (retval != eppnt->p_filesz) {
+                    goto exit_perror;
+                }
+            }
+            if (interp_name[eppnt->p_filesz - 1] != 0) {
+                errmsg = "Invalid PT_INTERP entry";
+                goto exit_errmsg;
+            }
+            *pinterp_name = interp_name;
         }
+    }
+
+    if (info->end_data == 0) {
+        info->start_data = info->end_code;
+        info->end_data = info->end_code;
+        info->brk = info->end_code;
+    }
+
+    if (qemu_log_enabled()) {
+        load_symbols(ehdr, image_fd, load_bias);
+    }
+
+    close(image_fd);
+    return;
 
-	eppnt = elf_phdata;
-	for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
-	  if (eppnt->p_type == PT_LOAD) {
-	    int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
-	    int elf_prot = 0;
-	    abi_ulong vaddr = 0;
-	    abi_ulong k;
-
-	    if (eppnt->p_flags & PF_R) elf_prot =  PROT_READ;
-	    if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
-	    if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
-	    if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) {
-	    	elf_type |= MAP_FIXED;
-	    	vaddr = eppnt->p_vaddr;
-	    }
-	    error = target_mmap(load_addr+TARGET_ELF_PAGESTART(vaddr),
-		 eppnt->p_filesz + TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr),
-		 elf_prot,
-		 elf_type,
-		 interpreter_fd,
-		 eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr));
-
-	    if (error == -1) {
-	      /* Real error */
-	      close(interpreter_fd);
-	      free(elf_phdata);
-	      return ~((abi_ulong)0UL);
-	    }
-
-	    if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
-	      load_addr = error;
-	      load_addr_set = 1;
-	    }
-
-	    /*
-	     * Find the end of the file  mapping for this phdr, and keep
-	     * track of the largest address we see for this.
-	     */
-	    k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
-	    if (k > elf_bss) elf_bss = k;
-
-	    /*
-	     * Do the same thing for the memory mapping - between
-	     * elf_bss and last_bss is the bss section.
-	     */
-	    k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
-	    if (k > last_bss) last_bss = k;
-	  }
-
-	/* Now use mmap to map the library into memory. */
-
-	close(interpreter_fd);
-
-	/*
-	 * Now fill out the bss section.  First pad the last page up
-	 * to the page boundary, and then perform a mmap to make sure
-	 * that there are zeromapped pages up to and including the last
-	 * bss page.
-	 */
-	padzero(elf_bss, last_bss);
-	elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */
-
-	/* Map the last of the bss segment */
-	if (last_bss > elf_bss) {
-            target_mmap(elf_bss, last_bss-elf_bss,
-                        PROT_READ|PROT_WRITE|PROT_EXEC,
-                        MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
-	}
-	free(elf_phdata);
-
-	*interp_load_addr = load_addr;
-	return ((abi_ulong) interp_elf_ex->e_entry) + load_addr;
+ exit_read:
+    if (retval >= 0) {
+        errmsg = "Incomplete read of file header";
+        goto exit_errmsg;
+    }
+ exit_perror:
+    errmsg = strerror(errno);
+ exit_errmsg:
+    fprintf(stderr, "%s: %s\n", image_name, errmsg);
+    exit(-1);
+}
+
+static void load_elf_interp(const char *filename, struct image_info *info,
+                            char bprm_buf[BPRM_BUF_SIZE])
+{
+    int fd, retval;
+
+    fd = open(path(filename), O_RDONLY);
+    if (fd < 0) {
+        goto exit_perror;
+    }
+
+    retval = read(fd, bprm_buf, BPRM_BUF_SIZE);
+    if (retval < 0) {
+        goto exit_perror;
+    }
+    if (retval < BPRM_BUF_SIZE) {
+        memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval);
+    }
+
+    load_elf_image(filename, fd, info, NULL, bprm_buf);
+    return;
+
+ exit_perror:
+    fprintf(stderr, "%s: %s\n", filename, strerror(errno));
+    exit(-1);
 }
 
 static int symfind(const void *s0, const void *s1)
@@ -1371,88 +1475,97 @@ static int symcmp(const void *s0, const void *s1)
 }
 
 /* Best attempt to load symbols from this ELF object. */
-static void load_symbols(struct elfhdr *hdr, int fd)
+static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
 {
-    unsigned int i, nsyms;
-    struct elf_shdr sechdr, symtab, strtab;
+    int i, shnum, nsyms, sym_idx = 0, str_idx = 0;
+    struct elf_shdr *shdr;
     char *strings;
     struct syminfo *s;
     struct elf_sym *syms;
 
-    lseek(fd, hdr->e_shoff, SEEK_SET);
-    for (i = 0; i < hdr->e_shnum; i++) {
-        if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
-            return;
-#ifdef BSWAP_NEEDED
-        bswap_shdr(&sechdr);
-#endif
-        if (sechdr.sh_type == SHT_SYMTAB) {
-            symtab = sechdr;
-            lseek(fd, hdr->e_shoff
-                  + sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
-            if (read(fd, &strtab, sizeof(strtab))
-                != sizeof(strtab))
-                return;
-#ifdef BSWAP_NEEDED
-            bswap_shdr(&strtab);
-#endif
+    shnum = hdr->e_shnum;
+    i = shnum * sizeof(struct elf_shdr);
+    shdr = (struct elf_shdr *)alloca(i);
+    if (pread(fd, shdr, i, hdr->e_shoff) != i) {
+        return;
+    }
+
+    bswap_shdr(shdr, shnum);
+    for (i = 0; i < shnum; ++i) {
+        if (shdr[i].sh_type == SHT_SYMTAB) {
+            sym_idx = i;
+            str_idx = shdr[i].sh_link;
             goto found;
         }
     }
-    return; /* Shouldn't happen... */
+
+    /* There will be no symbol table if the file was stripped.  */
+    return;
 
  found:
-    /* Now know where the strtab and symtab are.  Snarf them. */
+    /* Now know where the strtab and symtab are.  Snarf them.  */
     s = malloc(sizeof(*s));
-    syms = malloc(symtab.sh_size);
-    if (!syms)
-        return;
-    s->disas_strtab = strings = malloc(strtab.sh_size);
-    if (!s->disas_strtab)
+    if (!s) {
         return;
+    }
 
-    lseek(fd, symtab.sh_offset, SEEK_SET);
-    if (read(fd, syms, symtab.sh_size) != symtab.sh_size)
+    i = shdr[str_idx].sh_size;
+    s->disas_strtab = strings = malloc(i);
+    if (!strings || pread(fd, strings, i, shdr[str_idx].sh_offset) != i) {
+        free(s);
+        free(strings);
         return;
+    }
 
-    nsyms = symtab.sh_size / sizeof(struct elf_sym);
+    i = shdr[sym_idx].sh_size;
+    syms = malloc(i);
+    if (!syms || pread(fd, syms, i, shdr[sym_idx].sh_offset) != i) {
+        free(s);
+        free(strings);
+        free(syms);
+        return;
+    }
 
-    i = 0;
-    while (i < nsyms) {
-#ifdef BSWAP_NEEDED
+    nsyms = i / sizeof(struct elf_sym);
+    for (i = 0; i < nsyms; ) {
         bswap_sym(syms + i);
-#endif
-        // Throw away entries which we do not need.
-        if (syms[i].st_shndx == SHN_UNDEF ||
-                syms[i].st_shndx >= SHN_LORESERVE ||
-                ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
-            nsyms--;
-            if (i < nsyms) {
+        /* Throw away entries which we do not need.  */
+        if (syms[i].st_shndx == SHN_UNDEF
+            || syms[i].st_shndx >= SHN_LORESERVE
+            || ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
+            if (i < --nsyms) {
                 syms[i] = syms[nsyms];
             }
-            continue;
-        }
+        } else {
 #if defined(TARGET_ARM) || defined (TARGET_MIPS)
-        /* The bottom address bit marks a Thumb or MIPS16 symbol.  */
-        syms[i].st_value &= ~(target_ulong)1;
+            /* The bottom address bit marks a Thumb or MIPS16 symbol.  */
+            syms[i].st_value &= ~(target_ulong)1;
 #endif
-        i++;
+            syms[i].st_value += load_bias;
+            i++;
+        }
     }
+
+    /* Attempt to free the storage associated with the local symbols
+       that we threw away.  Whether or not this has any effect on the
+       memory allocation depends on the malloc implementation and how
+       many symbols we managed to discard.  */
     syms = realloc(syms, nsyms * sizeof(*syms));
+    if (syms == NULL) {
+        free(s);
+        free(strings);
+        return;
+    }
 
     qsort(syms, nsyms, sizeof(*syms), symcmp);
 
-    lseek(fd, strtab.sh_offset, SEEK_SET);
-    if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
-        return;
     s->disas_num_syms = nsyms;
 #if ELF_CLASS == ELFCLASS32
     s->disas_symtab.elf32 = syms;
-    s->lookup_symbol = lookup_symbolxx;
 #else
     s->disas_symtab.elf64 = syms;
-    s->lookup_symbol = lookup_symbolxx;
 #endif
+    s->lookup_symbol = lookup_symbolxx;
     s->next = syminfos;
     syminfos = s;
 }
@@ -1460,477 +1573,67 @@ static void load_symbols(struct elfhdr *hdr, int fd)
 int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
                     struct image_info * info)
 {
+    struct image_info interp_info;
     struct elfhdr elf_ex;
-    struct elfhdr interp_elf_ex;
-    struct exec interp_ex;
-    int interpreter_fd = -1; /* avoid warning */
-    abi_ulong load_addr, load_bias;
-    int load_addr_set = 0;
-    unsigned int interpreter_type = INTERPRETER_NONE;
-    unsigned char ibcs2_interpreter;
-    int i;
-    abi_ulong mapped_addr;
-    struct elf_phdr * elf_ppnt;
-    struct elf_phdr *elf_phdata;
-    abi_ulong elf_bss, k, elf_brk;
-    int retval;
-    char * elf_interpreter;
-    abi_ulong elf_entry, interp_load_addr = 0;
-    int status;
-    abi_ulong start_code, end_code, start_data, end_data;
-    abi_ulong reloc_func_desc = 0;
-    abi_ulong elf_stack;
-    char passed_fileno[6];
-
-    ibcs2_interpreter = 0;
-    status = 0;
-    load_addr = 0;
-    load_bias = 0;
-    elf_ex = *((struct elfhdr *) bprm->buf);          /* exec-header */
-#ifdef BSWAP_NEEDED
-    bswap_ehdr(&elf_ex);
-#endif
-
-    /* First of all, some simple consistency checks */
-    if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
-       				(! elf_check_arch(elf_ex.e_machine))) {
-	    return -ENOEXEC;
-    }
-
-    bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
-    bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
-    bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
-    if (!bprm->p) {
-        retval = -E2BIG;
-    }
-
-    /* Now read in all of the header information */
-    elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum);
-    if (elf_phdata == NULL) {
-	return -ENOMEM;
-    }
-
-    retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET);
-    if(retval > 0) {
-	retval = read(bprm->fd, (char *) elf_phdata,
-				elf_ex.e_phentsize * elf_ex.e_phnum);
-    }
-
-    if (retval < 0) {
-	perror("load_elf_binary");
-	exit(-1);
-	free (elf_phdata);
-	return -errno;
-    }
-
-#ifdef BSWAP_NEEDED
-    elf_ppnt = elf_phdata;
-    for (i=0; i<elf_ex.e_phnum; i++, elf_ppnt++) {
-        bswap_phdr(elf_ppnt);
-    }
-#endif
-    elf_ppnt = elf_phdata;
-
-    elf_bss = 0;
-    elf_brk = 0;
-
-
-    elf_stack = ~((abi_ulong)0UL);
-    elf_interpreter = NULL;
-    start_code = ~((abi_ulong)0UL);
-    end_code = 0;
-    start_data = 0;
-    end_data = 0;
-    interp_ex.a_info = 0;
-
-    for(i=0;i < elf_ex.e_phnum; i++) {
-	if (elf_ppnt->p_type == PT_INTERP) {
-	    if ( elf_interpreter != NULL )
-	    {
-		free (elf_phdata);
-		free(elf_interpreter);
-		close(bprm->fd);
-		return -EINVAL;
-	    }
-
-	    /* This is the program interpreter used for
-	     * shared libraries - for now assume that this
-	     * is an a.out format binary
-	     */
-
-	    elf_interpreter = (char *)malloc(elf_ppnt->p_filesz);
-
-	    if (elf_interpreter == NULL) {
-		free (elf_phdata);
-		close(bprm->fd);
-		return -ENOMEM;
-	    }
-
-	    retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET);
-	    if(retval >= 0) {
-		retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz);
-	    }
-	    if(retval < 0) {
-	 	perror("load_elf_binary2");
-		exit(-1);
-	    }
-
-	    /* If the program interpreter is one of these two,
-	       then assume an iBCS2 image. Otherwise assume
-	       a native linux image. */
-
-	    /* JRP - Need to add X86 lib dir stuff here... */
-
-	    if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
-		strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) {
-	      ibcs2_interpreter = 1;
-	    }
+    char *elf_interpreter = NULL;
 
-#if 0
-	    printf("Using ELF interpreter %s\n", path(elf_interpreter));
-#endif
-	    if (retval >= 0) {
-		retval = open(path(elf_interpreter), O_RDONLY);
-		if(retval >= 0) {
-		    interpreter_fd = retval;
-		}
-		else {
-		    perror(elf_interpreter);
-		    exit(-1);
-		    /* retval = -errno; */
-		}
-	    }
-
-	    if (retval >= 0) {
-		retval = lseek(interpreter_fd, 0, SEEK_SET);
-		if(retval >= 0) {
-		    retval = read(interpreter_fd,bprm->buf,128);
-		}
-	    }
-	    if (retval >= 0) {
-		interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */
-		interp_elf_ex = *((struct elfhdr *) bprm->buf); /* elf exec-header */
-	    }
-	    if (retval < 0) {
-		perror("load_elf_binary3");
-		exit(-1);
-		free (elf_phdata);
-		free(elf_interpreter);
-		close(bprm->fd);
-		return retval;
-	    }
-	}
-	elf_ppnt++;
-    }
-
-    /* Some simple consistency checks for the interpreter */
-    if (elf_interpreter){
-	interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
-
-	/* Now figure out which format our binary is */
-	if ((N_MAGIC(interp_ex) != OMAGIC) && (N_MAGIC(interp_ex) != ZMAGIC) &&
-	    	(N_MAGIC(interp_ex) != QMAGIC)) {
-	  interpreter_type = INTERPRETER_ELF;
-	}
-
-	if (interp_elf_ex.e_ident[0] != 0x7f ||
-            strncmp((char *)&interp_elf_ex.e_ident[1], "ELF",3) != 0) {
-	    interpreter_type &= ~INTERPRETER_ELF;
-	}
-
-	if (!interpreter_type) {
-	    free(elf_interpreter);
-	    free(elf_phdata);
-	    close(bprm->fd);
-	    return -ELIBBAD;
-	}
-    }
-
-    /* OK, we are done with that, now set up the arg stuff,
-       and then start this sucker up */
-
-    {
-	char * passed_p;
-
-	if (interpreter_type == INTERPRETER_AOUT) {
-	    snprintf(passed_fileno, sizeof(passed_fileno), "%d", bprm->fd);
-	    passed_p = passed_fileno;
-
-	    if (elf_interpreter) {
-		bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p);
-		bprm->argc++;
-	    }
-	}
-	if (!bprm->p) {
-	    if (elf_interpreter) {
-	        free(elf_interpreter);
-	    }
-	    free (elf_phdata);
-	    close(bprm->fd);
-	    return -E2BIG;
-	}
-    }
-
-    /* OK, This is the point of no return */
-    info->end_data = 0;
-    info->end_code = 0;
     info->start_mmap = (abi_ulong)ELF_START_MMAP;
     info->mmap = 0;
-    elf_entry = (abi_ulong) elf_ex.e_entry;
+    info->rss = 0;
 
-#if defined(CONFIG_USE_GUEST_BASE)
-    /*
-     * In case where user has not explicitly set the guest_base, we
-     * probe here that should we set it automatically.
-     */
-    if (!(have_guest_base || reserved_va)) {
-        /*
-         * Go through ELF program header table and find the address
-         * range used by loadable segments.  Check that this is available on
-         * the host, and if not find a suitable value for guest_base.  */
-        abi_ulong app_start = ~0;
-        abi_ulong app_end = 0;
-        abi_ulong addr;
-        unsigned long host_start;
-        unsigned long real_start;
-        unsigned long host_size;
-        for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum;
-            i++, elf_ppnt++) {
-            if (elf_ppnt->p_type != PT_LOAD)
-                continue;
-            addr = elf_ppnt->p_vaddr;
-            if (addr < app_start) {
-                app_start = addr;
-            }
-            addr += elf_ppnt->p_memsz;
-            if (addr > app_end) {
-                app_end = addr;
-            }
-        }
+    load_elf_image(bprm->filename, bprm->fd, info,
+                   &elf_interpreter, bprm->buf);
 
-        /* If we don't have any loadable segments then something
-           is very wrong.  */
-        assert(app_start < app_end);
+    /* ??? We need a copy of the elf header for passing to create_elf_tables.
+       If we do nothing, we'll have overwritten this when we re-use bprm->buf
+       when we load the interpreter.  */
+    elf_ex = *(struct elfhdr *)bprm->buf;
 
-        /* Round addresses to page boundaries.  */
-        app_start = app_start & qemu_host_page_mask;
-        app_end = HOST_PAGE_ALIGN(app_end);
-        if (app_start < mmap_min_addr) {
-            host_start = HOST_PAGE_ALIGN(mmap_min_addr);
-        } else {
-            host_start = app_start;
-            if (host_start != app_start) {
-                fprintf(stderr, "qemu: Address overflow loading ELF binary\n");
-                abort();
-            }
-        }
-        host_size = app_end - app_start;
-        while (1) {
-            /* Do not use mmap_find_vma here because that is limited to the
-               guest address space.  We are going to make the
-               guest address space fit whatever we're given.  */
-            real_start = (unsigned long)mmap((void *)host_start, host_size,
-                PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
-            if (real_start == (unsigned long)-1) {
-                fprintf(stderr, "qemu: Virtual memory exausted\n");
-                abort();
-            }
-            if (real_start == host_start) {
-                break;
-            }
-            /* That address didn't work.  Unmap and try a different one.
-               The address the host picked because is typically
-               right at the top of the host address space and leaves the
-               guest with no usable address space.  Resort to a linear search.
-               We already compensated for mmap_min_addr, so this should not
-               happen often.  Probably means we got unlucky and host address
-               space randomization put a shared library somewhere
-               inconvenient.  */
-            munmap((void *)real_start, host_size);
-            host_start += qemu_host_page_size;
-            if (host_start == app_start) {
-                /* Theoretically possible if host doesn't have any
-                   suitably aligned areas.  Normally the first mmap will
-                   fail.  */
-                fprintf(stderr, "qemu: Unable to find space for application\n");
-                abort();
-            }
-        }
-        qemu_log("Relocating guest address space from 0x" TARGET_ABI_FMT_lx
-                 " to 0x%lx\n", app_start, real_start);
-        guest_base = real_start - app_start;
+    bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
+    bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
+    bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
+    if (!bprm->p) {
+        fprintf(stderr, "%s: %s\n", bprm->filename, strerror(E2BIG));
+        exit(-1);
     }
-#endif /* CONFIG_USE_GUEST_BASE */
 
     /* Do this so that we can load the interpreter, if need be.  We will
        change some of these later */
-    info->rss = 0;
     bprm->p = setup_arg_pages(bprm->p, bprm, info);
-    info->start_stack = bprm->p;
 
-    /* Now we do a little grungy work by mmaping the ELF image into
-     * the correct location in memory.  At this point, we assume that
-     * the image should be loaded at fixed address, not at a variable
-     * address.
-     */
+    if (elf_interpreter) {
+        load_elf_interp(elf_interpreter, &interp_info, bprm->buf);
 
-    for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
-        int elf_prot = 0;
-        int elf_flags = 0;
-        abi_ulong error;
+        /* If the program interpreter is one of these two, then assume
+           an iBCS2 image.  Otherwise assume a native linux image.  */
 
-	if (elf_ppnt->p_type != PT_LOAD)
-            continue;
+        if (strcmp(elf_interpreter, "/usr/lib/libc.so.1") == 0
+            || strcmp(elf_interpreter, "/usr/lib/ld.so.1") == 0) {
+            info->personality = PER_SVR4;
 
-        if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
-        if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
-        if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
-        elf_flags = MAP_PRIVATE | MAP_DENYWRITE;
-        if (elf_ex.e_type == ET_EXEC || load_addr_set) {
-            elf_flags |= MAP_FIXED;
-        } else if (elf_ex.e_type == ET_DYN) {
-            /* Try and get dynamic programs out of the way of the default mmap
-               base, as well as whatever program they might try to exec.  This
-               is because the brk will follow the loader, and is not movable.  */
-            /* NOTE: for qemu, we do a big mmap to get enough space
-               without hardcoding any address */
-            error = target_mmap(0, ET_DYN_MAP_SIZE,
-                                PROT_NONE, MAP_PRIVATE | MAP_ANON,
-                                -1, 0);
-            if (error == -1) {
-                perror("mmap");
-                exit(-1);
-            }
-            load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr);
+            /* Why this, you ask???  Well SVr4 maps page 0 as read-only,
+               and some applications "depend" upon this behavior.  Since
+               we do not have the power to recompile these, we emulate
+               the SVr4 behavior.  Sigh.  */
+            target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
+                        MAP_FIXED | MAP_PRIVATE, -1, 0);
         }
-
-        error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr),
-                            (elf_ppnt->p_filesz +
-                             TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
-                            elf_prot,
-                            (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),
-                            bprm->fd,
-                            (elf_ppnt->p_offset -
-                             TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
-        if (error == -1) {
-            perror("mmap");
-            exit(-1);
-        }
-
-#ifdef LOW_ELF_STACK
-        if (TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)
-            elf_stack = TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr);
-#endif
-
-        if (!load_addr_set) {
-            load_addr_set = 1;
-            load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
-            if (elf_ex.e_type == ET_DYN) {
-                load_bias += error -
-                    TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr);
-                load_addr += load_bias;
-                reloc_func_desc = load_bias;
-            }
-        }
-        k = elf_ppnt->p_vaddr;
-        if (k < start_code)
-            start_code = k;
-        if (start_data < k)
-            start_data = k;
-        k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
-        if (k > elf_bss)
-            elf_bss = k;
-        if ((elf_ppnt->p_flags & PF_X) && end_code <  k)
-            end_code = k;
-        if (end_data < k)
-            end_data = k;
-        k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
-        if (k > elf_brk) elf_brk = k;
-    }
-
-    elf_entry += load_bias;
-    elf_bss += load_bias;
-    elf_brk += load_bias;
-    start_code += load_bias;
-    end_code += load_bias;
-    start_data += load_bias;
-    end_data += load_bias;
-
-    if (elf_interpreter) {
-	if (interpreter_type & 1) {
-	    elf_entry = load_aout_interp(&interp_ex, interpreter_fd);
-	}
-	else if (interpreter_type & 2) {
-	    elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd,
-					    &interp_load_addr);
-	}
-        reloc_func_desc = interp_load_addr;
-
-	close(interpreter_fd);
-	free(elf_interpreter);
-
-	if (elf_entry == ~((abi_ulong)0UL)) {
-	    printf("Unable to load interpreter\n");
-	    free(elf_phdata);
-	    exit(-1);
-	    return 0;
-	}
     }
 
-    free(elf_phdata);
-
-    if (qemu_log_enabled())
-	load_symbols(&elf_ex, bprm->fd);
-
-    if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd);
-    info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
-
-#ifdef LOW_ELF_STACK
-    info->start_stack = bprm->p = elf_stack - 4;
-#endif
-    bprm->p = create_elf_tables(bprm->p,
-		    bprm->argc,
-		    bprm->envc,
-                    &elf_ex,
-                    load_addr, load_bias,
-		    interp_load_addr,
-		    (interpreter_type == INTERPRETER_AOUT ? 0 : 1),
-		    info);
-    info->load_addr = reloc_func_desc;
-    info->start_brk = info->brk = elf_brk;
-    info->end_code = end_code;
-    info->start_code = start_code;
-    info->start_data = start_data;
-    info->end_data = end_data;
+    bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex,
+                                info, (elf_interpreter ? &interp_info : NULL));
     info->start_stack = bprm->p;
 
-    /* Calling set_brk effectively mmaps the pages that we need for the bss and break
-       sections */
-    set_brk(elf_bss, elf_brk);
-
-    padzero(elf_bss, elf_brk);
-
-#if 0
-    printf("(start_brk) %x\n" , info->start_brk);
-    printf("(end_code) %x\n" , info->end_code);
-    printf("(start_code) %x\n" , info->start_code);
-    printf("(end_data) %x\n" , info->end_data);
-    printf("(start_stack) %x\n" , info->start_stack);
-    printf("(brk) %x\n" , info->brk);
-#endif
-
-    if ( info->personality == PER_SVR4 )
-    {
-	    /* Why this, you ask???  Well SVr4 maps page 0 as read-only,
-	       and some applications "depend" upon this behavior.
-	       Since we do not have the power to recompile these, we
-	       emulate the SVr4 behavior.  Sigh.  */
-	    mapped_addr = target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
-                                      MAP_FIXED | MAP_PRIVATE, -1, 0);
+    /* If we have an interpreter, set that as the program's entry point.
+       Copy the load_addr as well, to help PPC64 interpret the entry
+       point as a function descriptor.  Do this after creating elf tables
+       so that we copy the original program entry point into the AUXV.  */
+    if (elf_interpreter) {
+        info->load_addr = interp_info.load_addr;
+        info->entry = interp_info.entry;
+        free(elf_interpreter);
     }
 
-    info->entry = elf_entry;
-
 #ifdef USE_ELF_CORE_DUMP
     bprm->core_dump = &elf_core_dump;
 #endif
@@ -1939,7 +1642,6 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
 }
 
 #ifdef USE_ELF_CORE_DUMP
-
 /*
  * Definitions to generate Intel SVR4-like core files.
  * These mostly have the same names as the SVR4 types with "target_elf_"
@@ -2076,17 +1778,17 @@ struct mm_struct {
 static struct mm_struct *vma_init(void);
 static void vma_delete(struct mm_struct *);
 static int vma_add_mapping(struct mm_struct *, abi_ulong,
-    abi_ulong, abi_ulong);
+                           abi_ulong, abi_ulong);
 static int vma_get_mapping_count(const struct mm_struct *);
 static struct vm_area_struct *vma_first(const struct mm_struct *);
 static struct vm_area_struct *vma_next(struct vm_area_struct *);
 static abi_ulong vma_dump_size(const struct vm_area_struct *);
 static int vma_walker(void *priv, abi_ulong start, abi_ulong end,
-    unsigned long flags);
+                      unsigned long flags);
 
 static void fill_elf_header(struct elfhdr *, int, uint16_t, uint32_t);
 static void fill_note(struct memelfnote *, const char *, int,
-    unsigned int, void *);
+                      unsigned int, void *);
 static void fill_prstatus(struct target_elf_prstatus *, const TaskState *, int);
 static int fill_psinfo(struct target_elf_prpsinfo *, const TaskState *);
 static void fill_auxv_note(struct memelfnote *, const TaskState *);
@@ -2102,9 +1804,6 @@ static int write_note(struct memelfnote *, int);
 static int write_note_info(struct elf_note_info *, int);
 
 #ifdef BSWAP_NEEDED
-static void bswap_prstatus(struct target_elf_prstatus *);
-static void bswap_psinfo(struct target_elf_prpsinfo *);
-
 static void bswap_prstatus(struct target_elf_prstatus *prstatus)
 {
     prstatus->pr_info.si_signo = tswapl(prstatus->pr_info.si_signo);
@@ -2132,6 +1831,17 @@ static void bswap_psinfo(struct target_elf_prpsinfo *psinfo)
     psinfo->pr_pgrp = tswap32(psinfo->pr_pgrp);
     psinfo->pr_sid = tswap32(psinfo->pr_sid);
 }
+
+static void bswap_note(struct elf_note *en)
+{
+    bswap32s(&en->n_namesz);
+    bswap32s(&en->n_descsz);
+    bswap32s(&en->n_type);
+}
+#else
+static inline void bswap_prstatus(struct target_elf_prstatus *p) { }
+static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {}
+static inline void bswap_note(struct elf_note *en) { }
 #endif /* BSWAP_NEEDED */
 
 /*
@@ -2166,7 +1876,7 @@ static void vma_delete(struct mm_struct *mm)
 }
 
 static int vma_add_mapping(struct mm_struct *mm, abi_ulong start,
-    abi_ulong end, abi_ulong flags)
+                           abi_ulong end, abi_ulong flags)
 {
     struct vm_area_struct *vma;
 
@@ -2235,7 +1945,7 @@ static abi_ulong vma_dump_size(const struct vm_area_struct *vma)
 }
 
 static int vma_walker(void *priv, abi_ulong start, abi_ulong end,
-    unsigned long flags)
+                      unsigned long flags)
 {
     struct mm_struct *mm = (struct mm_struct *)priv;
 
@@ -2244,7 +1954,7 @@ static int vma_walker(void *priv, abi_ulong start, abi_ulong end,
 }
 
 static void fill_note(struct memelfnote *note, const char *name, int type,
-    unsigned int sz, void *data)
+                      unsigned int sz, void *data)
 {
     unsigned int namesz;
 
@@ -2265,7 +1975,7 @@ static void fill_note(struct memelfnote *note, const char *name, int type,
 }
 
 static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine,
-    uint32_t flags)
+                            uint32_t flags)
 {
     (void) memset(elf, 0, sizeof(*elf));
 
@@ -2284,9 +1994,7 @@ static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine,
     elf->e_phentsize = sizeof(struct elf_phdr);
     elf->e_phnum = segs;
 
-#ifdef BSWAP_NEEDED
     bswap_ehdr(elf);
-#endif
 }
 
 static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
@@ -2300,9 +2008,7 @@ static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
     phdr->p_flags = 0;
     phdr->p_align = 0;
 
-#ifdef BSWAP_NEEDED
-    bswap_phdr(phdr);
-#endif
+    bswap_phdr(phdr, 1);
 }
 
 static size_t note_size(const struct memelfnote *note)
@@ -2311,7 +2017,7 @@ static size_t note_size(const struct memelfnote *note)
 }
 
 static void fill_prstatus(struct target_elf_prstatus *prstatus,
-    const TaskState *ts, int signr)
+                          const TaskState *ts, int signr)
 {
     (void) memset(prstatus, 0, sizeof (*prstatus));
     prstatus->pr_info.si_signo = prstatus->pr_cursig = signr;
@@ -2320,9 +2026,7 @@ static void fill_prstatus(struct target_elf_prstatus *prstatus,
     prstatus->pr_pgrp = getpgrp();
     prstatus->pr_sid = getsid(0);
 
-#ifdef BSWAP_NEEDED
     bswap_prstatus(prstatus);
-#endif
 }
 
 static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts)
@@ -2352,13 +2056,11 @@ static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts)
     filename = strdup(ts->bprm->filename);
     base_filename = strdup(basename(filename));
     (void) strncpy(psinfo->pr_fname, base_filename,
-        sizeof(psinfo->pr_fname));
+                   sizeof(psinfo->pr_fname));
     free(base_filename);
     free(filename);
 
-#ifdef BSWAP_NEEDED
     bswap_psinfo(psinfo);
-#endif
     return (0);
 }
 
@@ -2401,7 +2103,7 @@ static void fill_auxv_note(struct memelfnote *note, const TaskState *ts)
  * Returns 0 in case of success, -1 otherwise (errno is set).
  */
 static int core_dump_filename(const TaskState *ts, char *buf,
-    size_t bufsize)
+                              size_t bufsize)
 {
     char timestamp[64];
     char *filename = NULL;
@@ -2413,16 +2115,16 @@ static int core_dump_filename(const TaskState *ts, char *buf,
 
     if (gettimeofday(&tv, NULL) < 0) {
         (void) fprintf(stderr, "unable to get current timestamp: %s",
-            strerror(errno));
+                       strerror(errno));
         return (-1);
     }
 
     filename = strdup(ts->bprm->filename);
     base_filename = strdup(basename(filename));
     (void) strftime(timestamp, sizeof (timestamp), "%Y%m%d-%H%M%S",
-        localtime_r(&tv.tv_sec, &tm));
+                    localtime_r(&tv.tv_sec, &tm));
     (void) snprintf(buf, bufsize, "qemu_%s_%s_%d.core",
-        base_filename, timestamp, (int)getpid());
+                    base_filename, timestamp, (int)getpid());
     free(base_filename);
     free(filename);
 
@@ -2483,9 +2185,7 @@ static int write_note(struct memelfnote *men, int fd)
     en.n_type = men->type;
     en.n_descsz = men->datasz;
 
-#ifdef BSWAP_NEEDED
     bswap_note(&en);
-#endif
 
     if (dump_write(fd, &en, sizeof(en)) != 0)
         return (-1);
@@ -2507,7 +2207,7 @@ static void fill_thread_info(struct elf_note_info *info, const CPUState *env)
     fill_prstatus(&ets->prstatus, ts, 0);
     elf_core_copy_regs(&ets->prstatus.pr_reg, env);
     fill_note(&ets->notes[0], "CORE", NT_PRSTATUS, sizeof (ets->prstatus),
-        &ets->prstatus);
+              &ets->prstatus);
 
     QTAILQ_INSERT_TAIL(&info->thread_list, ets, ets_link);
 
@@ -2515,7 +2215,7 @@ static void fill_thread_info(struct elf_note_info *info, const CPUState *env)
 }
 
 static int fill_note_info(struct elf_note_info *info,
-    long signr, const CPUState *env)
+                          long signr, const CPUState *env)
 {
 #define NUMNOTES 3
     CPUState *cpu = NULL;
@@ -2543,10 +2243,10 @@ static int fill_note_info(struct elf_note_info *info,
     fill_prstatus(info->prstatus, ts, signr);
     elf_core_copy_regs(&info->prstatus->pr_reg, env);
     fill_note(&info->notes[0], "CORE", NT_PRSTATUS,
-        sizeof (*info->prstatus), info->prstatus);
+              sizeof (*info->prstatus), info->prstatus);
     fill_psinfo(info->psinfo, ts);
     fill_note(&info->notes[1], "CORE", NT_PRPSINFO,
-        sizeof (*info->psinfo), info->psinfo);
+              sizeof (*info->psinfo), info->psinfo);
     fill_auxv_note(&info->notes[2], ts);
     info->numnote = 3;
 
@@ -2593,7 +2293,7 @@ static int write_note_info(struct elf_note_info *info, int fd)
 
     /* write prstatus for each thread */
     for (ets = info->thread_list.tqh_first; ets != NULL;
-        ets = ets->ets_link.tqe_next) {
+         ets = ets->ets_link.tqe_next) {
         if ((error = write_note(&ets->notes[0], fd)) != 0)
             return (error);
     }
@@ -2661,13 +2361,13 @@ static int elf_core_dump(int signr, const CPUState *env)
     errno = 0;
     getrlimit(RLIMIT_CORE, &dumpsize);
     if (dumpsize.rlim_cur == 0)
-       return 0;
+        return 0;
 
     if (core_dump_filename(ts, corefile, sizeof (corefile)) < 0)
         return (-errno);
 
     if ((fd = open(corefile, O_WRONLY | O_CREAT,
-        S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
+                   S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
         return (-errno);
 
     /*
@@ -2756,7 +2456,7 @@ static int elf_core_dump(int signr, const CPUState *env)
         end = vma->vma_start + vma_dump_size(vma);
 
         for (addr = vma->vma_start; addr < end;
-            addr += TARGET_PAGE_SIZE) {
+             addr += TARGET_PAGE_SIZE) {
             char page[TARGET_PAGE_SIZE];
             int error;
 
@@ -2767,7 +2467,7 @@ static int elf_core_dump(int signr, const CPUState *env)
             error = copy_from_user(page, addr, sizeof (page));
             if (error != 0) {
                 (void) fprintf(stderr, "unable to dump " TARGET_ABI_FMT_lx "\n",
-                    addr);
+                               addr);
                 errno = -error;
                 goto out;
             }
@@ -2776,7 +2476,7 @@ static int elf_core_dump(int signr, const CPUState *env)
         }
     }
 
-out:
+ out:
     free_note_info(&info);
     if (mm != NULL)
         vma_delete(mm);
@@ -2786,15 +2486,8 @@ out:
         return (-errno);
     return (0);
 }
-
 #endif /* USE_ELF_CORE_DUMP */
 
-static int load_aout_interp(void * exptr, int interp_fd)
-{
-    printf("a.out interpreter not yet supported\n");
-    return(0);
-}
-
 void do_init_thread(struct target_pt_regs *regs, struct image_info *infop)
 {
     init_thread(regs, infop);
diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c
index 13ad9aaebe..9ee27c3558 100644
--- a/linux-user/linuxload.c
+++ b/linux-user/linuxload.c
@@ -96,18 +96,16 @@ static int prepare_binprm(struct linux_binprm *bprm)
 	}
     }
 
-    retval = lseek(bprm->fd, 0L, SEEK_SET);
-    if(retval >= 0) {
-        retval = read(bprm->fd, bprm->buf, 128);
-    }
-    if(retval < 0) {
+    retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE);
+    if (retval < 0) {
 	perror("prepare_binprm");
 	exit(-1);
-	/* return(-errno); */
     }
-    else {
-	return(retval);
+    if (retval < BPRM_BUF_SIZE) {
+        /* Make sure the rest of the loader won't read garbage.  */
+        memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval);
     }
+    return retval;
 }
 
 /* Construct the envp and argv tables on the target stack.  */
@@ -163,8 +161,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
     int i;
 
     bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
-    for (i=0 ; i<MAX_ARG_PAGES ; i++)       /* clear page-table */
-            bprm->page[i] = NULL;
+    memset(bprm->page, 0, sizeof(bprm->page));
     retval = open(filename, O_RDONLY);
     if (retval < 0)
         return retval;
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 39da6dfb40..e10a6ef2e2 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -225,13 +225,13 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
     int prot;
     int looped = 0;
 
-    if (size > reserved_va) {
+    if (size > RESERVED_VA) {
         return (abi_ulong)-1;
     }
 
     last_addr = start;
     for (addr = start; last_addr + size != addr; addr += qemu_host_page_size) {
-        if (last_addr + size >= reserved_va
+        if (last_addr + size >= RESERVED_VA
             || (abi_ulong)(last_addr + size) < last_addr) {
             if (looped) {
                 return (abi_ulong)-1;
@@ -271,7 +271,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
 
     size = HOST_PAGE_ALIGN(size);
 
-    if (reserved_va) {
+    if (RESERVED_VA) {
         return mmap_find_vma_reserved(start, size);
     }
 
@@ -651,7 +651,7 @@ int target_munmap(abi_ulong start, abi_ulong len)
     ret = 0;
     /* unmap what we can */
     if (real_start < real_end) {
-        if (reserved_va) {
+        if (RESERVED_VA) {
             mmap_reserve(real_start, real_end - real_start);
         } else {
             ret = munmap(g2h(real_start), real_end - real_start);
@@ -679,7 +679,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
                                      flags,
                                      g2h(new_addr));
 
-        if (reserved_va && host_addr != MAP_FAILED) {
+        if (RESERVED_VA && host_addr != MAP_FAILED) {
             /* If new and old addresses overlap then the above mremap will
                already have failed with EINVAL.  */
             mmap_reserve(old_addr, old_size);
@@ -701,7 +701,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
         }
     } else {
         int prot = 0;
-        if (reserved_va && old_size < new_size) {
+        if (RESERVED_VA && old_size < new_size) {
             abi_ulong addr;
             for (addr = old_addr + old_size;
                  addr < old_addr + new_size;
@@ -711,7 +711,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
         }
         if (prot == 0) {
             host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
-            if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
+            if (host_addr != MAP_FAILED && RESERVED_VA && old_size > new_size) {
                 mmap_reserve(old_addr + old_size, new_size - old_size);
             }
         } else {
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 1878d5a61e..794fe49133 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -31,6 +31,7 @@
  * task_struct fields in the kernel
  */
 struct image_info {
+        abi_ulong       load_bias;
         abi_ulong       load_addr;
         abi_ulong       start_code;
         abi_ulong       end_code;
@@ -144,12 +145,16 @@ extern unsigned long mmap_min_addr;
  */
 #define MAX_ARG_PAGES 33
 
+/* Read a good amount of data initially, to hopefully get all the
+   program headers loaded.  */
+#define BPRM_BUF_SIZE  1024
+
 /*
  * This structure is used to hold the arguments that are
  * used when loading binaries.
  */
 struct linux_binprm {
-        char buf[128];
+        char buf[BPRM_BUF_SIZE] __attribute__((aligned));
         void *page[MAX_ARG_PAGES];
         abi_ulong p;
 	int fd;
diff --git a/qemu-options.hx b/qemu-options.hx
index 9ecc54e6ed..db86feb09b 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -835,6 +835,13 @@ empty, with a @code{deny} policy. Thus no one will be allowed to
 use the VNC server until the ACLs have been loaded. This can be
 achieved using the @code{acl} monitor command.
 
+@item lossy
+
+Enable lossy compression methods (gradient, JPEG, ...). If this
+option is set, VNC client may receive lossy framebuffer updates
+depending on its encoding settings. Enabling this option can save
+a lot of bandwidth at the expense of quality.
+
 @end table
 ETEXI
 
diff --git a/qemu-thread.c b/qemu-thread.c
index faf406142d..fbc78fef4b 100644
--- a/qemu-thread.c
+++ b/qemu-thread.c
@@ -34,6 +34,15 @@ void qemu_mutex_init(QemuMutex *mutex)
         error_exit(err, __func__);
 }
 
+void qemu_mutex_destroy(QemuMutex *mutex)
+{
+    int err;
+
+    err = pthread_mutex_destroy(&mutex->lock);
+    if (err)
+        error_exit(err, __func__);
+}
+
 void qemu_mutex_lock(QemuMutex *mutex)
 {
     int err;
@@ -90,6 +99,15 @@ void qemu_cond_init(QemuCond *cond)
         error_exit(err, __func__);
 }
 
+void qemu_cond_destroy(QemuCond *cond)
+{
+    int err;
+
+    err = pthread_cond_destroy(&cond->cond);
+    if (err)
+        error_exit(err, __func__);
+}
+
 void qemu_cond_signal(QemuCond *cond)
 {
     int err;
@@ -168,3 +186,7 @@ int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2)
    return pthread_equal(thread1->thread, thread2->thread);
 }
 
+void qemu_thread_exit(void *retval)
+{
+    pthread_exit(retval);
+}
diff --git a/qemu-thread.h b/qemu-thread.h
index 5ef4a3aed5..19bb30c940 100644
--- a/qemu-thread.h
+++ b/qemu-thread.h
@@ -20,12 +20,14 @@ typedef struct QemuCond QemuCond;
 typedef struct QemuThread QemuThread;
 
 void qemu_mutex_init(QemuMutex *mutex);
+void qemu_mutex_destroy(QemuMutex *mutex);
 void qemu_mutex_lock(QemuMutex *mutex);
 int qemu_mutex_trylock(QemuMutex *mutex);
 int qemu_mutex_timedlock(QemuMutex *mutex, uint64_t msecs);
 void qemu_mutex_unlock(QemuMutex *mutex);
 
 void qemu_cond_init(QemuCond *cond);
+void qemu_cond_destroy(QemuCond *cond);
 void qemu_cond_signal(QemuCond *cond);
 void qemu_cond_broadcast(QemuCond *cond);
 void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex);
@@ -37,4 +39,6 @@ void qemu_thread_create(QemuThread *thread,
 void qemu_thread_signal(QemuThread *thread, int sig);
 void qemu_thread_self(QemuThread *thread);
 int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2);
+void qemu_thread_exit(void *retval);
+
 #endif
diff --git a/savevm.c b/savevm.c
index ee279895cd..7a1de3c532 100644
--- a/savevm.c
+++ b/savevm.c
@@ -551,6 +551,19 @@ int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
     return size1 - size;
 }
 
+static int qemu_peek_byte(QEMUFile *f)
+{
+    if (f->is_write)
+        abort();
+
+    if (f->buf_index >= f->buf_size) {
+        qemu_fill_buffer(f);
+        if (f->buf_index >= f->buf_size)
+            return 0;
+    }
+    return f->buf[f->buf_index];
+}
+
 int qemu_get_byte(QEMUFile *f)
 {
     if (f->is_write)
@@ -1198,10 +1211,16 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
     }
 }
 
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+                                    void *opaque);
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+                                   void *opaque);
+
 int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
                        void *opaque, int version_id)
 {
     VMStateField *field = vmsd->fields;
+    int ret;
 
     if (version_id > vmsd->version_id) {
         return -EINVAL;
@@ -1223,7 +1242,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
             (!field->field_exists &&
              field->version_id <= version_id)) {
             void *base_addr = opaque + field->offset;
-            int ret, i, n_elems = 1;
+            int i, n_elems = 1;
             int size = field->size;
 
             if (field->flags & VMS_VBUFFER) {
@@ -1261,6 +1280,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
         }
         field++;
     }
+    ret = vmstate_subsection_load(f, vmsd, opaque);
+    if (ret != 0) {
+        return ret;
+    }
     if (vmsd->post_load) {
         return vmsd->post_load(opaque, version_id);
     }
@@ -1313,6 +1336,7 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
         }
         field++;
     }
+    vmstate_subsection_save(f, vmsd, opaque);
 }
 
 static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
@@ -1341,6 +1365,7 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
 #define QEMU_VM_SECTION_PART         0x02
 #define QEMU_VM_SECTION_END          0x03
 #define QEMU_VM_SECTION_FULL         0x04
+#define QEMU_VM_SUBSECTION           0x05
 
 int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
                             int shared)
@@ -1529,6 +1554,65 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
     return NULL;
 }
 
+static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection *sub, char *idstr)
+{
+    while(sub && sub->needed) {
+        if (strcmp(idstr, sub->vmsd->name) == 0) {
+            return sub->vmsd;
+        }
+        sub++;
+    }
+    return NULL;
+}
+
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+                                   void *opaque)
+{
+    while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) {
+        char idstr[256];
+        int ret;
+        uint8_t version_id, subsection, len;
+        const VMStateDescription *sub_vmsd;
+
+        subsection = qemu_get_byte(f);
+        len = qemu_get_byte(f);
+        qemu_get_buffer(f, (uint8_t *)idstr, len);
+        idstr[len] = 0;
+        version_id = qemu_get_be32(f);
+
+        sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
+        if (sub_vmsd == NULL) {
+            return -ENOENT;
+        }
+        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
+        if (ret) {
+            return ret;
+        }
+    }
+    return 0;
+}
+
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+                                    void *opaque)
+{
+    const VMStateSubsection *sub = vmsd->subsections;
+
+    while (sub && sub->needed) {
+        if (sub->needed(opaque)) {
+            const VMStateDescription *vmsd = sub->vmsd;
+            uint8_t len;
+
+            qemu_put_byte(f, QEMU_VM_SUBSECTION);
+            len = strlen(vmsd->name);
+            qemu_put_byte(f, len);
+            qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
+            qemu_put_be32(f, vmsd->version_id);
+            vmstate_save_state(f, vmsd, opaque);
+        }
+        sub++;
+    }
+}
+
 typedef struct LoadStateEntry {
     QLIST_ENTRY(LoadStateEntry) entry;
     SaveStateEntry *se;
diff --git a/tests/cris/check_addo.c b/tests/cris/check_addo.c
index 8a0565a1a9..3d8e789f5a 100644
--- a/tests/cris/check_addo.c
+++ b/tests/cris/check_addo.c
@@ -85,7 +85,7 @@ int main(void)
 	cris_tst_cc_init();
 	asm volatile ("setf\tzvnc\n");
 	cris_addo_pi_b(p, t);
-	cris_tst_cc(1, 1, 1, 1);
+	cris_tst_cc(0, 0, 0, 0);
 	asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
 	if (*(uint16_t*)r != 0xff22)
 		err();
@@ -114,7 +114,7 @@ int main(void)
 	cris_tst_cc_init();
 	asm volatile ("setf\tzvnc\n");
 	cris_addo_pi_d(p, t);
-	cris_tst_cc(1, 1, 1, 1);
+	cris_tst_cc(0, 0, 0, 0);
 	asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
 	r = (void*)(((char *)r) + 76789885);
 	if (*r != 0x55aa77ff)
diff --git a/tests/cris/check_addoq.c b/tests/cris/check_addoq.c
index b8b15c3090..ed509e27e0 100644
--- a/tests/cris/check_addoq.c
+++ b/tests/cris/check_addoq.c
@@ -27,7 +27,7 @@ int main(void)
 	cris_tst_cc_init();
 	asm volatile ("setf\tzvnc\n");
 	cris_addoq(4, t);
-	cris_tst_cc(1, 1, 1, 1);
+	cris_tst_cc(0, 0, 0, 0);
 	asm volatile ("move.d\t$acr, %0\n" : "=r" (p));
 	if (*p != 0x88ccee19)
 		err();
diff --git a/tests/cris/check_settls1.c b/tests/cris/check_settls1.c
index 0ed99cf4c1..69d202652a 100644
--- a/tests/cris/check_settls1.c
+++ b/tests/cris/check_settls1.c
@@ -11,11 +11,15 @@
 
 int main (void)
 {
-    unsigned long tp;
+    unsigned long tp, old_tp;
     int ret;
 
+    asm volatile ("move $pid,%0" : "=r" (old_tp));
+    old_tp &= ~0xff;
+
     ret = syscall (SYS_set_thread_area, 0xf0);
     if (ret != -1 || errno != EINVAL) {
+        syscall (SYS_set_thread_area, old_tp);
         perror ("Invalid thread area accepted:");
         abort();
     }
@@ -26,10 +30,12 @@ int main (void)
         abort ();
     }
 
-    asm ("move $pid,%0" : "=r" (tp));
+    asm volatile ("move $pid,%0" : "=r" (tp));
     tp &= ~0xff;
+    syscall (SYS_set_thread_area, old_tp);
 
     if (tp != 0xeddeed00) {
+	* (volatile int *) 0 = 0;
         perror ("tls2");
         abort ();
     }
diff --git a/cocoa.m b/ui/cocoa.m
index 56c789a98c..56c789a98c 100644
--- a/cocoa.m
+++ b/ui/cocoa.m
diff --git a/curses.c b/ui/curses.c
index ed3165e45e..ed3165e45e 100644
--- a/curses.c
+++ b/ui/curses.c
diff --git a/curses_keys.h b/ui/curses_keys.h
index 1decd1119d..1decd1119d 100644
--- a/curses_keys.h
+++ b/ui/curses_keys.h
diff --git a/d3des.c b/ui/d3des.c
index 60c840ed53..60c840ed53 100644
--- a/d3des.c
+++ b/ui/d3des.c
diff --git a/d3des.h b/ui/d3des.h
index ea3da44ce9..ea3da44ce9 100644
--- a/d3des.h
+++ b/ui/d3des.h
diff --git a/keymaps.c b/ui/keymaps.c
index 78c7ea375c..78c7ea375c 100644
--- a/keymaps.c
+++ b/ui/keymaps.c
diff --git a/keymaps.h b/ui/keymaps.h
index a7600d5751..a7600d5751 100644
--- a/keymaps.h
+++ b/ui/keymaps.h
diff --git a/sdl.c b/ui/sdl.c
index 0072680f4c..0072680f4c 100644
--- a/sdl.c
+++ b/ui/sdl.c
diff --git a/sdl_keysym.h b/ui/sdl_keysym.h
index ee904805da..ee904805da 100644
--- a/sdl_keysym.h
+++ b/ui/sdl_keysym.h
diff --git a/sdl_zoom.c b/ui/sdl_zoom.c
index a986c7c14c..a986c7c14c 100644
--- a/sdl_zoom.c
+++ b/ui/sdl_zoom.c
diff --git a/sdl_zoom.h b/ui/sdl_zoom.h
index 74955bc944..74955bc944 100644
--- a/sdl_zoom.h
+++ b/ui/sdl_zoom.h
diff --git a/sdl_zoom_template.h b/ui/sdl_zoom_template.h
index 64bbca849b..64bbca849b 100644
--- a/sdl_zoom_template.h
+++ b/ui/sdl_zoom_template.h
diff --git a/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index a51ddc8a18..a51ddc8a18 100644
--- a/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
diff --git a/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
index fd9b18a8fe..fd9b18a8fe 100644
--- a/vnc-auth-sasl.h
+++ b/ui/vnc-auth-sasl.h
diff --git a/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 07c169186a..07c169186a 100644
--- a/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
diff --git a/vnc-auth-vencrypt.h b/ui/vnc-auth-vencrypt.h
index 9f674c5173..9f674c5173 100644
--- a/vnc-auth-vencrypt.h
+++ b/ui/vnc-auth-vencrypt.h
diff --git a/vnchextile.h b/ui/vnc-enc-hextile-template.h
index b9f9f5ef89..b9f9f5ef89 100644
--- a/vnchextile.h
+++ b/ui/vnc-enc-hextile-template.h
diff --git a/vnc-encoding-hextile.c b/ui/vnc-enc-hextile.c
index 728f25e5d8..364a491157 100644
--- a/vnc-encoding-hextile.c
+++ b/ui/vnc-enc-hextile.c
@@ -33,32 +33,32 @@ static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
 }
 
 #define BPP 8
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
 #undef BPP
 
 #define BPP 16
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
 #undef BPP
 
 #define BPP 32
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
 #undef BPP
 
 #define GENERIC
 #define BPP 8
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
 #undef BPP
 #undef GENERIC
 
 #define GENERIC
 #define BPP 16
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
 #undef BPP
 #undef GENERIC
 
 #define GENERIC
 #define BPP 32
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
 #undef BPP
 #undef GENERIC
 
@@ -75,7 +75,7 @@ int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
     has_fg = has_bg = 0;
     for (j = y; j < (y + h); j += 16) {
         for (i = x; i < (x + w); i += 16) {
-            vs->send_hextile_tile(vs, i, j,
+            vs->hextile.send_tile(vs, i, j,
                                   MIN(16, x + w - i), MIN(16, y + h - j),
                                   last_bg, last_fg, &has_bg, &has_fg);
         }
@@ -91,25 +91,25 @@ void vnc_hextile_set_pixel_conversion(VncState *vs, int generic)
     if (!generic) {
         switch (vs->ds->surface->pf.bits_per_pixel) {
             case 8:
-                vs->send_hextile_tile = send_hextile_tile_8;
+                vs->hextile.send_tile = send_hextile_tile_8;
                 break;
             case 16:
-                vs->send_hextile_tile = send_hextile_tile_16;
+                vs->hextile.send_tile = send_hextile_tile_16;
                 break;
             case 32:
-                vs->send_hextile_tile = send_hextile_tile_32;
+                vs->hextile.send_tile = send_hextile_tile_32;
                 break;
         }
     } else {
         switch (vs->ds->surface->pf.bits_per_pixel) {
             case 8:
-                vs->send_hextile_tile = send_hextile_tile_generic_8;
+                vs->hextile.send_tile = send_hextile_tile_generic_8;
                 break;
             case 16:
-                vs->send_hextile_tile = send_hextile_tile_generic_16;
+                vs->hextile.send_tile = send_hextile_tile_generic_16;
                 break;
             case 32:
-                vs->send_hextile_tile = send_hextile_tile_generic_32;
+                vs->hextile.send_tile = send_hextile_tile_generic_32;
                 break;
         }
     }
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
new file mode 100644
index 0000000000..8ca55702b1
--- /dev/null
+++ b/ui/vnc-enc-tight.c
@@ -0,0 +1,1717 @@
+/*
+ * QEMU VNC display driver: tight encoding
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky.  All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config-host.h"
+
+#ifdef CONFIG_VNC_PNG
+#include <png.h>
+#endif
+#ifdef CONFIG_VNC_JPEG
+#include <stdio.h>
+#include <jpeglib.h>
+#endif
+
+#include "qemu-common.h"
+
+#include "bswap.h"
+#include "qint.h"
+#include "vnc.h"
+#include "vnc-enc-tight.h"
+#include "vnc-palette.h"
+
+/* Compression level stuff. The following array contains various
+   encoder parameters for each of 10 compression levels (0..9).
+   Last three parameters correspond to JPEG quality levels (0..9). */
+
+static const struct {
+    int max_rect_size, max_rect_width;
+    int mono_min_rect_size, gradient_min_rect_size;
+    int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level;
+    int gradient_threshold, gradient_threshold24;
+    int idx_max_colors_divisor;
+    int jpeg_quality, jpeg_threshold, jpeg_threshold24;
+} tight_conf[] = {
+    {   512,   32,   6, 65536, 0, 0, 0, 0,   0,   0,   4,  5, 10000, 23000 },
+    {  2048,  128,   6, 65536, 1, 1, 1, 0,   0,   0,   8, 10,  8000, 18000 },
+    {  6144,  256,   8, 65536, 3, 3, 2, 0,   0,   0,  24, 15,  6500, 15000 },
+    { 10240, 1024,  12, 65536, 5, 5, 3, 0,   0,   0,  32, 25,  5000, 12000 },
+    { 16384, 2048,  12, 65536, 6, 6, 4, 0,   0,   0,  32, 37,  4000, 10000 },
+    { 32768, 2048,  12,  4096, 7, 7, 5, 4, 150, 380,  32, 50,  3000,  8000 },
+    { 65536, 2048,  16,  4096, 7, 7, 6, 4, 170, 420,  48, 60,  2000,  5000 },
+    { 65536, 2048,  16,  4096, 8, 8, 7, 5, 180, 450,  64, 70,  1000,  2500 },
+    { 65536, 2048,  32,  8192, 9, 9, 8, 6, 190, 475,  64, 75,   500,  1200 },
+    { 65536, 2048,  32,  8192, 9, 9, 9, 6, 200, 500,  96, 80,   200,   500 }
+};
+
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+                                         int w, int h);
+
+#ifdef CONFIG_VNC_PNG
+static const struct {
+    int png_zlib_level, png_filters;
+} tight_png_conf[] = {
+    { 0, PNG_NO_FILTERS },
+    { 1, PNG_NO_FILTERS },
+    { 2, PNG_NO_FILTERS },
+    { 3, PNG_NO_FILTERS },
+    { 4, PNG_NO_FILTERS },
+    { 5, PNG_ALL_FILTERS },
+    { 6, PNG_ALL_FILTERS },
+    { 7, PNG_ALL_FILTERS },
+    { 8, PNG_ALL_FILTERS },
+    { 9, PNG_ALL_FILTERS },
+};
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+                         VncPalette *palette);
+
+static bool tight_can_send_png_rect(VncState *vs, int w, int h)
+{
+    if (vs->tight.type != VNC_ENCODING_TIGHT_PNG) {
+        return false;
+    }
+
+    if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+        vs->clientds.pf.bytes_per_pixel == 1) {
+        return false;
+    }
+
+    return true;
+}
+#endif
+
+/*
+ * Code to guess if given rectangle is suitable for smooth image
+ * compression (by applying "gradient" filter or JPEG coder).
+ */
+
+static unsigned int
+tight_detect_smooth_image24(VncState *vs, int w, int h)
+{
+    int off;
+    int x, y, d, dx;
+    unsigned int c;
+    unsigned int stats[256];
+    int pixels = 0;
+    int pix, left[3];
+    unsigned int errors;
+    unsigned char *buf = vs->tight.tight.buffer;
+
+    /*
+     * If client is big-endian, color samples begin from the second
+     * byte (offset 1) of a 32-bit pixel value.
+     */
+    off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG);
+
+    memset(stats, 0, sizeof (stats));
+
+    for (y = 0, x = 0; y < h && x < w;) {
+        for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH;
+             d++) {
+            for (c = 0; c < 3; c++) {
+                left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF;
+            }
+            for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) {
+                for (c = 0; c < 3; c++) {
+                    pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
+                    stats[abs(pix - left[c])]++;
+                    left[c] = pix;
+                }
+                pixels++;
+            }
+        }
+        if (w > h) {
+            x += h;
+            y = 0;
+        } else {
+            x = 0;
+            y += w;
+        }
+    }
+
+    /* 95% smooth or more ... */
+    if (stats[0] * 33 / pixels >= 95) {
+        return 0;
+    }
+
+    errors = 0;
+    for (c = 1; c < 8; c++) {
+        errors += stats[c] * (c * c);
+        if (stats[c] == 0 || stats[c] > stats[c-1] * 2) {
+            return 0;
+        }
+    }
+    for (; c < 256; c++) {
+        errors += stats[c] * (c * c);
+    }
+    errors /= (pixels * 3 - stats[0]);
+
+    return errors;
+}
+
+#define DEFINE_DETECT_FUNCTION(bpp)                                     \
+                                                                        \
+    static unsigned int                                                 \
+    tight_detect_smooth_image##bpp(VncState *vs, int w, int h) {        \
+        bool endian;                                                    \
+        uint##bpp##_t pix;                                              \
+        int max[3], shift[3];                                           \
+        int x, y, d, dx;                                                \
+        unsigned int c;                                                 \
+        unsigned int stats[256];                                        \
+        int pixels = 0;                                                 \
+        int sample, sum, left[3];                                       \
+        unsigned int errors;                                            \
+        unsigned char *buf = vs->tight.tight.buffer;                    \
+                                                                        \
+        endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) !=        \
+                  (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG));     \
+                                                                        \
+                                                                        \
+        max[0] = vs->clientds.pf.rmax;                                  \
+        max[1] = vs->clientds.pf.gmax;                                  \
+        max[2] = vs->clientds.pf.bmax;                                  \
+        shift[0] = vs->clientds.pf.rshift;                              \
+        shift[1] = vs->clientds.pf.gshift;                              \
+        shift[2] = vs->clientds.pf.bshift;                              \
+                                                                        \
+        memset(stats, 0, sizeof(stats));                                \
+                                                                        \
+        y = 0, x = 0;                                                   \
+        while (y < h && x < w) {                                        \
+            for (d = 0; d < h - y &&                                    \
+                     d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) {  \
+                pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d];              \
+                if (endian) {                                           \
+                    pix = bswap_##bpp(pix);                             \
+                }                                                       \
+                for (c = 0; c < 3; c++) {                               \
+                    left[c] = (int)(pix >> shift[c] & max[c]);          \
+                }                                                       \
+                for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH;       \
+                     dx++) {                                            \
+                    pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx];       \
+                    if (endian) {                                       \
+                        pix = bswap_##bpp(pix);                         \
+                    }                                                   \
+                    sum = 0;                                            \
+                    for (c = 0; c < 3; c++) {                           \
+                        sample = (int)(pix >> shift[c] & max[c]);       \
+                        sum += abs(sample - left[c]);                   \
+                        left[c] = sample;                               \
+                    }                                                   \
+                    if (sum > 255) {                                    \
+                        sum = 255;                                      \
+                    }                                                   \
+                    stats[sum]++;                                       \
+                    pixels++;                                           \
+                }                                                       \
+            }                                                           \
+            if (w > h) {                                                \
+                x += h;                                                 \
+                y = 0;                                                  \
+            } else {                                                    \
+                x = 0;                                                  \
+                y += w;                                                 \
+            }                                                           \
+        }                                                               \
+                                                                        \
+        if ((stats[0] + stats[1]) * 100 / pixels >= 90) {               \
+            return 0;                                                   \
+        }                                                               \
+                                                                        \
+        errors = 0;                                                     \
+        for (c = 1; c < 8; c++) {                                       \
+            errors += stats[c] * (c * c);                               \
+            if (stats[c] == 0 || stats[c] > stats[c-1] * 2) {           \
+                return 0;                                               \
+            }                                                           \
+        }                                                               \
+        for (; c < 256; c++) {                                          \
+            errors += stats[c] * (c * c);                               \
+        }                                                               \
+        errors /= (pixels - stats[0]);                                  \
+                                                                        \
+        return errors;                                                  \
+    }
+
+DEFINE_DETECT_FUNCTION(16)
+DEFINE_DETECT_FUNCTION(32)
+
+static int
+tight_detect_smooth_image(VncState *vs, int w, int h)
+{
+    unsigned int errors;
+    int compression = vs->tight.compression;
+    int quality = vs->tight.quality;
+
+    if (!vs->vd->lossy) {
+        return 0;
+    }
+
+    if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+        vs->clientds.pf.bytes_per_pixel == 1 ||
+        w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) {
+        return 0;
+    }
+
+    if (vs->tight.quality != (uint8_t)-1) {
+        if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) {
+            return 0;
+        }
+    } else {
+        if (w * h < tight_conf[compression].gradient_min_rect_size) {
+            return 0;
+        }
+    }
+
+    if (vs->clientds.pf.bytes_per_pixel == 4) {
+        if (vs->tight.pixel24) {
+            errors = tight_detect_smooth_image24(vs, w, h);
+            if (vs->tight.quality != (uint8_t)-1) {
+                return (errors < tight_conf[quality].jpeg_threshold24);
+            }
+            return (errors < tight_conf[compression].gradient_threshold24);
+        } else {
+            errors = tight_detect_smooth_image32(vs, w, h);
+        }
+    } else {
+        errors = tight_detect_smooth_image16(vs, w, h);
+    }
+    if (quality != -1) {
+        return (errors < tight_conf[quality].jpeg_threshold);
+    }
+    return (errors < tight_conf[compression].gradient_threshold);
+}
+
+/*
+ * Code to determine how many different colors used in rectangle.
+ */
+#define DEFINE_FILL_PALETTE_FUNCTION(bpp)                               \
+                                                                        \
+    static int                                                          \
+    tight_fill_palette##bpp(VncState *vs, int x, int y,                 \
+                            int max, size_t count,                      \
+                            uint32_t *bg, uint32_t *fg,                 \
+                            VncPalette **palette) {                     \
+        uint##bpp##_t *data;                                            \
+        uint##bpp##_t c0, c1, ci;                                       \
+        int i, n0, n1;                                                  \
+                                                                        \
+        data = (uint##bpp##_t *)vs->tight.tight.buffer;                 \
+                                                                        \
+        c0 = data[0];                                                   \
+        i = 1;                                                          \
+        while (i < count && data[i] == c0)                              \
+            i++;                                                        \
+        if (i >= count) {                                               \
+            *bg = *fg = c0;                                             \
+            return 1;                                                   \
+        }                                                               \
+                                                                        \
+        if (max < 2) {                                                  \
+            return 0;                                                   \
+        }                                                               \
+                                                                        \
+        n0 = i;                                                         \
+        c1 = data[i];                                                   \
+        n1 = 0;                                                         \
+        for (i++; i < count; i++) {                                     \
+            ci = data[i];                                               \
+            if (ci == c0) {                                             \
+                n0++;                                                   \
+            } else if (ci == c1) {                                      \
+                n1++;                                                   \
+            } else                                                      \
+                break;                                                  \
+        }                                                               \
+        if (i >= count) {                                               \
+            if (n0 > n1) {                                              \
+                *bg = (uint32_t)c0;                                     \
+                *fg = (uint32_t)c1;                                     \
+            } else {                                                    \
+                *bg = (uint32_t)c1;                                     \
+                *fg = (uint32_t)c0;                                     \
+            }                                                           \
+            return 2;                                                   \
+        }                                                               \
+                                                                        \
+        if (max == 2) {                                                 \
+            return 0;                                                   \
+        }                                                               \
+                                                                        \
+        *palette = palette_new(max, bpp);                               \
+        palette_put(*palette, c0);                                      \
+        palette_put(*palette, c1);                                      \
+        palette_put(*palette, ci);                                      \
+                                                                        \
+        for (i++; i < count; i++) {                                     \
+            if (data[i] == ci) {                                        \
+                continue;                                               \
+            } else {                                                    \
+                ci = data[i];                                           \
+                if (!palette_put(*palette, (uint32_t)ci)) {             \
+                    return 0;                                           \
+                }                                                       \
+            }                                                           \
+        }                                                               \
+                                                                        \
+        return palette_size(*palette);                                  \
+    }
+
+DEFINE_FILL_PALETTE_FUNCTION(8)
+DEFINE_FILL_PALETTE_FUNCTION(16)
+DEFINE_FILL_PALETTE_FUNCTION(32)
+
+static int tight_fill_palette(VncState *vs, int x, int y,
+                              size_t count, uint32_t *bg, uint32_t *fg,
+                              VncPalette **palette)
+{
+    int max;
+
+    max = count / tight_conf[vs->tight.compression].idx_max_colors_divisor;
+    if (max < 2 &&
+        count >= tight_conf[vs->tight.compression].mono_min_rect_size) {
+        max = 2;
+    }
+    if (max >= 256) {
+        max = 256;
+    }
+
+    switch(vs->clientds.pf.bytes_per_pixel) {
+    case 4:
+        return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette);
+    case 2:
+        return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette);
+    default:
+        max = 2;
+        return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette);
+    }
+    return 0;
+}
+
+/*
+ * Converting truecolor samples into palette indices.
+ */
+#define DEFINE_IDX_ENCODE_FUNCTION(bpp)                                 \
+                                                                        \
+    static void                                                         \
+    tight_encode_indexed_rect##bpp(uint8_t *buf, int count,             \
+                                   VncPalette *palette) {               \
+        uint##bpp##_t *src;                                             \
+        uint##bpp##_t rgb;                                              \
+        int i, rep;                                                     \
+        uint8_t idx;                                                    \
+                                                                        \
+        src = (uint##bpp##_t *) buf;                                    \
+                                                                        \
+        for (i = 0; i < count; i++) {                                   \
+                                                                        \
+            rgb = *src++;                                               \
+            rep = 0;                                                    \
+            while (i < count && *src == rgb) {                          \
+                rep++, src++, i++;                                      \
+            }                                                           \
+            idx = palette_idx(palette, rgb);                            \
+            /*                                                          \
+             * Should never happen, but don't break everything          \
+             * if it does, use the first color instead                  \
+             */                                                         \
+            if (idx == (uint8_t)-1) {                                   \
+                idx = 0;                                                \
+            }                                                           \
+            while (rep >= 0) {                                          \
+                *buf++ = idx;                                           \
+                rep--;                                                  \
+            }                                                           \
+        }                                                               \
+    }
+
+DEFINE_IDX_ENCODE_FUNCTION(16)
+DEFINE_IDX_ENCODE_FUNCTION(32)
+
+#define DEFINE_MONO_ENCODE_FUNCTION(bpp)                                \
+                                                                        \
+    static void                                                         \
+    tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h,             \
+                                uint##bpp##_t bg, uint##bpp##_t fg) {   \
+        uint##bpp##_t *ptr;                                             \
+        unsigned int value, mask;                                       \
+        int aligned_width;                                              \
+        int x, y, bg_bits;                                              \
+                                                                        \
+        ptr = (uint##bpp##_t *) buf;                                    \
+        aligned_width = w - w % 8;                                      \
+                                                                        \
+        for (y = 0; y < h; y++) {                                       \
+            for (x = 0; x < aligned_width; x += 8) {                    \
+                for (bg_bits = 0; bg_bits < 8; bg_bits++) {             \
+                    if (*ptr++ != bg) {                                 \
+                        break;                                          \
+                    }                                                   \
+                }                                                       \
+                if (bg_bits == 8) {                                     \
+                    *buf++ = 0;                                         \
+                    continue;                                           \
+                }                                                       \
+                mask = 0x80 >> bg_bits;                                 \
+                value = mask;                                           \
+                for (bg_bits++; bg_bits < 8; bg_bits++) {               \
+                    mask >>= 1;                                         \
+                    if (*ptr++ != bg) {                                 \
+                        value |= mask;                                  \
+                    }                                                   \
+                }                                                       \
+                *buf++ = (uint8_t)value;                                \
+            }                                                           \
+                                                                        \
+            mask = 0x80;                                                \
+            value = 0;                                                  \
+            if (x >= w) {                                               \
+                continue;                                               \
+            }                                                           \
+                                                                        \
+            for (; x < w; x++) {                                        \
+                if (*ptr++ != bg) {                                     \
+                    value |= mask;                                      \
+                }                                                       \
+                mask >>= 1;                                             \
+            }                                                           \
+            *buf++ = (uint8_t)value;                                    \
+        }                                                               \
+    }
+
+DEFINE_MONO_ENCODE_FUNCTION(8)
+DEFINE_MONO_ENCODE_FUNCTION(16)
+DEFINE_MONO_ENCODE_FUNCTION(32)
+
+/*
+ * ``Gradient'' filter for 24-bit color samples.
+ * Should be called only when redMax, greenMax and blueMax are 255.
+ * Color components assumed to be byte-aligned.
+ */
+
+static void
+tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h)
+{
+    uint32_t *buf32;
+    uint32_t pix32;
+    int shift[3];
+    int *prev;
+    int here[3], upper[3], left[3], upperleft[3];
+    int prediction;
+    int x, y, c;
+
+    buf32 = (uint32_t *)buf;
+    memset(vs->tight.gradient.buffer, 0, w * 3 * sizeof(int));
+
+    if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+        (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+        shift[0] = vs->clientds.pf.rshift;
+        shift[1] = vs->clientds.pf.gshift;
+        shift[2] = vs->clientds.pf.bshift;
+    } else {
+        shift[0] = 24 - vs->clientds.pf.rshift;
+        shift[1] = 24 - vs->clientds.pf.gshift;
+        shift[2] = 24 - vs->clientds.pf.bshift;
+    }
+
+    for (y = 0; y < h; y++) {
+        for (c = 0; c < 3; c++) {
+            upper[c] = 0;
+            here[c] = 0;
+        }
+        prev = (int *)vs->tight.gradient.buffer;
+        for (x = 0; x < w; x++) {
+            pix32 = *buf32++;
+            for (c = 0; c < 3; c++) {
+                upperleft[c] = upper[c];
+                left[c] = here[c];
+                upper[c] = *prev;
+                here[c] = (int)(pix32 >> shift[c] & 0xFF);
+                *prev++ = here[c];
+
+                prediction = left[c] + upper[c] - upperleft[c];
+                if (prediction < 0) {
+                    prediction = 0;
+                } else if (prediction > 0xFF) {
+                    prediction = 0xFF;
+                }
+                *buf++ = (char)(here[c] - prediction);
+            }
+        }
+    }
+}
+
+
+/*
+ * ``Gradient'' filter for other color depths.
+ */
+
+#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp)                            \
+                                                                        \
+    static void                                                         \
+    tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf,        \
+                               int w, int h) {                          \
+        uint##bpp##_t pix, diff;                                        \
+        bool endian;                                                    \
+        int *prev;                                                      \
+        int max[3], shift[3];                                           \
+        int here[3], upper[3], left[3], upperleft[3];                   \
+        int prediction;                                                 \
+        int x, y, c;                                                    \
+                                                                        \
+        memset (vs->tight.gradient.buffer, 0, w * 3 * sizeof(int));     \
+                                                                        \
+        endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) !=        \
+                  (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG));     \
+                                                                        \
+        max[0] = vs->clientds.pf.rmax;                                  \
+        max[1] = vs->clientds.pf.gmax;                                  \
+        max[2] = vs->clientds.pf.bmax;                                  \
+        shift[0] = vs->clientds.pf.rshift;                              \
+        shift[1] = vs->clientds.pf.gshift;                              \
+        shift[2] = vs->clientds.pf.bshift;                              \
+                                                                        \
+        for (y = 0; y < h; y++) {                                       \
+            for (c = 0; c < 3; c++) {                                   \
+                upper[c] = 0;                                           \
+                here[c] = 0;                                            \
+            }                                                           \
+            prev = (int *)vs->tight.gradient.buffer;                    \
+            for (x = 0; x < w; x++) {                                   \
+                pix = *buf;                                             \
+                if (endian) {                                           \
+                    pix = bswap_##bpp(pix);                             \
+                }                                                       \
+                diff = 0;                                               \
+                for (c = 0; c < 3; c++) {                               \
+                    upperleft[c] = upper[c];                            \
+                    left[c] = here[c];                                  \
+                    upper[c] = *prev;                                   \
+                    here[c] = (int)(pix >> shift[c] & max[c]);          \
+                    *prev++ = here[c];                                  \
+                                                                        \
+                    prediction = left[c] + upper[c] - upperleft[c];     \
+                    if (prediction < 0) {                               \
+                        prediction = 0;                                 \
+                    } else if (prediction > max[c]) {                   \
+                        prediction = max[c];                            \
+                    }                                                   \
+                    diff |= ((here[c] - prediction) & max[c])           \
+                        << shift[c];                                    \
+                }                                                       \
+                if (endian) {                                           \
+                    diff = bswap_##bpp(diff);                           \
+                }                                                       \
+                *buf++ = diff;                                          \
+            }                                                           \
+        }                                                               \
+    }
+
+DEFINE_GRADIENT_FILTER_FUNCTION(16)
+DEFINE_GRADIENT_FILTER_FUNCTION(32)
+
+/*
+ * Check if a rectangle is all of the same color. If needSameColor is
+ * set to non-zero, then also check that its color equals to the
+ * *colorPtr value. The result is 1 if the test is successfull, and in
+ * that case new color will be stored in *colorPtr.
+ */
+
+#define DEFINE_CHECK_SOLID_FUNCTION(bpp)                                \
+                                                                        \
+    static bool                                                         \
+    check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h,     \
+                          uint32_t* color, bool samecolor)              \
+    {                                                                   \
+        VncDisplay *vd = vs->vd;                                        \
+        uint##bpp##_t *fbptr;                                           \
+        uint##bpp##_t c;                                                \
+        int dx, dy;                                                     \
+                                                                        \
+        fbptr = (uint##bpp##_t *)                                       \
+            (vd->server->data + y * ds_get_linesize(vs->ds) +           \
+             x * ds_get_bytes_per_pixel(vs->ds));                       \
+                                                                        \
+        c = *fbptr;                                                     \
+        if (samecolor && (uint32_t)c != *color) {                       \
+            return false;                                               \
+        }                                                               \
+                                                                        \
+        for (dy = 0; dy < h; dy++) {                                    \
+            for (dx = 0; dx < w; dx++) {                                \
+                if (c != fbptr[dx]) {                                   \
+                    return false;                                       \
+                }                                                       \
+            }                                                           \
+            fbptr = (uint##bpp##_t *)                                   \
+                ((uint8_t *)fbptr + ds_get_linesize(vs->ds));           \
+        }                                                               \
+                                                                        \
+        *color = (uint32_t)c;                                           \
+        return true;                                                    \
+    }
+
+DEFINE_CHECK_SOLID_FUNCTION(32)
+DEFINE_CHECK_SOLID_FUNCTION(16)
+DEFINE_CHECK_SOLID_FUNCTION(8)
+
+static bool check_solid_tile(VncState *vs, int x, int y, int w, int h,
+                             uint32_t* color, bool samecolor)
+{
+    VncDisplay *vd = vs->vd;
+
+    switch(vd->server->pf.bytes_per_pixel) {
+    case 4:
+        return check_solid_tile32(vs, x, y, w, h, color, samecolor);
+    case 2:
+        return check_solid_tile16(vs, x, y, w, h, color, samecolor);
+    default:
+        return check_solid_tile8(vs, x, y, w, h, color, samecolor);
+    }
+}
+
+static void find_best_solid_area(VncState *vs, int x, int y, int w, int h,
+                                 uint32_t color, int *w_ptr, int *h_ptr)
+{
+    int dx, dy, dw, dh;
+    int w_prev;
+    int w_best = 0, h_best = 0;
+
+    w_prev = w;
+
+    for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+        dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy);
+        dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev);
+
+        if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) {
+            break;
+        }
+
+        for (dx = x + dw; dx < x + w_prev;) {
+            dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx);
+
+            if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) {
+                break;
+            }
+            dx += dw;
+        }
+
+        w_prev = dx - x;
+        if (w_prev * (dy + dh - y) > w_best * h_best) {
+            w_best = w_prev;
+            h_best = dy + dh - y;
+        }
+    }
+
+    *w_ptr = w_best;
+    *h_ptr = h_best;
+}
+
+static void extend_solid_area(VncState *vs, int x, int y, int w, int h,
+                              uint32_t color, int *x_ptr, int *y_ptr,
+                              int *w_ptr, int *h_ptr)
+{
+    int cx, cy;
+
+    /* Try to extend the area upwards. */
+    for ( cy = *y_ptr - 1;
+          cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+          cy-- );
+    *h_ptr += *y_ptr - (cy + 1);
+    *y_ptr = cy + 1;
+
+    /* ... downwards. */
+    for ( cy = *y_ptr + *h_ptr;
+          cy < y + h &&
+              check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+          cy++ );
+    *h_ptr += cy - (*y_ptr + *h_ptr);
+
+    /* ... to the left. */
+    for ( cx = *x_ptr - 1;
+          cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+          cx-- );
+    *w_ptr += *x_ptr - (cx + 1);
+    *x_ptr = cx + 1;
+
+    /* ... to the right. */
+    for ( cx = *x_ptr + *w_ptr;
+          cx < x + w &&
+              check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+          cx++ );
+    *w_ptr += cx - (*x_ptr + *w_ptr);
+}
+
+static int tight_init_stream(VncState *vs, int stream_id,
+                             int level, int strategy)
+{
+    z_streamp zstream = &vs->tight.stream[stream_id];
+
+    if (zstream->opaque == NULL) {
+        int err;
+
+        VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id);
+        VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs);
+        zstream->zalloc = vnc_zlib_zalloc;
+        zstream->zfree = vnc_zlib_zfree;
+
+        err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
+                           MAX_MEM_LEVEL, strategy);
+
+        if (err != Z_OK) {
+            fprintf(stderr, "VNC: error initializing zlib\n");
+            return -1;
+        }
+
+        vs->tight.levels[stream_id] = level;
+        zstream->opaque = vs;
+    }
+
+    if (vs->tight.levels[stream_id] != level) {
+        if (deflateParams(zstream, level, strategy) != Z_OK) {
+            return -1;
+        }
+        vs->tight.levels[stream_id] = level;
+    }
+    return 0;
+}
+
+static void tight_send_compact_size(VncState *vs, size_t len)
+{
+    int lpc = 0;
+    int bytes = 0;
+    char buf[3] = {0, 0, 0};
+
+    buf[bytes++] = len & 0x7F;
+    if (len > 0x7F) {
+        buf[bytes-1] |= 0x80;
+        buf[bytes++] = (len >> 7) & 0x7F;
+        if (len > 0x3FFF) {
+            buf[bytes-1] |= 0x80;
+            buf[bytes++] = (len >> 14) & 0xFF;
+        }
+    }
+    for (lpc = 0; lpc < bytes; lpc++) {
+        vnc_write_u8(vs, buf[lpc]);
+    }
+}
+
+static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
+                               int level, int strategy)
+{
+    z_streamp zstream = &vs->tight.stream[stream_id];
+    int previous_out;
+
+    if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) {
+        vnc_write(vs, vs->tight.tight.buffer, vs->tight.tight.offset);
+        return bytes;
+    }
+
+    if (tight_init_stream(vs, stream_id, level, strategy)) {
+        return -1;
+    }
+
+    /* reserve memory in output buffer */
+    buffer_reserve(&vs->tight.zlib, bytes + 64);
+
+    /* set pointers */
+    zstream->next_in = vs->tight.tight.buffer;
+    zstream->avail_in = vs->tight.tight.offset;
+    zstream->next_out = vs->tight.zlib.buffer + vs->tight.zlib.offset;
+    zstream->avail_out = vs->tight.zlib.capacity - vs->tight.zlib.offset;
+    zstream->data_type = Z_BINARY;
+    previous_out = zstream->total_out;
+
+    /* start encoding */
+    if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+        fprintf(stderr, "VNC: error during tight compression\n");
+        return -1;
+    }
+
+    vs->tight.zlib.offset = vs->tight.zlib.capacity - zstream->avail_out;
+    bytes = zstream->total_out - previous_out;
+
+    tight_send_compact_size(vs, bytes);
+    vnc_write(vs, vs->tight.zlib.buffer, bytes);
+
+    buffer_reset(&vs->tight.zlib);
+
+    return bytes;
+}
+
+/*
+ * Subencoding implementations.
+ */
+static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
+{
+    uint32_t *buf32;
+    uint32_t pix;
+    int rshift, gshift, bshift;
+
+    buf32 = (uint32_t *)buf;
+
+    if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+        (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+        rshift = vs->clientds.pf.rshift;
+        gshift = vs->clientds.pf.gshift;
+        bshift = vs->clientds.pf.bshift;
+    } else {
+        rshift = 24 - vs->clientds.pf.rshift;
+        gshift = 24 - vs->clientds.pf.gshift;
+        bshift = 24 - vs->clientds.pf.bshift;
+    }
+
+    if (ret) {
+        *ret = count * 3;
+    }
+
+    while (count--) {
+        pix = *buf32++;
+        *buf++ = (char)(pix >> rshift);
+        *buf++ = (char)(pix >> gshift);
+        *buf++ = (char)(pix >> bshift);
+    }
+}
+
+static int send_full_color_rect(VncState *vs, int x, int y, int w, int h)
+{
+    int stream = 0;
+    size_t bytes;
+
+#ifdef CONFIG_VNC_PNG
+    if (tight_can_send_png_rect(vs, w, h)) {
+        return send_png_rect(vs, x, y, w, h, NULL);
+    }
+#endif
+
+    vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
+
+    if (vs->tight.pixel24) {
+        tight_pack24(vs, vs->tight.tight.buffer, w * h, &vs->tight.tight.offset);
+        bytes = 3;
+    } else {
+        bytes = vs->clientds.pf.bytes_per_pixel;
+    }
+
+    bytes = tight_compress_data(vs, stream, w * h * bytes,
+                                tight_conf[vs->tight.compression].raw_zlib_level,
+                                Z_DEFAULT_STRATEGY);
+
+    return (bytes >= 0);
+}
+
+static int send_solid_rect(VncState *vs)
+{
+    size_t bytes;
+
+    vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */
+
+    if (vs->tight.pixel24) {
+        tight_pack24(vs, vs->tight.tight.buffer, 1, &vs->tight.tight.offset);
+        bytes = 3;
+    } else {
+        bytes = vs->clientds.pf.bytes_per_pixel;
+    }
+
+    vnc_write(vs, vs->tight.tight.buffer, bytes);
+    return 1;
+}
+
+static int send_mono_rect(VncState *vs, int x, int y,
+                          int w, int h, uint32_t bg, uint32_t fg)
+{
+    size_t bytes;
+    int stream = 1;
+    int level = tight_conf[vs->tight.compression].mono_zlib_level;
+
+#ifdef CONFIG_VNC_PNG
+    if (tight_can_send_png_rect(vs, w, h)) {
+        int ret;
+        int bpp = vs->clientds.pf.bytes_per_pixel * 8;
+        VncPalette *palette = palette_new(2, bpp);
+
+        palette_put(palette, bg);
+        palette_put(palette, fg);
+        ret = send_png_rect(vs, x, y, w, h, palette);
+        palette_destroy(palette);
+        return ret;
+    }
+#endif
+
+    bytes = ((w + 7) / 8) * h;
+
+    vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+    vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+    vnc_write_u8(vs, 1);
+
+    switch(vs->clientds.pf.bytes_per_pixel) {
+    case 4:
+    {
+        uint32_t buf[2] = {bg, fg};
+        size_t ret = sizeof (buf);
+
+        if (vs->tight.pixel24) {
+            tight_pack24(vs, (unsigned char*)buf, 2, &ret);
+        }
+        vnc_write(vs, buf, ret);
+
+        tight_encode_mono_rect32(vs->tight.tight.buffer, w, h, bg, fg);
+        break;
+    }
+    case 2:
+        vnc_write(vs, &bg, 2);
+        vnc_write(vs, &fg, 2);
+        tight_encode_mono_rect16(vs->tight.tight.buffer, w, h, bg, fg);
+        break;
+    default:
+        vnc_write_u8(vs, bg);
+        vnc_write_u8(vs, fg);
+        tight_encode_mono_rect8(vs->tight.tight.buffer, w, h, bg, fg);
+        break;
+    }
+    vs->tight.tight.offset = bytes;
+
+    bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);
+    return (bytes >= 0);
+}
+
+struct palette_cb_priv {
+    VncState *vs;
+    uint8_t *header;
+#ifdef CONFIG_VNC_PNG
+    png_colorp png_palette;
+#endif
+};
+
+static void write_palette(int idx, uint32_t color, void *opaque)
+{
+    struct palette_cb_priv *priv = opaque;
+    VncState *vs = priv->vs;
+    uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
+
+    if (bytes == 4) {
+        ((uint32_t*)priv->header)[idx] = color;
+    } else {
+        ((uint16_t*)priv->header)[idx] = color;
+    }
+}
+
+static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)
+{
+    int stream = 3;
+    int level = tight_conf[vs->tight.compression].gradient_zlib_level;
+    size_t bytes;
+
+    if (vs->clientds.pf.bytes_per_pixel == 1)
+        return send_full_color_rect(vs, x, y, w, h);
+
+    vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+    vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT);
+
+    buffer_reserve(&vs->tight.gradient, w * 3 * sizeof (int));
+
+    if (vs->tight.pixel24) {
+        tight_filter_gradient24(vs, vs->tight.tight.buffer, w, h);
+        bytes = 3;
+    } else if (vs->clientds.pf.bytes_per_pixel == 4) {
+        tight_filter_gradient32(vs, (uint32_t *)vs->tight.tight.buffer, w, h);
+        bytes = 4;
+    } else {
+        tight_filter_gradient16(vs, (uint16_t *)vs->tight.tight.buffer, w, h);
+        bytes = 2;
+    }
+
+    buffer_reset(&vs->tight.gradient);
+
+    bytes = w * h * bytes;
+    vs->tight.tight.offset = bytes;
+
+    bytes = tight_compress_data(vs, stream, bytes,
+                                level, Z_FILTERED);
+    return (bytes >= 0);
+}
+
+static int send_palette_rect(VncState *vs, int x, int y,
+                             int w, int h, VncPalette *palette)
+{
+    int stream = 2;
+    int level = tight_conf[vs->tight.compression].idx_zlib_level;
+    int colors;
+    size_t bytes;
+
+#ifdef CONFIG_VNC_PNG
+    if (tight_can_send_png_rect(vs, w, h)) {
+        return send_png_rect(vs, x, y, w, h, palette);
+    }
+#endif
+
+    colors = palette_size(palette);
+
+    vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+    vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+    vnc_write_u8(vs, colors - 1);
+
+    switch(vs->clientds.pf.bytes_per_pixel) {
+    case 4:
+    {
+        size_t old_offset, offset;
+        uint32_t header[palette_size(palette)];
+        struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+        old_offset = vs->output.offset;
+        palette_iter(palette, write_palette, &priv);
+        vnc_write(vs, header, sizeof(header));
+
+        if (vs->tight.pixel24) {
+            tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
+            vs->output.offset = old_offset + offset;
+        }
+
+        tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
+        break;
+    }
+    case 2:
+    {
+        uint16_t header[palette_size(palette)];
+        struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+        palette_iter(palette, write_palette, &priv);
+        vnc_write(vs, header, sizeof(header));
+        tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
+        break;
+    }
+    default:
+        return -1; /* No palette for 8bits colors */
+        break;
+    }
+    bytes = w * h;
+    vs->tight.tight.offset = bytes;
+
+    bytes = tight_compress_data(vs, stream, bytes,
+                                level, Z_DEFAULT_STRATEGY);
+    return (bytes >= 0);
+}
+
+#if defined(CONFIG_VNC_JPEG) || defined(CONFIG_VNC_PNG)
+static void rgb_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
+                              int count)
+{
+    VncDisplay *vd = vs->vd;
+    uint32_t *fbptr;
+    uint32_t pix;
+
+    fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) +
+                         x * ds_get_bytes_per_pixel(vs->ds));
+
+    while (count--) {
+        pix = *fbptr++;
+        *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift);
+        *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift);
+        *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift);
+    }
+}
+
+#define DEFINE_RGB_GET_ROW_FUNCTION(bpp)                                \
+                                                                        \
+    static void                                                         \
+    rgb_prepare_row##bpp(VncState *vs, uint8_t *dst,                    \
+                         int x, int y, int count)                       \
+    {                                                                   \
+        VncDisplay *vd = vs->vd;                                        \
+        uint##bpp##_t *fbptr;                                           \
+        uint##bpp##_t pix;                                              \
+        int r, g, b;                                                    \
+                                                                        \
+        fbptr = (uint##bpp##_t *)                                       \
+            (vd->server->data + y * ds_get_linesize(vs->ds) +           \
+             x * ds_get_bytes_per_pixel(vs->ds));                       \
+                                                                        \
+        while (count--) {                                               \
+            pix = *fbptr++;                                             \
+                                                                        \
+            r = (int)((pix >> vs->ds->surface->pf.rshift)               \
+                      & vs->ds->surface->pf.rmax);                      \
+            g = (int)((pix >> vs->ds->surface->pf.gshift)               \
+                      & vs->ds->surface->pf.gmax);                      \
+            b = (int)((pix >> vs->ds->surface->pf.bshift)               \
+                      & vs->ds->surface->pf.bmax);                      \
+                                                                        \
+            *dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \
+                               / vs->ds->surface->pf.rmax);             \
+            *dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \
+                               / vs->ds->surface->pf.gmax);             \
+            *dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \
+                               / vs->ds->surface->pf.bmax);             \
+        }                                                               \
+    }
+
+DEFINE_RGB_GET_ROW_FUNCTION(16)
+DEFINE_RGB_GET_ROW_FUNCTION(32)
+
+static void rgb_prepare_row(VncState *vs, uint8_t *dst, int x, int y,
+                            int count)
+{
+    if (ds_get_bytes_per_pixel(vs->ds) == 4) {
+        if (vs->ds->surface->pf.rmax == 0xFF &&
+            vs->ds->surface->pf.gmax == 0xFF &&
+            vs->ds->surface->pf.bmax == 0xFF) {
+            rgb_prepare_row24(vs, dst, x, y, count);
+        } else {
+            rgb_prepare_row32(vs, dst, x, y, count);
+        }
+    } else {
+        rgb_prepare_row16(vs, dst, x, y, count);
+    }
+}
+#endif /* CONFIG_VNC_JPEG or CONFIG_VNC_PNG */
+
+/*
+ * JPEG compression stuff.
+ */
+#ifdef CONFIG_VNC_JPEG
+/*
+ * Destination manager implementation for JPEG library.
+ */
+
+/* This is called once per encoding */
+static void jpeg_init_destination(j_compress_ptr cinfo)
+{
+    VncState *vs = cinfo->client_data;
+    Buffer *buffer = &vs->tight.jpeg;
+
+    cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset;
+    cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset);
+}
+
+/* This is called when we ran out of buffer (shouldn't happen!) */
+static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
+{
+    VncState *vs = cinfo->client_data;
+    Buffer *buffer = &vs->tight.jpeg;
+
+    buffer->offset = buffer->capacity;
+    buffer_reserve(buffer, 2048);
+    jpeg_init_destination(cinfo);
+    return TRUE;
+}
+
+/* This is called when we are done processing data */
+static void jpeg_term_destination(j_compress_ptr cinfo)
+{
+    VncState *vs = cinfo->client_data;
+    Buffer *buffer = &vs->tight.jpeg;
+
+    buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer;
+}
+
+static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
+{
+    struct jpeg_compress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    struct jpeg_destination_mgr manager;
+    JSAMPROW row[1];
+    uint8_t *buf;
+    int dy;
+
+    if (ds_get_bytes_per_pixel(vs->ds) == 1)
+        return send_full_color_rect(vs, x, y, w, h);
+
+    buffer_reserve(&vs->tight.jpeg, 2048);
+
+    cinfo.err = jpeg_std_error(&jerr);
+    jpeg_create_compress(&cinfo);
+
+    cinfo.client_data = vs;
+    cinfo.image_width = w;
+    cinfo.image_height = h;
+    cinfo.input_components = 3;
+    cinfo.in_color_space = JCS_RGB;
+
+    jpeg_set_defaults(&cinfo);
+    jpeg_set_quality(&cinfo, quality, true);
+
+    manager.init_destination = jpeg_init_destination;
+    manager.empty_output_buffer = jpeg_empty_output_buffer;
+    manager.term_destination = jpeg_term_destination;
+    cinfo.dest = &manager;
+
+    jpeg_start_compress(&cinfo, true);
+
+    buf = qemu_malloc(w * 3);
+    row[0] = buf;
+    for (dy = 0; dy < h; dy++) {
+        rgb_prepare_row(vs, buf, x, y + dy, w);
+        jpeg_write_scanlines(&cinfo, row, 1);
+    }
+    qemu_free(buf);
+
+    jpeg_finish_compress(&cinfo);
+    jpeg_destroy_compress(&cinfo);
+
+    vnc_write_u8(vs, VNC_TIGHT_JPEG << 4);
+
+    tight_send_compact_size(vs, vs->tight.jpeg.offset);
+    vnc_write(vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset);
+    buffer_reset(&vs->tight.jpeg);
+
+    return 1;
+}
+#endif /* CONFIG_VNC_JPEG */
+
+/*
+ * PNG compression stuff.
+ */
+#ifdef CONFIG_VNC_PNG
+static void write_png_palette(int idx, uint32_t pix, void *opaque)
+{
+    struct palette_cb_priv *priv = opaque;
+    VncState *vs = priv->vs;
+    png_colorp color = &priv->png_palette[idx];
+
+    if (vs->tight.pixel24)
+    {
+        color->red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+        color->green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+        color->blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+    }
+    else
+    {
+        int red, green, blue;
+
+        red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+        green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+        blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+        color->red = ((red * 255 + vs->clientds.pf.rmax / 2) /
+                      vs->clientds.pf.rmax);
+        color->green = ((green * 255 + vs->clientds.pf.gmax / 2) /
+                        vs->clientds.pf.gmax);
+        color->blue = ((blue * 255 + vs->clientds.pf.bmax / 2) /
+                       vs->clientds.pf.bmax);
+    }
+}
+
+static void png_write_data(png_structp png_ptr, png_bytep data,
+                           png_size_t length)
+{
+    VncState *vs = png_get_io_ptr(png_ptr);
+
+    buffer_reserve(&vs->tight.png, vs->tight.png.offset + length);
+    memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length);
+
+    vs->tight.png.offset += length;
+}
+
+static void png_flush_data(png_structp png_ptr)
+{
+}
+
+static void *vnc_png_malloc(png_structp png_ptr, png_size_t size)
+{
+    return qemu_malloc(size);
+}
+
+static void vnc_png_free(png_structp png_ptr, png_voidp ptr)
+{
+    qemu_free(ptr);
+}
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+                         VncPalette *palette)
+{
+    png_byte color_type;
+    png_structp png_ptr;
+    png_infop info_ptr;
+    png_colorp png_palette = NULL;
+    size_t offset;
+    int level = tight_png_conf[vs->tight.compression].png_zlib_level;
+    int filters = tight_png_conf[vs->tight.compression].png_filters;
+    uint8_t *buf;
+    int dy;
+
+    png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
+                                        NULL, vnc_png_malloc, vnc_png_free);
+
+    if (png_ptr == NULL)
+        return -1;
+
+    info_ptr = png_create_info_struct(png_ptr);
+
+    if (info_ptr == NULL) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return -1;
+    }
+
+    png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data);
+    png_set_compression_level(png_ptr, level);
+    png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters);
+
+    if (palette) {
+        color_type = PNG_COLOR_TYPE_PALETTE;
+    } else {
+        color_type = PNG_COLOR_TYPE_RGB;
+    }
+
+    png_set_IHDR(png_ptr, info_ptr, w, h,
+                 8, color_type, PNG_INTERLACE_NONE,
+                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        struct palette_cb_priv priv;
+
+        png_palette = png_malloc(png_ptr, sizeof(*png_palette) *
+                                 palette_size(palette));
+
+        priv.vs = vs;
+        priv.png_palette = png_palette;
+        palette_iter(palette, write_png_palette, &priv);
+
+        png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette));
+
+        offset = vs->tight.tight.offset;
+        if (vs->clientds.pf.bytes_per_pixel == 4) {
+            tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
+        } else {
+            tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
+        }
+    }
+
+    png_write_info(png_ptr, info_ptr);
+
+    buffer_reserve(&vs->tight.png, 2048);
+    buf = qemu_malloc(w * 3);
+    for (dy = 0; dy < h; dy++)
+    {
+        if (color_type == PNG_COLOR_TYPE_PALETTE) {
+            memcpy(buf, vs->tight.tight.buffer + (dy * w), w);
+        } else {
+            rgb_prepare_row(vs, buf, x, y + dy, w);
+        }
+        png_write_row(png_ptr, buf);
+    }
+    qemu_free(buf);
+
+    png_write_end(png_ptr, NULL);
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        png_free(png_ptr, png_palette);
+    }
+
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+
+    vnc_write_u8(vs, VNC_TIGHT_PNG << 4);
+
+    tight_send_compact_size(vs, vs->tight.png.offset);
+    vnc_write(vs, vs->tight.png.buffer, vs->tight.png.offset);
+    buffer_reset(&vs->tight.png);
+    return 1;
+}
+#endif /* CONFIG_VNC_PNG */
+
+static void vnc_tight_start(VncState *vs)
+{
+    buffer_reset(&vs->tight.tight);
+
+    // make the output buffer be the zlib buffer, so we can compress it later
+    vs->tight.tmp = vs->output;
+    vs->output = vs->tight.tight;
+}
+
+static void vnc_tight_stop(VncState *vs)
+{
+    // switch back to normal output/zlib buffers
+    vs->tight.tight = vs->output;
+    vs->output = vs->tight.tmp;
+}
+
+static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h,
+                                int bg, int fg, int colors, VncPalette *palette)
+{
+    int ret;
+
+    if (colors == 0) {
+        if (tight_detect_smooth_image(vs, w, h)) {
+            ret = send_gradient_rect(vs, x, y, w, h);
+        } else {
+            ret = send_full_color_rect(vs, x, y, w, h);
+        }
+    } else if (colors == 1) {
+        ret = send_solid_rect(vs);
+    } else if (colors == 2) {
+        ret = send_mono_rect(vs, x, y, w, h, bg, fg);
+    } else if (colors <= 256) {
+        ret = send_palette_rect(vs, x, y, w, h, palette);
+    } else {
+        ret = 0;
+    }
+    return ret;
+}
+
+#ifdef CONFIG_VNC_JPEG
+static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h,
+                              int bg, int fg, int colors,
+                              VncPalette *palette)
+{
+    int ret;
+
+    if (colors == 0) {
+        if (tight_detect_smooth_image(vs, w, h)) {
+            int quality = tight_conf[vs->tight.quality].jpeg_quality;
+
+            ret = send_jpeg_rect(vs, x, y, w, h, quality);
+        } else {
+            ret = send_full_color_rect(vs, x, y, w, h);
+        }
+    } else if (colors == 1) {
+        ret = send_solid_rect(vs);
+    } else if (colors == 2) {
+        ret = send_mono_rect(vs, x, y, w, h, bg, fg);
+    } else if (colors <= 256) {
+        if (colors > 96 &&
+            tight_detect_smooth_image(vs, w, h)) {
+            int quality = tight_conf[vs->tight.quality].jpeg_quality;
+
+            ret = send_jpeg_rect(vs, x, y, w, h, quality);
+        } else {
+            ret = send_palette_rect(vs, x, y, w, h, palette);
+        }
+    }
+    return ret;
+}
+#endif
+
+static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
+{
+    VncPalette *palette = NULL;
+    uint32_t bg = 0, fg = 0;
+    int colors;
+    int ret = 0;
+
+    vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type);
+
+    vnc_tight_start(vs);
+    vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+    vnc_tight_stop(vs);
+
+    colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
+
+#ifdef CONFIG_VNC_JPEG
+    if (vs->tight.quality != -1) {
+        ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, palette);
+    } else {
+        ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
+    }
+#else
+    ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
+#endif
+
+    palette_destroy(palette);
+    return ret;
+}
+
+static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
+{
+    vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type);
+
+    vnc_tight_start(vs);
+    vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+    vnc_tight_stop(vs);
+
+    return send_solid_rect(vs);
+}
+
+static int send_rect_simple(VncState *vs, int x, int y, int w, int h)
+{
+    int max_size, max_width;
+    int max_sub_width, max_sub_height;
+    int dx, dy;
+    int rw, rh;
+    int n = 0;
+
+    max_size = tight_conf[vs->tight.compression].max_rect_size;
+    max_width = tight_conf[vs->tight.compression].max_rect_width;
+
+    if (w > max_width || w * h > max_size) {
+        max_sub_width = (w > max_width) ? max_width : w;
+        max_sub_height = max_size / max_sub_width;
+
+        for (dy = 0; dy < h; dy += max_sub_height) {
+            for (dx = 0; dx < w; dx += max_width) {
+                rw = MIN(max_sub_width, w - dx);
+                rh = MIN(max_sub_height, h - dy);
+                n += send_sub_rect(vs, x+dx, y+dy, rw, rh);
+            }
+        }
+    } else {
+        n += send_sub_rect(vs, x, y, w, h);
+    }
+
+    return n;
+}
+
+static int find_large_solid_color_rect(VncState *vs, int x, int y,
+                                       int w, int h, int max_rows)
+{
+    int dx, dy, dw, dh;
+    int n = 0;
+
+    /* Try to find large solid-color areas and send them separately. */
+
+    for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+        /* If a rectangle becomes too large, send its upper part now. */
+
+        if (dy - y >= max_rows) {
+            n += send_rect_simple(vs, x, y, w, max_rows);
+            y += max_rows;
+            h -= max_rows;
+        }
+
+        dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy));
+
+        for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+            uint32_t color_value;
+            int x_best, y_best, w_best, h_best;
+
+            dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx));
+
+            if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) {
+                continue ;
+            }
+
+            /* Get dimensions of solid-color area. */
+
+            find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y),
+                                 color_value, &w_best, &h_best);
+
+            /* Make sure a solid rectangle is large enough
+               (or the whole rectangle is of the same color). */
+
+            if (w_best * h_best != w * h &&
+                w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) {
+                continue;
+            }
+
+            /* Try to extend solid rectangle to maximum size. */
+
+            x_best = dx; y_best = dy;
+            extend_solid_area(vs, x, y, w, h, color_value,
+                              &x_best, &y_best, &w_best, &h_best);
+
+            /* Send rectangles at top and left to solid-color area. */
+
+            if (y_best != y) {
+                n += send_rect_simple(vs, x, y, w, y_best-y);
+            }
+            if (x_best != x) {
+                n += tight_send_framebuffer_update(vs, x, y_best,
+                                                   x_best-x, h_best);
+            }
+
+            /* Send solid-color rectangle. */
+            n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best);
+
+            /* Send remaining rectangles (at right and bottom). */
+
+            if (x_best + w_best != x + w) {
+                n += tight_send_framebuffer_update(vs, x_best+w_best,
+                                                   y_best,
+                                                   w-(x_best-x)-w_best,
+                                                   h_best);
+            }
+            if (y_best + h_best != y + h) {
+                n += tight_send_framebuffer_update(vs, x, y_best+h_best,
+                                                   w, h-(y_best-y)-h_best);
+            }
+
+            /* Return after all recursive calls are done. */
+            return n;
+        }
+    }
+    return n + send_rect_simple(vs, x, y, w, h);
+}
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+                                         int w, int h)
+{
+    int max_rows;
+
+    if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF &&
+        vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) {
+        vs->tight.pixel24 = true;
+    } else {
+        vs->tight.pixel24 = false;
+    }
+
+    if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE)
+        return send_rect_simple(vs, x, y, w, h);
+
+    /* Calculate maximum number of rows in one non-solid rectangle. */
+
+    max_rows = tight_conf[vs->tight.compression].max_rect_size;
+    max_rows /= MIN(tight_conf[vs->tight.compression].max_rect_width, w);
+
+    return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
+}
+
+int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
+                                      int w, int h)
+{
+    vs->tight.type = VNC_ENCODING_TIGHT;
+    return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+                                          int w, int h)
+{
+    vs->tight.type = VNC_ENCODING_TIGHT_PNG;
+    return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+void vnc_tight_clear(VncState *vs)
+{
+    int i;
+    for (i=0; i<ARRAY_SIZE(vs->tight.stream); i++) {
+        if (vs->tight.stream[i].opaque) {
+            deflateEnd(&vs->tight.stream[i]);
+        }
+    }
+
+    buffer_free(&vs->tight.tight);
+    buffer_free(&vs->tight.zlib);
+    buffer_free(&vs->tight.gradient);
+#ifdef CONFIG_VNC_JPEG
+    buffer_free(&vs->tight.jpeg);
+#endif
+#ifdef CONFIG_VNC_PNG
+    buffer_free(&vs->tight.png);
+#endif
+}
diff --git a/vnc-encoding-tight.h b/ui/vnc-enc-tight.h
index 64d10625fe..a3add788e2 100644
--- a/vnc-encoding-tight.h
+++ b/ui/vnc-enc-tight.h
@@ -42,8 +42,9 @@
  *   bit 3:    if 1, then compression stream 3 should be reset;
  *   bits 7-4: if 1000 (0x08), then the compression type is "fill",
  *             if 1001 (0x09), then the compression type is "jpeg",
+ *             if 1010 (0x0A), then the compression type is "png",
  *             if 0xxx, then the compression type is "basic",
- *             values greater than 1001 are not valid.
+ *             values greater than 1010 are not valid.
  *
  * If the compression type is "basic", then bits 6..4 of the
  * compression control byte (those xxx in 0xxx) specify the following:
@@ -53,17 +54,17 @@
  *   bit 6:     if 1, then a "filter id" byte is following this byte.
  *
  *-- The data that follows after the compression control byte described
- * above depends on the compression type ("fill", "jpeg" or "basic").
+ * above depends on the compression type ("fill", "jpeg", "png" or "basic").
  *
  *-- If the compression type is "fill", then the only pixel value follows, in
  * client pixel format (see NOTE 1). This value applies to all pixels of the
  * rectangle.
  *
- *-- If the compression type is "jpeg", the following data stream looks like
- * this:
+ *-- If the compression type is "jpeg" or "png", the following data stream
+ * looks like this:
  *
  *   1..3 bytes:  data size (N) in compact representation;
- *   N bytes:     JPEG image.
+ *   N bytes:     JPEG or PNG image.
  *
  * Data size is compactly represented in one, two or three bytes, according
  * to the following scheme:
@@ -144,7 +145,7 @@
  *-- NOTE 2. The decoder must reset compression streams' states before
  * decoding the rectangle, if some of bits 0,1,2,3 in the compression control
  * byte are set to 1. Note that the decoder must reset zlib streams even if
- * the compression type is "fill" or "jpeg".
+ * the compression type is "fill", "jpeg" or "png".
  *
  *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only
  * when bits-per-pixel value is either 16 or 32, not 8.
@@ -158,7 +159,8 @@
 #define VNC_TIGHT_EXPLICIT_FILTER       0x04
 #define VNC_TIGHT_FILL                  0x08
 #define VNC_TIGHT_JPEG                  0x09
-#define VNC_TIGHT_MAX_SUBENCODING       0x09
+#define VNC_TIGHT_PNG                   0x0A
+#define VNC_TIGHT_MAX_SUBENCODING       0x0A
 
 /* Filters to improve compression efficiency */
 #define VNC_TIGHT_FILTER_COPY             0x00
@@ -173,4 +175,9 @@
 #define VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE  2048
 #define VNC_TIGHT_MAX_SPLIT_TILE_SIZE       16
 
+#define VNC_TIGHT_JPEG_MIN_RECT_SIZE      4096
+#define VNC_TIGHT_DETECT_SUBROW_WIDTH        7
+#define VNC_TIGHT_DETECT_MIN_WIDTH           8
+#define VNC_TIGHT_DETECT_MIN_HEIGHT          8
+
 #endif /* VNC_ENCODING_TIGHT_H */
diff --git a/vnc-encoding-zlib.c b/ui/vnc-enc-zlib.c
index a99bc387dc..3c6e6abf94 100644
--- a/vnc-encoding-zlib.c
+++ b/ui/vnc-enc-zlib.c
@@ -47,21 +47,21 @@ void vnc_zlib_zfree(void *x, void *addr)
 
 static void vnc_zlib_start(VncState *vs)
 {
-    buffer_reset(&vs->zlib);
+    buffer_reset(&vs->zlib.zlib);
 
     // make the output buffer be the zlib buffer, so we can compress it later
-    vs->zlib_tmp = vs->output;
-    vs->output = vs->zlib;
+    vs->zlib.tmp = vs->output;
+    vs->output = vs->zlib.zlib;
 }
 
 static int vnc_zlib_stop(VncState *vs)
 {
-    z_streamp zstream = &vs->zlib_stream;
+    z_streamp zstream = &vs->zlib.stream;
     int previous_out;
 
     // switch back to normal output/zlib buffers
-    vs->zlib = vs->output;
-    vs->output = vs->zlib_tmp;
+    vs->zlib.zlib = vs->output;
+    vs->output = vs->zlib.tmp;
 
     // compress the zlib buffer
 
@@ -75,7 +75,7 @@ static int vnc_zlib_stop(VncState *vs)
         zstream->zalloc = vnc_zlib_zalloc;
         zstream->zfree = vnc_zlib_zfree;
 
-        err = deflateInit2(zstream, vs->tight_compression, Z_DEFLATED, MAX_WBITS,
+        err = deflateInit2(zstream, vs->tight.compression, Z_DEFLATED, MAX_WBITS,
                            MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
 
         if (err != Z_OK) {
@@ -83,24 +83,24 @@ static int vnc_zlib_stop(VncState *vs)
             return -1;
         }
 
-        vs->zlib_level = vs->tight_compression;
+        vs->zlib.level = vs->tight.compression;
         zstream->opaque = vs;
     }
 
-    if (vs->tight_compression != vs->zlib_level) {
-        if (deflateParams(zstream, vs->tight_compression,
+    if (vs->tight.compression != vs->zlib.level) {
+        if (deflateParams(zstream, vs->tight.compression,
                           Z_DEFAULT_STRATEGY) != Z_OK) {
             return -1;
         }
-        vs->zlib_level = vs->tight_compression;
+        vs->zlib.level = vs->tight.compression;
     }
 
     // reserve memory in output buffer
-    buffer_reserve(&vs->output, vs->zlib.offset + 64);
+    buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64);
 
     // set pointers
-    zstream->next_in = vs->zlib.buffer;
-    zstream->avail_in = vs->zlib.offset;
+    zstream->next_in = vs->zlib.zlib.buffer;
+    zstream->avail_in = vs->zlib.zlib.offset;
     zstream->next_out = vs->output.buffer + vs->output.offset;
     zstream->avail_out = vs->output.capacity - vs->output.offset;
     zstream->data_type = Z_BINARY;
@@ -145,8 +145,8 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
 
 void vnc_zlib_clear(VncState *vs)
 {
-    if (vs->zlib_stream.opaque) {
-        deflateEnd(&vs->zlib_stream);
+    if (vs->zlib.stream.opaque) {
+        deflateEnd(&vs->zlib.stream);
     }
-    buffer_free(&vs->zlib);
+    buffer_free(&vs->zlib.zlib);
 }
diff --git a/ui/vnc-jobs-async.c b/ui/vnc-jobs-async.c
new file mode 100644
index 0000000000..6e9cf08b69
--- /dev/null
+++ b/ui/vnc-jobs-async.c
@@ -0,0 +1,331 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+
+/*
+ * Locking:
+ *
+ * There is three levels of locking:
+ * - jobs queue lock: for each operation on the queue (push, pop, isEmpty?)
+ * - VncDisplay global lock: mainly used for framebuffer updates to avoid
+ *                      screen corruption if the framebuffer is updated
+ *			while the worker is doing something.
+ * - VncState::output lock: used to make sure the output buffer is not corrupted
+ * 		   	 if two threads try to write on it at the same time
+ *
+ * While the VNC worker thread is working, the VncDisplay global lock is hold
+ * to avoid screen corruptions (this does not block vnc_refresh() because it
+ * uses trylock()) but the output lock is not hold because the thread work on
+ * its own output buffer.
+ * When the encoding job is done, the worker thread will hold the output lock
+ * and copy its output buffer in vs->output.
+*/
+
+struct VncJobQueue {
+    QemuCond cond;
+    QemuMutex mutex;
+    QemuThread thread;
+    Buffer buffer;
+    bool exit;
+    QTAILQ_HEAD(, VncJob) jobs;
+};
+
+typedef struct VncJobQueue VncJobQueue;
+
+/*
+ * We use a single global queue, but most of the functions are
+ * already reetrant, so we can easilly add more than one encoding thread
+ */
+static VncJobQueue *queue;
+
+static void vnc_lock_queue(VncJobQueue *queue)
+{
+    qemu_mutex_lock(&queue->mutex);
+}
+
+static void vnc_unlock_queue(VncJobQueue *queue)
+{
+    qemu_mutex_unlock(&queue->mutex);
+}
+
+VncJob *vnc_job_new(VncState *vs)
+{
+    VncJob *job = qemu_mallocz(sizeof(VncJob));
+
+    job->vs = vs;
+    vnc_lock_queue(queue);
+    QLIST_INIT(&job->rectangles);
+    vnc_unlock_queue(queue);
+    return job;
+}
+
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
+{
+    VncRectEntry *entry = qemu_mallocz(sizeof(VncRectEntry));
+
+    entry->rect.x = x;
+    entry->rect.y = y;
+    entry->rect.w = w;
+    entry->rect.h = h;
+
+    vnc_lock_queue(queue);
+    QLIST_INSERT_HEAD(&job->rectangles, entry, next);
+    vnc_unlock_queue(queue);
+    return 1;
+}
+
+void vnc_job_push(VncJob *job)
+{
+    vnc_lock_queue(queue);
+    if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
+        qemu_free(job);
+    } else {
+        QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
+        qemu_cond_broadcast(&queue->cond);
+    }
+    vnc_unlock_queue(queue);
+}
+
+static bool vnc_has_job_locked(VncState *vs)
+{
+    VncJob *job;
+
+    QTAILQ_FOREACH(job, &queue->jobs, next) {
+        if (job->vs == vs || !vs) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool vnc_has_job(VncState *vs)
+{
+    bool ret;
+
+    vnc_lock_queue(queue);
+    ret = vnc_has_job_locked(vs);
+    vnc_unlock_queue(queue);
+    return ret;
+}
+
+void vnc_jobs_clear(VncState *vs)
+{
+    VncJob *job, *tmp;
+
+    vnc_lock_queue(queue);
+    QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) {
+        if (job->vs == vs || !vs) {
+            QTAILQ_REMOVE(&queue->jobs, job, next);
+        }
+    }
+    vnc_unlock_queue(queue);
+}
+
+void vnc_jobs_join(VncState *vs)
+{
+    vnc_lock_queue(queue);
+    while (vnc_has_job_locked(vs)) {
+        qemu_cond_wait(&queue->cond, &queue->mutex);
+    }
+    vnc_unlock_queue(queue);
+}
+
+/*
+ * Copy data for local use
+ */
+static void vnc_async_encoding_start(VncState *orig, VncState *local)
+{
+    local->vnc_encoding = orig->vnc_encoding;
+    local->features = orig->features;
+    local->ds = orig->ds;
+    local->vd = orig->vd;
+    local->write_pixels = orig->write_pixels;
+    local->clientds = orig->clientds;
+    local->tight = orig->tight;
+    local->zlib = orig->zlib;
+    local->hextile = orig->hextile;
+    local->output =  queue->buffer;
+    local->csock = -1; /* Don't do any network work on this thread */
+
+    buffer_reset(&local->output);
+}
+
+static void vnc_async_encoding_end(VncState *orig, VncState *local)
+{
+    orig->tight = local->tight;
+    orig->zlib = local->zlib;
+    orig->hextile = local->hextile;
+}
+
+static int vnc_worker_thread_loop(VncJobQueue *queue)
+{
+    VncJob *job;
+    VncRectEntry *entry, *tmp;
+    VncState vs;
+    int n_rectangles;
+    int saved_offset;
+    bool flush;
+
+    vnc_lock_queue(queue);
+    while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
+        qemu_cond_wait(&queue->cond, &queue->mutex);
+    }
+    /* Here job can only be NULL if queue->exit is true */
+    job = QTAILQ_FIRST(&queue->jobs);
+    vnc_unlock_queue(queue);
+
+    if (queue->exit) {
+        return -1;
+    }
+
+    vnc_lock_output(job->vs);
+    if (job->vs->csock == -1 || job->vs->abort == true) {
+        goto disconnected;
+    }
+    vnc_unlock_output(job->vs);
+
+    /* Make a local copy of vs and switch output buffers */
+    vnc_async_encoding_start(job->vs, &vs);
+
+    /* Start sending rectangles */
+    n_rectangles = 0;
+    vnc_write_u8(&vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+    vnc_write_u8(&vs, 0);
+    saved_offset = vs.output.offset;
+    vnc_write_u16(&vs, 0);
+
+    vnc_lock_display(job->vs->vd);
+    QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
+        int n;
+
+        if (job->vs->csock == -1) {
+            vnc_unlock_display(job->vs->vd);
+            goto disconnected;
+        }
+
+        n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
+                                        entry->rect.w, entry->rect.h);
+
+        if (n >= 0) {
+            n_rectangles += n;
+        }
+        qemu_free(entry);
+    }
+    vnc_unlock_display(job->vs->vd);
+
+    /* Put n_rectangles at the beginning of the message */
+    vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+    vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+
+    /* Switch back buffers */
+    vnc_lock_output(job->vs);
+    if (job->vs->csock == -1) {
+        goto disconnected;
+    }
+
+    vnc_write(job->vs, vs.output.buffer, vs.output.offset);
+
+disconnected:
+    /* Copy persistent encoding data */
+    vnc_async_encoding_end(job->vs, &vs);
+    flush = (job->vs->csock != -1 && job->vs->abort != true);
+    vnc_unlock_output(job->vs);
+
+    if (flush) {
+        vnc_flush(job->vs);
+    }
+
+    vnc_lock_queue(queue);
+    QTAILQ_REMOVE(&queue->jobs, job, next);
+    vnc_unlock_queue(queue);
+    qemu_cond_broadcast(&queue->cond);
+    qemu_free(job);
+    return 0;
+}
+
+static VncJobQueue *vnc_queue_init(void)
+{
+    VncJobQueue *queue = qemu_mallocz(sizeof(VncJobQueue));
+
+    qemu_cond_init(&queue->cond);
+    qemu_mutex_init(&queue->mutex);
+    QTAILQ_INIT(&queue->jobs);
+    return queue;
+}
+
+static void vnc_queue_clear(VncJobQueue *q)
+{
+    qemu_cond_destroy(&queue->cond);
+    qemu_mutex_destroy(&queue->mutex);
+    buffer_free(&queue->buffer);
+    qemu_free(q);
+    queue = NULL; /* Unset global queue */
+}
+
+static void *vnc_worker_thread(void *arg)
+{
+    VncJobQueue *queue = arg;
+
+    qemu_thread_self(&queue->thread);
+
+    while (!vnc_worker_thread_loop(queue)) ;
+    vnc_queue_clear(queue);
+    return NULL;
+}
+
+void vnc_start_worker_thread(void)
+{
+    VncJobQueue *q;
+
+    if (vnc_worker_thread_running())
+        return ;
+
+    q = vnc_queue_init();
+    qemu_thread_create(&q->thread, vnc_worker_thread, q);
+    queue = q; /* Set global queue */
+}
+
+bool vnc_worker_thread_running(void)
+{
+    return queue; /* Check global queue */
+}
+
+void vnc_stop_worker_thread(void)
+{
+    if (!vnc_worker_thread_running())
+        return ;
+
+    /* Remove all jobs and wake up the thread */
+    vnc_lock_queue(queue);
+    queue->exit = true;
+    vnc_unlock_queue(queue);
+    vnc_jobs_clear(NULL);
+    qemu_cond_broadcast(&queue->cond);
+}
diff --git a/ui/vnc-jobs-sync.c b/ui/vnc-jobs-sync.c
new file mode 100644
index 0000000000..49b77afcc9
--- /dev/null
+++ b/ui/vnc-jobs-sync.c
@@ -0,0 +1,73 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+
+void vnc_jobs_clear(VncState *vs)
+{
+}
+
+void vnc_jobs_join(VncState *vs)
+{
+}
+
+VncJob *vnc_job_new(VncState *vs)
+{
+    vs->job.vs = vs;
+    vs->job.rectangles = 0;
+
+    vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+    vnc_write_u8(vs, 0);
+    vs->job.saved_offset = vs->output.offset;
+    vnc_write_u16(vs, 0);
+    return &vs->job;
+}
+
+void vnc_job_push(VncJob *job)
+{
+    VncState *vs = job->vs;
+
+    vs->output.buffer[job->saved_offset] = (job->rectangles >> 8) & 0xFF;
+    vs->output.buffer[job->saved_offset + 1] = job->rectangles & 0xFF;
+    vnc_flush(job->vs);
+}
+
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
+{
+    int n;
+
+    n = vnc_send_framebuffer_update(job->vs, x, y, w, h);
+    if (n >= 0)
+        job->rectangles += n;
+    return n;
+}
+
+bool vnc_has_job(VncState *vs)
+{
+    return false;
+}
diff --git a/ui/vnc-jobs.h b/ui/vnc-jobs.h
new file mode 100644
index 0000000000..b8dab8169f
--- /dev/null
+++ b/ui/vnc-jobs.h
@@ -0,0 +1,87 @@
+/*
+ * QEMU VNC display driver
+ *
+ * From libvncserver/rfb/rfbproto.h
+ * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
+ * Copyright (C) 2000-2002 Constantin Kaplinsky.  All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation.  All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ *
+ * 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 VNC_JOBS_H
+#define VNC_JOBS_H
+
+/* Jobs */
+VncJob *vnc_job_new(VncState *vs);
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h);
+void vnc_job_push(VncJob *job);
+bool vnc_has_job(VncState *vs);
+void vnc_jobs_clear(VncState *vs);
+void vnc_jobs_join(VncState *vs);
+
+#ifdef CONFIG_VNC_THREAD
+
+void vnc_start_worker_thread(void);
+bool vnc_worker_thread_running(void);
+void vnc_stop_worker_thread(void);
+
+#endif /* CONFIG_VNC_THREAD */
+
+/* Locks */
+static inline int vnc_trylock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+    return qemu_mutex_trylock(&vd->mutex);
+#else
+    return 0;
+#endif
+}
+
+static inline void vnc_lock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+    qemu_mutex_lock(&vd->mutex);
+#endif
+}
+
+static inline void vnc_unlock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+    qemu_mutex_unlock(&vd->mutex);
+#endif
+}
+
+static inline void vnc_lock_output(VncState *vs)
+{
+#ifdef CONFIG_VNC_THREAD
+    qemu_mutex_lock(&vs->output_mutex);
+#endif
+}
+
+static inline void vnc_unlock_output(VncState *vs)
+{
+#ifdef CONFIG_VNC_THREAD
+    qemu_mutex_unlock(&vs->output_mutex);
+#endif
+}
+
+#endif /* VNC_JOBS_H */
diff --git a/ui/vnc-palette.c b/ui/vnc-palette.c
new file mode 100644
index 0000000000..bff6445cc1
--- /dev/null
+++ b/ui/vnc-palette.c
@@ -0,0 +1,136 @@
+/*
+ * QEMU VNC display driver: palette hash table
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky.  All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc-palette.h"
+
+static VncPaletteEntry *palette_find(const VncPalette *palette,
+                                     uint32_t color, unsigned int hash)
+{
+    VncPaletteEntry *entry;
+
+    QLIST_FOREACH(entry, &palette->table[hash], next) {
+        if (entry->color == color) {
+            return entry;
+        }
+    }
+
+    return NULL;
+}
+
+static unsigned int palette_hash(uint32_t rgb, int bpp)
+{
+    if (bpp == 16) {
+        return ((unsigned int)(((rgb >> 8) + rgb) & 0xFF));
+    } else {
+        return ((unsigned int)(((rgb >> 16) + (rgb >> 8)) & 0xFF));
+    }
+}
+
+VncPalette *palette_new(size_t max, int bpp)
+{
+    VncPalette *palette;
+
+    palette = qemu_mallocz(sizeof(*palette));
+    palette->max = max;
+    palette->bpp = bpp;
+    return palette;
+}
+
+void palette_destroy(VncPalette *palette)
+{
+    int i;
+
+    if (palette == NULL) {
+        return ;
+    }
+
+    for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) {
+        VncPaletteEntry *entry = QLIST_FIRST(&palette->table[i]);
+        while (entry) {
+            VncPaletteEntry *tmp = QLIST_NEXT(entry, next);
+            QLIST_REMOVE(entry, next);
+            qemu_free(entry);
+            entry = tmp;
+        }
+    }
+
+    qemu_free(palette);
+}
+
+int palette_put(VncPalette *palette, uint32_t color)
+{
+    unsigned int hash;
+    unsigned int idx = palette->size;
+    VncPaletteEntry *entry;
+
+    hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE;
+    entry = palette_find(palette, color, hash);
+
+    if (!entry && palette->size >= palette->max) {
+        return 0;
+    }
+    if (!entry) {
+        VncPaletteEntry *entry;
+
+        entry = qemu_mallocz(sizeof(*entry));
+        entry->color = color;
+        entry->idx = idx;
+        QLIST_INSERT_HEAD(&palette->table[hash], entry, next);
+        palette->size++;
+    }
+    return palette->size;
+}
+
+int palette_idx(const VncPalette *palette, uint32_t color)
+{
+    VncPaletteEntry *entry;
+    unsigned int hash;
+
+    hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE;
+    entry = palette_find(palette, color, hash);
+    return (entry == NULL ? -1 : entry->idx);
+}
+
+size_t palette_size(const VncPalette *palette)
+{
+    return palette->size;
+}
+
+void palette_iter(const VncPalette *palette,
+                  void (*iter)(int idx, uint32_t color, void *opaque),
+                  void *opaque)
+{
+    int i;
+    VncPaletteEntry *entry;
+
+    for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) {
+        QLIST_FOREACH(entry, &palette->table[i], next) {
+            iter(entry->idx, entry->color, opaque);
+        }
+    }
+}
diff --git a/ui/vnc-palette.h b/ui/vnc-palette.h
new file mode 100644
index 0000000000..d0645ebde8
--- /dev/null
+++ b/ui/vnc-palette.h
@@ -0,0 +1,63 @@
+/*
+ * QEMU VNC display driver: palette hash table
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky.  All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_PALETTE_H
+#define VNC_PALETTE_H
+
+#include "qlist.h"
+#include "qemu-queue.h"
+#include <stdint.h>
+
+#define VNC_PALETTE_HASH_SIZE 256
+
+typedef struct VncPaletteEntry {
+    int idx;
+    uint32_t color;
+    QLIST_ENTRY(VncPaletteEntry) next;
+} VncPaletteEntry;
+
+typedef struct VncPalette {
+    QObject_HEAD;
+    size_t size;
+    size_t max;
+    int bpp;
+    QLIST_HEAD(,VncPaletteEntry) table[VNC_PALETTE_HASH_SIZE];
+} VncPalette;
+
+VncPalette *palette_new(size_t max, int bpp);
+void palette_destroy(VncPalette *palette);
+
+int palette_put(VncPalette *palette, uint32_t color);
+int palette_idx(const VncPalette *palette, uint32_t color);
+size_t palette_size(const VncPalette *palette);
+
+void palette_iter(const VncPalette *palette,
+                  void (*iter)(int idx, uint32_t color, void *opaque),
+                  void *opaque);
+
+#endif /* VNC_PALETTE_H */
diff --git a/vnc-tls.c b/ui/vnc-tls.c
index dec626c539..dec626c539 100644
--- a/vnc-tls.c
+++ b/ui/vnc-tls.c
diff --git a/vnc-tls.h b/ui/vnc-tls.h
index 2b93633896..2b93633896 100644
--- a/vnc-tls.h
+++ b/ui/vnc-tls.h
diff --git a/vnc.c b/ui/vnc.c
index ed0e096000..7fc40acae8 100644
--- a/vnc.c
+++ b/ui/vnc.c
@@ -25,6 +25,7 @@
  */
 
 #include "vnc.h"
+#include "vnc-jobs.h"
 #include "sysemu.h"
 #include "qemu_socket.h"
 #include "qemu-timer.h"
@@ -45,7 +46,6 @@
     } \
 }
 
-
 static VncDisplay *vnc_display; /* needed for info vnc */
 static DisplayChangeListener *dcl;
 
@@ -351,10 +351,6 @@ void do_info_vnc(Monitor *mon, QObject **ret_data)
     }
 }
 
-static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
-    return (vs->features & (1 << feature));
-}
-
 /* TODO
    1) Get the queue working for IO.
    2) there is some weirdness when using the -S option (the screen is grey
@@ -363,6 +359,7 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
 */
 
 static int vnc_update_client(VncState *vs, int has_dirty);
+static int vnc_update_client_sync(VncState *vs, int has_dirty);
 static void vnc_disconnect_start(VncState *vs);
 static void vnc_disconnect_finish(VncState *vs);
 static void vnc_init_timer(VncDisplay *vd);
@@ -506,19 +503,48 @@ static void vnc_desktop_resize(VncState *vs)
     }
     vs->client_width = ds_get_width(ds);
     vs->client_height = ds_get_height(ds);
+    vnc_lock_output(vs);
     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
     vnc_write_u8(vs, 0);
     vnc_write_u16(vs, 1); /* number of rects */
     vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
                            VNC_ENCODING_DESKTOPRESIZE);
+    vnc_unlock_output(vs);
     vnc_flush(vs);
 }
 
+#ifdef CONFIG_VNC_THREAD
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+    VncState *vs;
+
+    QTAILQ_FOREACH(vs, &vd->clients, next) {
+        vnc_lock_output(vs);
+        vs->abort = true;
+        vnc_unlock_output(vs);
+    }
+    QTAILQ_FOREACH(vs, &vd->clients, next) {
+        vnc_jobs_join(vs);
+    }
+    QTAILQ_FOREACH(vs, &vd->clients, next) {
+        vnc_lock_output(vs);
+        vs->abort = false;
+        vnc_unlock_output(vs);
+    }
+}
+#else
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+}
+#endif
+
 static void vnc_dpy_resize(DisplayState *ds)
 {
     VncDisplay *vd = ds->opaque;
     VncState *vs;
 
+    vnc_abort_display_jobs(vd);
+
     /* server surface */
     if (!vd->server)
         vd->server = qemu_mallocz(sizeof(*vd->server));
@@ -646,7 +672,7 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
     return 1;
 }
 
-static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
 {
     int n = 0;
 
@@ -661,6 +687,9 @@ static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
         case VNC_ENCODING_TIGHT:
             n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
             break;
+        case VNC_ENCODING_TIGHT_PNG:
+            n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
+            break;
         default:
             vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
             n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
@@ -672,12 +701,14 @@ static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
 static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
 {
     /* send bitblit op to the vnc client */
+    vnc_lock_output(vs);
     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
     vnc_write_u8(vs, 0);
     vnc_write_u16(vs, 1); /* number of rects */
     vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
     vnc_write_u16(vs, src_x);
     vnc_write_u16(vs, src_y);
+    vnc_unlock_output(vs);
     vnc_flush(vs);
 }
 
@@ -694,7 +725,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int
     QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
         if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
             vs->force_update = 1;
-            vnc_update_client(vs, 1);
+            vnc_update_client_sync(vs, 1);
             /* vs might be free()ed here */
         }
     }
@@ -765,6 +796,7 @@ static int vnc_cursor_define(VncState *vs)
     int isize;
 
     if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
+        vnc_lock_output(vs);
         vnc_write_u8(vs,  VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
         vnc_write_u8(vs,  0);  /*  padding     */
         vnc_write_u16(vs, 1);  /*  # of rects  */
@@ -773,6 +805,7 @@ static int vnc_cursor_define(VncState *vs)
         isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel;
         vnc_write_pixels_generic(vs, &pf, c->data, isize);
         vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
+        vnc_unlock_output(vs);
         return 0;
     }
     return -1;
@@ -814,15 +847,29 @@ static int find_and_clear_dirty_height(struct VncState *vs,
     return h;
 }
 
+#ifdef CONFIG_VNC_THREAD
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+    int ret = vnc_update_client(vs, has_dirty);
+    vnc_jobs_join(vs);
+    return ret;
+}
+#else
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+    return vnc_update_client(vs, has_dirty);
+}
+#endif
+
 static int vnc_update_client(VncState *vs, int has_dirty)
 {
     if (vs->need_update && vs->csock != -1) {
         VncDisplay *vd = vs->vd;
+        VncJob *job;
         int y;
-        int n_rectangles;
-        int saved_offset;
         int width, height;
-        int n;
+        int n = 0;
+
 
         if (vs->output.offset && !vs->audio_cap && !vs->force_update)
             /* kernel send buffers are full -> drop frames to throttle */
@@ -837,11 +884,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
          * happening in parallel don't disturb us, the next pass will
          * send them to the client.
          */
-        n_rectangles = 0;
-        vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
-        vnc_write_u8(vs, 0);
-        saved_offset = vs->output.offset;
-        vnc_write_u16(vs, 0);
+        job = vnc_job_new(vs);
 
         width = MIN(vd->server->width, vs->client_width);
         height = MIN(vd->server->height, vs->client_height);
@@ -858,25 +901,23 @@ static int vnc_update_client(VncState *vs, int has_dirty)
                 } else {
                     if (last_x != -1) {
                         int h = find_and_clear_dirty_height(vs, y, last_x, x);
-                        n = send_framebuffer_update(vs, last_x * 16, y,
-                                                    (x - last_x) * 16, h);
-                        n_rectangles += n;
+
+                        n += vnc_job_add_rect(job, last_x * 16, y,
+                                              (x - last_x) * 16, h);
                     }
                     last_x = -1;
                 }
             }
             if (last_x != -1) {
                 int h = find_and_clear_dirty_height(vs, y, last_x, x);
-                n = send_framebuffer_update(vs, last_x * 16, y,
-                                            (x - last_x) * 16, h);
-                n_rectangles += n;
+                n += vnc_job_add_rect(job, last_x * 16, y,
+                                      (x - last_x) * 16, h);
             }
         }
-        vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
-        vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
-        vnc_flush(vs);
+
+        vnc_job_push(job);
         vs->force_update = 0;
-        return n_rectangles;
+        return n;
     }
 
     if (vs->csock == -1)
@@ -892,16 +933,20 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd)
 
     switch (cmd) {
     case AUD_CNOTIFY_DISABLE:
+        vnc_lock_output(vs);
         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
         vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
+        vnc_unlock_output(vs);
         vnc_flush(vs);
         break;
 
     case AUD_CNOTIFY_ENABLE:
+        vnc_lock_output(vs);
         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
         vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
+        vnc_unlock_output(vs);
         vnc_flush(vs);
         break;
     }
@@ -915,11 +960,13 @@ static void audio_capture(void *opaque, void *buf, int size)
 {
     VncState *vs = opaque;
 
+    vnc_lock_output(vs);
     vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
     vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
     vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
     vnc_write_u32(vs, size);
     vnc_write(vs, buf, size);
+    vnc_unlock_output(vs);
     vnc_flush(vs);
 }
 
@@ -961,6 +1008,9 @@ static void vnc_disconnect_start(VncState *vs)
 
 static void vnc_disconnect_finish(VncState *vs)
 {
+    vnc_jobs_join(vs); /* Wait encoding jobs */
+
+    vnc_lock_output(vs);
     vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED);
 
     buffer_free(&vs->input);
@@ -989,6 +1039,11 @@ static void vnc_disconnect_finish(VncState *vs)
     vnc_remove_timer(vs->vd);
     if (vs->vd->lock_key_sync)
         qemu_remove_led_event_handler(vs->led);
+    vnc_unlock_output(vs);
+
+#ifdef CONFIG_VNC_THREAD
+    qemu_mutex_destroy(&vs->output_mutex);
+#endif
     qemu_free(vs);
 }
 
@@ -1108,7 +1163,7 @@ static long vnc_client_write_plain(VncState *vs)
  * the client socket. Will delegate actual work according to whether
  * SASL SSF layers are enabled (thus requiring encryption calls)
  */
-void vnc_client_write(void *opaque)
+static void vnc_client_write_locked(void *opaque)
 {
     VncState *vs = opaque;
 
@@ -1122,6 +1177,19 @@ void vnc_client_write(void *opaque)
         vnc_client_write_plain(vs);
 }
 
+void vnc_client_write(void *opaque)
+{
+    VncState *vs = opaque;
+
+    vnc_lock_output(vs);
+    if (vs->output.offset) {
+        vnc_client_write_locked(opaque);
+    } else {
+        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+    }
+    vnc_unlock_output(vs);
+}
+
 void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
 {
     vs->read_handler = func;
@@ -1273,8 +1341,11 @@ void vnc_write_u8(VncState *vs, uint8_t value)
 
 void vnc_flush(VncState *vs)
 {
-    if (vs->csock != -1 && vs->output.offset)
-        vnc_client_write(vs);
+    vnc_lock_output(vs);
+    if (vs->csock != -1 && vs->output.offset) {
+        vnc_client_write_locked(vs);
+    }
+    vnc_unlock_output(vs);
 }
 
 uint8_t read_u8(uint8_t *data, size_t offset)
@@ -1309,12 +1380,14 @@ static void check_pointer_type_change(Notifier *notifier)
     int absolute = kbd_mouse_is_absolute();
 
     if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
+        vnc_lock_output(vs);
         vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
         vnc_write_u8(vs, 0);
         vnc_write_u16(vs, 1);
         vnc_framebuffer_update(vs, absolute, 0,
                                ds_get_width(vs->ds), ds_get_height(vs->ds),
                                VNC_ENCODING_POINTER_TYPE_CHANGE);
+        vnc_unlock_output(vs);
         vnc_flush(vs);
     }
     vs->absolute = absolute;
@@ -1618,21 +1691,25 @@ static void framebuffer_update_request(VncState *vs, int incremental,
 
 static void send_ext_key_event_ack(VncState *vs)
 {
+    vnc_lock_output(vs);
     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
     vnc_write_u8(vs, 0);
     vnc_write_u16(vs, 1);
     vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
                            VNC_ENCODING_EXT_KEY_EVENT);
+    vnc_unlock_output(vs);
     vnc_flush(vs);
 }
 
 static void send_ext_audio_ack(VncState *vs)
 {
+    vnc_lock_output(vs);
     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
     vnc_write_u8(vs, 0);
     vnc_write_u16(vs, 1);
     vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
                            VNC_ENCODING_AUDIO);
+    vnc_unlock_output(vs);
     vnc_flush(vs);
 }
 
@@ -1643,8 +1720,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
 
     vs->features = 0;
     vs->vnc_encoding = 0;
-    vs->tight_compression = 9;
-    vs->tight_quality = 9;
+    vs->tight.compression = 9;
+    vs->tight.quality = -1; /* Lossless by default */
     vs->absolute = -1;
 
     /*
@@ -1669,6 +1746,10 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
             vs->features |= VNC_FEATURE_TIGHT_MASK;
             vs->vnc_encoding = enc;
             break;
+        case VNC_ENCODING_TIGHT_PNG:
+            vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
+            vs->vnc_encoding = enc;
+            break;
         case VNC_ENCODING_ZLIB:
             vs->features |= VNC_FEATURE_ZLIB_MASK;
             vs->vnc_encoding = enc;
@@ -1692,10 +1773,10 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
             vs->features |= VNC_FEATURE_WMVI_MASK;
             break;
         case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
-            vs->tight_compression = (enc & 0x0F);
+            vs->tight.compression = (enc & 0x0F);
             break;
         case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
-            vs->tight_quality = (enc & 0x0F);
+            vs->tight.quality = (enc & 0x0F);
             break;
         default:
             VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
@@ -1791,12 +1872,14 @@ static void vnc_colordepth(VncState *vs)
 {
     if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
         /* Sending a WMVi message to notify the client*/
+        vnc_lock_output(vs);
         vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
         vnc_write_u8(vs, 0);
         vnc_write_u16(vs, 1); /* number of rects */
         vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), 
                                ds_get_height(vs->ds), VNC_ENCODING_WMVi);
         pixel_format_message(vs);
+        vnc_unlock_output(vs);
         vnc_flush(vs);
     } else {
         set_pixel_conversion(vs);
@@ -2224,12 +2307,21 @@ static void vnc_refresh(void *opaque)
 
     vga_hw_update();
 
+    if (vnc_trylock_display(vd)) {
+        vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+        qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) +
+                       vd->timer_interval);
+        return;
+    }
+
     has_dirty = vnc_refresh_server_surface(vd);
+    vnc_unlock_display(vd);
 
     QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
         rects += vnc_update_client(vs, has_dirty);
         /* vs might be free()ed here */
     }
+
     /* vd->timer could be NULL now if the last client disconnected,
      * in this case don't update the timer */
     if (vd->timer == NULL)
@@ -2288,6 +2380,10 @@ static void vnc_connect(VncDisplay *vd, int csock)
     vs->as.fmt = AUD_FMT_S16;
     vs->as.endianness = 0;
 
+#ifdef CONFIG_VNC_THREAD
+    qemu_mutex_init(&vs->output_mutex);
+#endif
+
     QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
 
     vga_hw_update();
@@ -2345,6 +2441,11 @@ void vnc_display_init(DisplayState *ds)
     if (!vs->kbd_layout)
         exit(1);
 
+#ifdef CONFIG_VNC_THREAD
+    qemu_mutex_init(&vs->mutex);
+    vnc_start_worker_thread();
+#endif
+
     dcl->dpy_copy = vnc_dpy_copy;
     dcl->dpy_update = vnc_dpy_update;
     dcl->dpy_resize = vnc_dpy_resize;
@@ -2482,6 +2583,8 @@ int vnc_display_open(DisplayState *ds, const char *display)
 #endif
         } else if (strncmp(options, "acl", 3) == 0) {
             acl = 1;
+        } else if (strncmp(options, "lossy", 5) == 0) {
+            vs->lossy = true;
         }
     }
 
diff --git a/vnc.h b/ui/vnc.h
index 7b64cf730b..9619b247fb 100644
--- a/vnc.h
+++ b/ui/vnc.h
@@ -29,10 +29,14 @@
 
 #include "qemu-common.h"
 #include "qemu-queue.h"
+#ifdef CONFIG_VNC_THREAD
+#include "qemu-thread.h"
+#endif
 #include "console.h"
 #include "monitor.h"
 #include "audio/audio.h"
 #include <zlib.h>
+#include <stdbool.h>
 
 #include "keymaps.h"
 
@@ -58,6 +62,9 @@ typedef struct Buffer
 } Buffer;
 
 typedef struct VncState VncState;
+typedef struct VncJob VncJob;
+typedef struct VncRect VncRect;
+typedef struct VncRectEntry VncRectEntry;
 
 typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
 
@@ -100,6 +107,9 @@ struct VncDisplay
     DisplayState *ds;
     kbd_layout_t *kbd_layout;
     int lock_key_sync;
+#ifdef CONFIG_VNC_THREAD
+    QemuMutex mutex;
+#endif
 
     QEMUCursor *cursor;
     int cursor_msize;
@@ -111,6 +121,7 @@ struct VncDisplay
     char *display;
     char *password;
     int auth;
+    bool lossy;
 #ifdef CONFIG_VNC_TLS
     int subauth; /* Used by VeNCrypt */
     VncDisplayTLS tls;
@@ -120,6 +131,67 @@ struct VncDisplay
 #endif
 };
 
+typedef struct VncTight {
+    int type;
+    uint8_t quality;
+    uint8_t compression;
+    uint8_t pixel24;
+    Buffer tight;
+    Buffer tmp;
+    Buffer zlib;
+    Buffer gradient;
+#ifdef CONFIG_VNC_JPEG
+    Buffer jpeg;
+#endif
+#ifdef CONFIG_VNC_PNG
+    Buffer png;
+#endif
+    int levels[4];
+    z_stream stream[4];
+} VncTight;
+
+typedef struct VncHextile {
+    VncSendHextileTile *send_tile;
+} VncHextile;
+
+typedef struct VncZlib {
+    Buffer zlib;
+    Buffer tmp;
+    z_stream stream;
+    int level;
+} VncZlib;
+
+#ifdef CONFIG_VNC_THREAD
+struct VncRect
+{
+    int x;
+    int y;
+    int w;
+    int h;
+};
+
+struct VncRectEntry
+{
+    struct VncRect rect;
+    QLIST_ENTRY(VncRectEntry) next;
+};
+
+struct VncJob
+{
+    VncState *vs;
+
+    QLIST_HEAD(, VncRectEntry) rectangles;
+    QTAILQ_ENTRY(VncJob) next;
+};
+#else
+struct VncJob
+{
+    VncState *vs;
+    int rectangles;
+    size_t saved_offset;
+};
+#endif
+
 struct VncState
 {
     int csock;
@@ -167,26 +239,20 @@ struct VncState
     uint8_t modifiers_state[256];
     QEMUPutLEDEntry *led;
 
-    /* Encoding specific */
-
-    /* Tight */
-    uint8_t tight_quality;
-    uint8_t tight_compression;
-    uint8_t tight_pixel24;
-    Buffer tight;
-    Buffer tight_tmp;
-    Buffer tight_zlib;
-    int tight_levels[4];
-    z_stream tight_stream[4];
+    bool abort;
+#ifndef CONFIG_VNC_THREAD
+    VncJob job;
+#else
+    QemuMutex output_mutex;
+#endif
 
-    /* Hextile */
-    VncSendHextileTile *send_hextile_tile;
+    /* Encoding specific, if you add something here, don't forget to
+     *  update vnc_async_encoding_start()
+     */
+    VncTight tight;
+    VncZlib zlib;
+    VncHextile hextile;
 
-    /* Zlib */
-    Buffer zlib;
-    Buffer zlib_tmp;
-    z_stream zlib_stream;
-    int zlib_level;
 
     Notifier mouse_mode_notifier;
 
@@ -253,6 +319,7 @@ enum {
 #define VNC_ENCODING_POINTER_TYPE_CHANGE  0XFFFFFEFF /* -257 */
 #define VNC_ENCODING_EXT_KEY_EVENT        0XFFFFFEFE /* -258 */
 #define VNC_ENCODING_AUDIO                0XFFFFFEFD /* -259 */
+#define VNC_ENCODING_TIGHT_PNG            0xFFFFFEFC /* -260 */
 #define VNC_ENCODING_WMVi                 0x574D5669
 
 /*****************************************************************************
@@ -269,6 +336,7 @@ enum {
 #define VNC_TIGHT_CCB_TYPE_MASK    (0x0f << 4)
 #define VNC_TIGHT_CCB_TYPE_FILL    (0x08 << 4)
 #define VNC_TIGHT_CCB_TYPE_JPEG    (0x09 << 4)
+#define VNC_TIGHT_CCB_TYPE_PNG     (0x0A << 4)
 #define VNC_TIGHT_CCB_BASIC_MAX    (0x07 << 4)
 #define VNC_TIGHT_CCB_BASIC_ZLIB   (0x03 << 4)
 #define VNC_TIGHT_CCB_BASIC_FILTER (0x04 << 4)
@@ -287,6 +355,7 @@ enum {
 #define VNC_FEATURE_ZLIB                     5
 #define VNC_FEATURE_COPYRECT                 6
 #define VNC_FEATURE_RICH_CURSOR              7
+#define VNC_FEATURE_TIGHT_PNG                8
 
 #define VNC_FEATURE_RESIZE_MASK              (1 << VNC_FEATURE_RESIZE)
 #define VNC_FEATURE_HEXTILE_MASK             (1 << VNC_FEATURE_HEXTILE)
@@ -296,6 +365,7 @@ enum {
 #define VNC_FEATURE_ZLIB_MASK                (1 << VNC_FEATURE_ZLIB)
 #define VNC_FEATURE_COPYRECT_MASK            (1 << VNC_FEATURE_COPYRECT)
 #define VNC_FEATURE_RICH_CURSOR_MASK         (1 << VNC_FEATURE_RICH_CURSOR)
+#define VNC_FEATURE_TIGHT_PNG_MASK           (1 << VNC_FEATURE_TIGHT_PNG)
 
 
 /* Client -> Server message IDs */
@@ -399,6 +469,10 @@ void buffer_append(Buffer *buffer, const void *data, size_t len);
 char *vnc_socket_local_addr(const char *format, int fd);
 char *vnc_socket_remote_addr(const char *format, int fd);
 
+static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
+    return (vs->features & (1 << feature));
+}
+
 /* Framebuffer */
 void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
                             int32_t encoding);
@@ -406,6 +480,8 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
 void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v);
 
 /* Encodings */
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+
 int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
 
 int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
@@ -417,8 +493,9 @@ void vnc_zlib_zfree(void *x, void *addr);
 int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
 void vnc_zlib_clear(VncState *vs);
 
-
 int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+                                          int w, int h);
 void vnc_tight_clear(VncState *vs);
 
 #endif /* __QEMU_VNC_H */
diff --git a/vnc_keysym.h b/ui/vnc_keysym.h
index 55cb87edec..55cb87edec 100644
--- a/vnc_keysym.h
+++ b/ui/vnc_keysym.h
diff --git a/x_keymap.c b/ui/x_keymap.c
index b9b0944180..b9b0944180 100644
--- a/x_keymap.c
+++ b/ui/x_keymap.c
diff --git a/x_keymap.h b/ui/x_keymap.h
index 2042ce0ed2..2042ce0ed2 100644
--- a/x_keymap.h
+++ b/ui/x_keymap.h
diff --git a/vnc-encoding-tight.c b/vnc-encoding-tight.c
deleted file mode 100644
index faba4834cd..0000000000
--- a/vnc-encoding-tight.c
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
- * QEMU VNC display driver: tight encoding
- *
- * From libvncserver/libvncserver/tight.c
- * Copyright (C) 2000, 2001 Const Kaplinsky.  All Rights Reserved.
- * Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
- *
- * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qdict.h"
-#include "qint.h"
-#include "vnc.h"
-#include "vnc-encoding-tight.h"
-
-/* Compression level stuff. The following array contains various
-   encoder parameters for each of 10 compression levels (0..9).
-   Last three parameters correspond to JPEG quality levels (0..9). */
-
-static const struct {
-    int max_rect_size, max_rect_width;
-    int mono_min_rect_size, gradient_min_rect_size;
-    int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level;
-    int gradient_threshold, gradient_threshold24;
-    int idx_max_colors_divisor;
-    int jpeg_quality, jpeg_threshold, jpeg_threshold24;
-} tight_conf[] = {
-    {   512,   32,   6, 65536, 0, 0, 0, 0,   0,   0,   4,  5, 10000, 23000 },
-    {  2048,  128,   6, 65536, 1, 1, 1, 0,   0,   0,   8, 10,  8000, 18000 },
-    {  6144,  256,   8, 65536, 3, 3, 2, 0,   0,   0,  24, 15,  6500, 15000 },
-    { 10240, 1024,  12, 65536, 5, 5, 3, 0,   0,   0,  32, 25,  5000, 12000 },
-    { 16384, 2048,  12, 65536, 6, 6, 4, 0,   0,   0,  32, 37,  4000, 10000 },
-    { 32768, 2048,  12,  4096, 7, 7, 5, 4, 150, 380,  32, 50,  3000,  8000 },
-    { 65536, 2048,  16,  4096, 7, 7, 6, 4, 170, 420,  48, 60,  2000,  5000 },
-    { 65536, 2048,  16,  4096, 8, 8, 7, 5, 180, 450,  64, 70,  1000,  2500 },
-    { 65536, 2048,  32,  8192, 9, 9, 8, 6, 190, 475,  64, 75,   500,  1200 },
-    { 65536, 2048,  32,  8192, 9, 9, 9, 6, 200, 500,  96, 80,   200,   500 }
-};
-
-/*
- * Code to determine how many different colors used in rectangle.
- */
-
-static void tight_palette_rgb2buf(uint32_t rgb, int bpp, uint8_t buf[6])
-{
-    memset(buf, 0, 6);
-
-    if (bpp == 32) {
-        buf[0] = ((rgb >> 24) & 0xFF);
-        buf[1] = ((rgb >> 16) & 0xFF);
-        buf[2] = ((rgb >>  8) & 0xFF);
-        buf[3] = ((rgb >>  0) & 0xFF);
-        buf[4] = ((buf[0] & 1) == 0) << 3 | ((buf[1] & 1) == 0) << 2;
-        buf[4]|= ((buf[2] & 1) == 0) << 1 | ((buf[3] & 1) == 0) << 0;
-        buf[0] |= 1;
-        buf[1] |= 1;
-        buf[2] |= 1;
-        buf[3] |= 1;
-    }
-    if (bpp == 16) {
-        buf[0] = ((rgb >> 8) & 0xFF);
-        buf[1] = ((rgb >> 0) & 0xFF);
-        buf[2] = ((buf[0] & 1) == 0) << 1 | ((buf[1] & 1) == 0) << 0;
-        buf[0] |= 1;
-        buf[1] |= 1;
-    }
-}
-
-static uint32_t tight_palette_buf2rgb(int bpp, const uint8_t *buf)
-{
-    uint32_t rgb = 0;
-
-    if (bpp == 32) {
-        rgb |= ((buf[0] & ~1) | !((buf[4] >> 3) & 1)) << 24;
-        rgb |= ((buf[1] & ~1) | !((buf[4] >> 2) & 1)) << 16;
-        rgb |= ((buf[2] & ~1) | !((buf[4] >> 1) & 1)) <<  8;
-        rgb |= ((buf[3] & ~1) | !((buf[4] >> 0) & 1)) <<  0;
-    }
-    if (bpp == 16) {
-        rgb |= ((buf[0] & ~1) | !((buf[2] >> 1) & 1)) << 8;
-        rgb |= ((buf[1] & ~1) | !((buf[2] >> 0) & 1)) << 0;
-    }
-    return rgb;
-}
-
-
-static int tight_palette_insert(QDict *palette, uint32_t rgb, int bpp, int max)
-{
-    uint8_t key[6];
-    int idx = qdict_size(palette);
-    bool present;
-
-    tight_palette_rgb2buf(rgb, bpp, key);
-    present = qdict_haskey(palette, (char *)key);
-    if (idx >= max && !present) {
-        return 0;
-    }
-    if (!present) {
-        qdict_put(palette, (char *)key, qint_from_int(idx));
-    }
-    return qdict_size(palette);
-}
-
-#define DEFINE_FILL_PALETTE_FUNCTION(bpp)                               \
-                                                                        \
-    static int                                                          \
-    tight_fill_palette##bpp(VncState *vs, int x, int y,                 \
-                            int max, size_t count,                      \
-                            uint32_t *bg, uint32_t *fg,                 \
-                            struct QDict **palette) {                   \
-        uint##bpp##_t *data;                                            \
-        uint##bpp##_t c0, c1, ci;                                       \
-        int i, n0, n1;                                                  \
-                                                                        \
-        data = (uint##bpp##_t *)vs->tight.buffer;                       \
-                                                                        \
-        c0 = data[0];                                                   \
-        i = 1;                                                          \
-        while (i < count && data[i] == c0)                              \
-            i++;                                                        \
-        if (i >= count) {                                               \
-            *bg = *fg = c0;                                             \
-            return 1;                                                   \
-        }                                                               \
-                                                                        \
-        if (max < 2) {                                                  \
-            return 0;                                                   \
-        }                                                               \
-                                                                        \
-        n0 = i;                                                         \
-        c1 = data[i];                                                   \
-        n1 = 0;                                                         \
-        for (i++; i < count; i++) {                                     \
-            ci = data[i];                                               \
-            if (ci == c0) {                                             \
-                n0++;                                                   \
-            } else if (ci == c1) {                                      \
-                n1++;                                                   \
-            } else                                                      \
-                break;                                                  \
-        }                                                               \
-        if (i >= count) {                                               \
-            if (n0 > n1) {                                              \
-                *bg = (uint32_t)c0;                                     \
-                *fg = (uint32_t)c1;                                     \
-            } else {                                                    \
-                *bg = (uint32_t)c1;                                     \
-                *fg = (uint32_t)c0;                                     \
-            }                                                           \
-            return 2;                                                   \
-        }                                                               \
-                                                                        \
-        if (max == 2) {                                                 \
-            return 0;                                                   \
-        }                                                               \
-                                                                        \
-        *palette = qdict_new();                                         \
-        tight_palette_insert(*palette, c0, bpp, max);                   \
-        tight_palette_insert(*palette, c1, bpp, max);                   \
-        tight_palette_insert(*palette, ci, bpp, max);                   \
-                                                                        \
-        for (i++; i < count; i++) {                                     \
-            if (data[i] == ci) {                                        \
-                continue;                                               \
-            } else {                                                    \
-                if (!tight_palette_insert(*palette, (uint32_t)ci,       \
-                                          bpp, max)) {                  \
-                    return 0;                                           \
-                }                                                       \
-                ci = data[i];                                           \
-            }                                                           \
-        }                                                               \
-                                                                        \
-        return qdict_size(*palette);                                    \
-    }
-
-DEFINE_FILL_PALETTE_FUNCTION(8)
-DEFINE_FILL_PALETTE_FUNCTION(16)
-DEFINE_FILL_PALETTE_FUNCTION(32)
-
-static int tight_fill_palette(VncState *vs, int x, int y,
-                              size_t count, uint32_t *bg, uint32_t *fg,
-                              struct QDict **palette)
-{
-    int max;
-
-    max = count / tight_conf[vs->tight_compression].idx_max_colors_divisor;
-    if (max < 2 &&
-        count >= tight_conf[vs->tight_compression].mono_min_rect_size) {
-        max = 2;
-    }
-    if (max >= 256) {
-        max = 256;
-    }
-
-    switch(vs->clientds.pf.bytes_per_pixel) {
-    case 4:
-        return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette);
-    case 2:
-        return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette);
-    default:
-        max = 2;
-        return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette);
-    }
-    return 0;
-}
-
-/* Callback to dump a palette with qdict_iter
-static void print_palette(const char *key, QObject *obj, void *opaque)
-{
-    uint8_t idx = qint_get_int(qobject_to_qint(obj));
-    uint32_t rgb = tight_palette_buf2rgb(32, (uint8_t *)key);
-
-    fprintf(stderr, "%.2x ", (unsigned char)*key);
-    while (*key++)
-        fprintf(stderr, "%.2x ", (unsigned char)*key);
-
-    fprintf(stderr, ": idx: %x rgb: %x\n", idx, rgb);
-}
-*/
-
-/*
- * Converting truecolor samples into palette indices.
- */
-#define DEFINE_IDX_ENCODE_FUNCTION(bpp)                                 \
-                                                                        \
-    static void                                                         \
-    tight_encode_indexed_rect##bpp(uint8_t *buf, int count,             \
-                                   struct QDict *palette) {             \
-        uint##bpp##_t *src;                                             \
-        uint##bpp##_t rgb;                                              \
-        uint8_t key[6];                                                 \
-        int i, rep;                                                     \
-        uint8_t idx;                                                    \
-                                                                        \
-        src = (uint##bpp##_t *) buf;                                    \
-                                                                        \
-        for (i = 0; i < count; i++) {                                   \
-            rgb = *src++;                                               \
-            rep = 0;                                                    \
-            while (i < count && *src == rgb) {                          \
-                rep++, src++, i++;                                      \
-            }                                                           \
-            tight_palette_rgb2buf(rgb, bpp, key);                       \
-            if (!qdict_haskey(palette, (char *)key)) {                  \
-                /*                                                      \
-                 * Should never happen, but don't break everything      \
-                 * if it does, use the first color instead              \
-                 */                                                     \
-                idx = 0;                                                \
-            } else {                                                    \
-                idx = qdict_get_int(palette, (char *)key);              \
-            }                                                           \
-            while (rep >= 0) {                                          \
-                *buf++ = idx;                                           \
-                rep--;                                                  \
-            }                                                           \
-        }                                                               \
-    }
-
-DEFINE_IDX_ENCODE_FUNCTION(16)
-DEFINE_IDX_ENCODE_FUNCTION(32)
-
-#define DEFINE_MONO_ENCODE_FUNCTION(bpp)                                \
-                                                                        \
-    static void                                                         \
-    tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h,             \
-                                uint##bpp##_t bg, uint##bpp##_t fg) {   \
-        uint##bpp##_t *ptr;                                             \
-        unsigned int value, mask;                                       \
-        int aligned_width;                                              \
-        int x, y, bg_bits;                                              \
-                                                                        \
-        ptr = (uint##bpp##_t *) buf;                                    \
-        aligned_width = w - w % 8;                                      \
-                                                                        \
-        for (y = 0; y < h; y++) {                                       \
-            for (x = 0; x < aligned_width; x += 8) {                    \
-                for (bg_bits = 0; bg_bits < 8; bg_bits++) {             \
-                    if (*ptr++ != bg) {                                 \
-                        break;                                          \
-                    }                                                   \
-                }                                                       \
-                if (bg_bits == 8) {                                     \
-                    *buf++ = 0;                                         \
-                    continue;                                           \
-                }                                                       \
-                mask = 0x80 >> bg_bits;                                 \
-                value = mask;                                           \
-                for (bg_bits++; bg_bits < 8; bg_bits++) {               \
-                    mask >>= 1;                                         \
-                    if (*ptr++ != bg) {                                 \
-                        value |= mask;                                  \
-                    }                                                   \
-                }                                                       \
-                *buf++ = (uint8_t)value;                                \
-            }                                                           \
-                                                                        \
-            mask = 0x80;                                                \
-            value = 0;                                                  \
-            if (x >= w) {                                               \
-                continue;                                               \
-            }                                                           \
-                                                                        \
-            for (; x < w; x++) {                                        \
-                if (*ptr++ != bg) {                                     \
-                    value |= mask;                                      \
-                }                                                       \
-                mask >>= 1;                                             \
-            }                                                           \
-            *buf++ = (uint8_t)value;                                    \
-        }                                                               \
-    }
-
-DEFINE_MONO_ENCODE_FUNCTION(8)
-DEFINE_MONO_ENCODE_FUNCTION(16)
-DEFINE_MONO_ENCODE_FUNCTION(32)
-
-/*
- * Check if a rectangle is all of the same color. If needSameColor is
- * set to non-zero, then also check that its color equals to the
- * *colorPtr value. The result is 1 if the test is successfull, and in
- * that case new color will be stored in *colorPtr.
- */
-
-#define DEFINE_CHECK_SOLID_FUNCTION(bpp)                                \
-                                                                        \
-    static bool                                                         \
-    check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h,     \
-                          uint32_t* color, bool samecolor)              \
-    {                                                                   \
-        VncDisplay *vd = vs->vd;                                        \
-        uint##bpp##_t *fbptr;                                           \
-        uint##bpp##_t c;                                                \
-        int dx, dy;                                                     \
-                                                                        \
-        fbptr = (uint##bpp##_t *)                                       \
-            (vd->server->data + y * ds_get_linesize(vs->ds) +           \
-             x * ds_get_bytes_per_pixel(vs->ds));                       \
-                                                                        \
-        c = *fbptr;                                                     \
-        if (samecolor && (uint32_t)c != *color) {                       \
-            return false;                                               \
-        }                                                               \
-                                                                        \
-        for (dy = 0; dy < h; dy++) {                                    \
-            for (dx = 0; dx < w; dx++) {                                \
-                if (c != fbptr[dx]) {                                   \
-                    return false;                                       \
-                }                                                       \
-            }                                                           \
-            fbptr = (uint##bpp##_t *)                                   \
-                ((uint8_t *)fbptr + ds_get_linesize(vs->ds));           \
-        }                                                               \
-                                                                        \
-        *color = (uint32_t)c;                                           \
-        return true;                                                    \
-    }
-
-DEFINE_CHECK_SOLID_FUNCTION(32)
-DEFINE_CHECK_SOLID_FUNCTION(16)
-DEFINE_CHECK_SOLID_FUNCTION(8)
-
-static bool check_solid_tile(VncState *vs, int x, int y, int w, int h,
-                             uint32_t* color, bool samecolor)
-{
-    VncDisplay *vd = vs->vd;
-
-    switch(vd->server->pf.bytes_per_pixel) {
-    case 4:
-        return check_solid_tile32(vs, x, y, w, h, color, samecolor);
-    case 2:
-        return check_solid_tile16(vs, x, y, w, h, color, samecolor);
-    default:
-        return check_solid_tile8(vs, x, y, w, h, color, samecolor);
-    }
-}
-
-static void find_best_solid_area(VncState *vs, int x, int y, int w, int h,
-                                 uint32_t color, int *w_ptr, int *h_ptr)
-{
-    int dx, dy, dw, dh;
-    int w_prev;
-    int w_best = 0, h_best = 0;
-
-    w_prev = w;
-
-    for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
-
-        dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy);
-        dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev);
-
-        if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) {
-            break;
-        }
-
-        for (dx = x + dw; dx < x + w_prev;) {
-            dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx);
-
-            if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) {
-                break;
-            }
-            dx += dw;
-        }
-
-        w_prev = dx - x;
-        if (w_prev * (dy + dh - y) > w_best * h_best) {
-            w_best = w_prev;
-            h_best = dy + dh - y;
-        }
-    }
-
-    *w_ptr = w_best;
-    *h_ptr = h_best;
-}
-
-static void extend_solid_area(VncState *vs, int x, int y, int w, int h,
-                              uint32_t color, int *x_ptr, int *y_ptr,
-                              int *w_ptr, int *h_ptr)
-{
-    int cx, cy;
-
-    /* Try to extend the area upwards. */
-    for ( cy = *y_ptr - 1;
-          cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
-          cy-- );
-    *h_ptr += *y_ptr - (cy + 1);
-    *y_ptr = cy + 1;
-
-    /* ... downwards. */
-    for ( cy = *y_ptr + *h_ptr;
-          cy < y + h &&
-              check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
-          cy++ );
-    *h_ptr += cy - (*y_ptr + *h_ptr);
-
-    /* ... to the left. */
-    for ( cx = *x_ptr - 1;
-          cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
-          cx-- );
-    *w_ptr += *x_ptr - (cx + 1);
-    *x_ptr = cx + 1;
-
-    /* ... to the right. */
-    for ( cx = *x_ptr + *w_ptr;
-          cx < x + w &&
-              check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
-          cx++ );
-    *w_ptr += cx - (*x_ptr + *w_ptr);
-}
-
-static int tight_init_stream(VncState *vs, int stream_id,
-                             int level, int strategy)
-{
-    z_streamp zstream = &vs->tight_stream[stream_id];
-
-    if (zstream->opaque == NULL) {
-        int err;
-
-        VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id);
-        VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs);
-        zstream->zalloc = vnc_zlib_zalloc;
-        zstream->zfree = vnc_zlib_zfree;
-
-        err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
-                           MAX_MEM_LEVEL, strategy);
-
-        if (err != Z_OK) {
-            fprintf(stderr, "VNC: error initializing zlib\n");
-            return -1;
-        }
-
-        vs->tight_levels[stream_id] = level;
-        zstream->opaque = vs;
-    }
-
-    if (vs->tight_levels[stream_id] != level) {
-        if (deflateParams(zstream, level, strategy) != Z_OK) {
-            return -1;
-        }
-        vs->tight_levels[stream_id] = level;
-    }
-    return 0;
-}
-
-static void tight_send_compact_size(VncState *vs, size_t len)
-{
-    int lpc = 0;
-    int bytes = 0;
-    char buf[3] = {0, 0, 0};
-
-    buf[bytes++] = len & 0x7F;
-    if (len > 0x7F) {
-        buf[bytes-1] |= 0x80;
-        buf[bytes++] = (len >> 7) & 0x7F;
-        if (len > 0x3FFF) {
-            buf[bytes-1] |= 0x80;
-            buf[bytes++] = (len >> 14) & 0xFF;
-        }
-    }
-    for (lpc = 0; lpc < bytes; lpc++) {
-        vnc_write_u8(vs, buf[lpc]);
-    }
-}
-
-static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
-                               int level, int strategy)
-{
-    z_streamp zstream = &vs->tight_stream[stream_id];
-    int previous_out;
-
-    if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) {
-        vnc_write(vs, vs->tight.buffer, vs->tight.offset);
-        return bytes;
-    }
-
-    if (tight_init_stream(vs, stream_id, level, strategy)) {
-        return -1;
-    }
-
-    /* reserve memory in output buffer */
-    buffer_reserve(&vs->tight_zlib, bytes + 64);
-
-    /* set pointers */
-    zstream->next_in = vs->tight.buffer;
-    zstream->avail_in = vs->tight.offset;
-    zstream->next_out = vs->tight_zlib.buffer + vs->tight_zlib.offset;
-    zstream->avail_out = vs->tight_zlib.capacity - vs->tight_zlib.offset;
-    zstream->data_type = Z_BINARY;
-    previous_out = zstream->total_out;
-
-    /* start encoding */
-    if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
-        fprintf(stderr, "VNC: error during tight compression\n");
-        return -1;
-    }
-
-    vs->tight_zlib.offset = vs->tight_zlib.capacity - zstream->avail_out;
-    bytes = zstream->total_out - previous_out;
-
-    tight_send_compact_size(vs, bytes);
-    vnc_write(vs, vs->tight_zlib.buffer, bytes);
-
-    buffer_reset(&vs->tight_zlib);
-
-    return bytes;
-}
-
-/*
- * Subencoding implementations.
- */
-static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
-{
-    uint32_t *buf32;
-    uint32_t pix;
-    int rshift, gshift, bshift;
-
-    buf32 = (uint32_t *)buf;
-
-    if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
-        (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
-        rshift = vs->clientds.pf.rshift;
-        gshift = vs->clientds.pf.gshift;
-        bshift = vs->clientds.pf.bshift;
-    } else {
-        rshift = 24 - vs->clientds.pf.rshift;
-        gshift = 24 - vs->clientds.pf.gshift;
-        bshift = 24 - vs->clientds.pf.bshift;
-    }
-
-    if (ret) {
-        *ret = count * 3;
-    }
-
-    while (count--) {
-        pix = *buf32++;
-        *buf++ = (char)(pix >> rshift);
-        *buf++ = (char)(pix >> gshift);
-        *buf++ = (char)(pix >> bshift);
-    }
-}
-
-static int send_full_color_rect(VncState *vs, int w, int h)
-{
-    int stream = 0;
-    size_t bytes;
-
-    vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
-
-    if (vs->tight_pixel24) {
-        tight_pack24(vs, vs->tight.buffer, w * h, &vs->tight.offset);
-        bytes = 3;
-    } else {
-        bytes = vs->clientds.pf.bytes_per_pixel;
-    }
-
-    bytes = tight_compress_data(vs, stream, w * h * bytes,
-                                tight_conf[vs->tight_compression].raw_zlib_level,
-                                Z_DEFAULT_STRATEGY);
-
-    return (bytes >= 0);
-}
-
-static int send_solid_rect(VncState *vs)
-{
-    size_t bytes;
-
-    vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */
-
-    if (vs->tight_pixel24) {
-        tight_pack24(vs, vs->tight.buffer, 1, &vs->tight.offset);
-        bytes = 3;
-    } else {
-        bytes = vs->clientds.pf.bytes_per_pixel;
-    }
-
-    vnc_write(vs, vs->tight.buffer, bytes);
-    return 1;
-}
-
-static int send_mono_rect(VncState *vs, int w, int h, uint32_t bg, uint32_t fg)
-{
-    size_t bytes;
-    int stream = 1;
-    int level = tight_conf[vs->tight_compression].mono_zlib_level;
-
-    bytes = ((w + 7) / 8) * h;
-
-    vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
-    vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
-    vnc_write_u8(vs, 1);
-
-    switch(vs->clientds.pf.bytes_per_pixel) {
-    case 4:
-    {
-        uint32_t buf[2] = {bg, fg};
-        size_t ret = sizeof (buf);
-
-        if (vs->tight_pixel24) {
-            tight_pack24(vs, (unsigned char*)buf, 2, &ret);
-        }
-        vnc_write(vs, buf, ret);
-
-        tight_encode_mono_rect32(vs->tight.buffer, w, h, bg, fg);
-        break;
-    }
-    case 2:
-        vnc_write(vs, &bg, 2);
-        vnc_write(vs, &fg, 2);
-        tight_encode_mono_rect16(vs->tight.buffer, w, h, bg, fg);
-        break;
-    default:
-        vnc_write_u8(vs, bg);
-        vnc_write_u8(vs, fg);
-        tight_encode_mono_rect8(vs->tight.buffer, w, h, bg, fg);
-        break;
-    }
-    vs->tight.offset = bytes;
-
-    bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);
-    return (bytes >= 0);
-}
-
-struct palette_cb_priv {
-    VncState *vs;
-    uint8_t *header;
-};
-
-static void write_palette(const char *key, QObject *obj, void *opaque)
-{
-    struct palette_cb_priv *priv = opaque;
-    VncState *vs = priv->vs;
-    uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
-    uint8_t idx = qint_get_int(qobject_to_qint(obj));
-
-    if (bytes == 4) {
-        uint32_t color = tight_palette_buf2rgb(32, (uint8_t *)key);
-
-        ((uint32_t*)priv->header)[idx] = color;
-    } else {
-        uint16_t color = tight_palette_buf2rgb(16, (uint8_t *)key);
-
-        ((uint16_t*)priv->header)[idx] = color;
-    }
-}
-
-static int send_palette_rect(VncState *vs, int w, int h, struct QDict *palette)
-{
-    int stream = 2;
-    int level = tight_conf[vs->tight_compression].idx_zlib_level;
-    int colors;
-    size_t bytes;
-
-    colors = qdict_size(palette);
-
-    vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
-    vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
-    vnc_write_u8(vs, colors - 1);
-
-    switch(vs->clientds.pf.bytes_per_pixel) {
-    case 4:
-    {
-        size_t old_offset, offset;
-        uint32_t header[qdict_size(palette)];
-        struct palette_cb_priv priv = { vs, (uint8_t *)header };
-
-        old_offset = vs->output.offset;
-        qdict_iter(palette, write_palette, &priv);
-        vnc_write(vs, header, sizeof(header));
-
-        if (vs->tight_pixel24) {
-            tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
-            vs->output.offset = old_offset + offset;
-        }
-
-        tight_encode_indexed_rect32(vs->tight.buffer, w * h, palette);
-        break;
-    }
-    case 2:
-    {
-        uint16_t header[qdict_size(palette)];
-        struct palette_cb_priv priv = { vs, (uint8_t *)header };
-
-        qdict_iter(palette, write_palette, &priv);
-        vnc_write(vs, header, sizeof(header));
-        tight_encode_indexed_rect16(vs->tight.buffer, w * h, palette);
-        break;
-    }
-    default:
-        return -1; /* No palette for 8bits colors */
-        break;
-    }
-    bytes = w * h;
-    vs->tight.offset = bytes;
-
-    bytes = tight_compress_data(vs, stream, bytes,
-                                level, Z_DEFAULT_STRATEGY);
-    return (bytes >= 0);
-}
-
-static void vnc_tight_start(VncState *vs)
-{
-    buffer_reset(&vs->tight);
-
-    // make the output buffer be the zlib buffer, so we can compress it later
-    vs->tight_tmp = vs->output;
-    vs->output = vs->tight;
-}
-
-static void vnc_tight_stop(VncState *vs)
-{
-    // switch back to normal output/zlib buffers
-    vs->tight = vs->output;
-    vs->output = vs->tight_tmp;
-}
-
-static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
-{
-    struct QDict *palette = NULL;
-    uint32_t bg = 0, fg = 0;
-    int colors;
-    int ret = 0;
-
-    vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
-
-    vnc_tight_start(vs);
-    vnc_raw_send_framebuffer_update(vs, x, y, w, h);
-    vnc_tight_stop(vs);
-
-    colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
-
-    if (colors == 0) {
-        ret = send_full_color_rect(vs, w, h);
-    } else if (colors == 1) {
-        ret = send_solid_rect(vs);
-    } else if (colors == 2) {
-        ret = send_mono_rect(vs, w, h, bg, fg);
-    } else if (colors <= 256) {
-        ret = send_palette_rect(vs, w, h, palette);
-    }
-    QDECREF(palette);
-    return ret;
-}
-
-static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
-{
-    vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
-
-    vnc_tight_start(vs);
-    vnc_raw_send_framebuffer_update(vs, x, y, w, h);
-    vnc_tight_stop(vs);
-
-    return send_solid_rect(vs);
-}
-
-static int send_rect_simple(VncState *vs, int x, int y, int w, int h)
-{
-    int max_size, max_width;
-    int max_sub_width, max_sub_height;
-    int dx, dy;
-    int rw, rh;
-    int n = 0;
-
-    max_size = tight_conf[vs->tight_compression].max_rect_size;
-    max_width = tight_conf[vs->tight_compression].max_rect_width;
-
-    if (w > max_width || w * h > max_size) {
-        max_sub_width = (w > max_width) ? max_width : w;
-        max_sub_height = max_size / max_sub_width;
-
-        for (dy = 0; dy < h; dy += max_sub_height) {
-            for (dx = 0; dx < w; dx += max_width) {
-                rw = MIN(max_sub_width, w - dx);
-                rh = MIN(max_sub_height, h - dy);
-                n += send_sub_rect(vs, x+dx, y+dy, rw, rh);
-            }
-        }
-    } else {
-        n += send_sub_rect(vs, x, y, w, h);
-    }
-
-    return n;
-}
-
-static int find_large_solid_color_rect(VncState *vs, int x, int y,
-                                       int w, int h, int max_rows)
-{
-    int dx, dy, dw, dh;
-    int n = 0;
-
-    /* Try to find large solid-color areas and send them separately. */
-
-    for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
-
-        /* If a rectangle becomes too large, send its upper part now. */
-
-        if (dy - y >= max_rows) {
-            n += send_rect_simple(vs, x, y, w, max_rows);
-            y += max_rows;
-            h -= max_rows;
-        }
-
-        dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy));
-
-        for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
-            uint32_t color_value;
-            int x_best, y_best, w_best, h_best;
-
-            dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx));
-
-            if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) {
-                continue ;
-            }
-
-            /* Get dimensions of solid-color area. */
-
-            find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y),
-                                 color_value, &w_best, &h_best);
-
-            /* Make sure a solid rectangle is large enough
-               (or the whole rectangle is of the same color). */
-
-            if (w_best * h_best != w * h &&
-                w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) {
-                continue;
-            }
-
-            /* Try to extend solid rectangle to maximum size. */
-
-            x_best = dx; y_best = dy;
-            extend_solid_area(vs, x, y, w, h, color_value,
-                              &x_best, &y_best, &w_best, &h_best);
-
-            /* Send rectangles at top and left to solid-color area. */
-
-            if (y_best != y) {
-                n += send_rect_simple(vs, x, y, w, y_best-y);
-            }
-            if (x_best != x) {
-                n += vnc_tight_send_framebuffer_update(vs, x, y_best,
-                                                       x_best-x, h_best);
-            }
-
-            /* Send solid-color rectangle. */
-            n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best);
-
-            /* Send remaining rectangles (at right and bottom). */
-
-            if (x_best + w_best != x + w) {
-                n += vnc_tight_send_framebuffer_update(vs, x_best+w_best,
-                                                       y_best,
-                                                       w-(x_best-x)-w_best,
-                                                       h_best);
-            }
-            if (y_best + h_best != y + h) {
-                n += vnc_tight_send_framebuffer_update(vs, x, y_best+h_best,
-                                                       w, h-(y_best-y)-h_best);
-            }
-
-            /* Return after all recursive calls are done. */
-            return n;
-        }
-    }
-    return n + send_rect_simple(vs, x, y, w, h);
-}
-
-int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
-                                      int w, int h)
-{
-    int max_rows;
-
-    if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF &&
-        vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) {
-        vs->tight_pixel24 = true;
-    } else {
-        vs->tight_pixel24 = false;
-    }
-
-    if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE)
-        return send_rect_simple(vs, x, y, w, h);
-
-    /* Calculate maximum number of rows in one non-solid rectangle. */
-
-    max_rows = tight_conf[vs->tight_compression].max_rect_size;
-    max_rows /= MIN(tight_conf[vs->tight_compression].max_rect_width, w);
-
-    return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
-}
-
-void vnc_tight_clear(VncState *vs)
-{
-    int i;
-    for (i=0; i<ARRAY_SIZE(vs->tight_stream); i++) {
-        if (vs->tight_stream[i].opaque) {
-            deflateEnd(&vs->tight_stream[i]);
-        }
-    }
-
-    buffer_free(&vs->tight);
-    buffer_free(&vs->tight_zlib);
-}