diff options
| -rw-r--r-- | Makefile | 38 | ||||
| -rw-r--r-- | Makefile.objs | 29 | ||||
| -rw-r--r-- | Makefile.target | 2 | ||||
| -rw-r--r-- | VERSION | 2 | ||||
| -rwxr-xr-x | configure | 85 | ||||
| -rw-r--r-- | cpu-all.h | 2 | ||||
| -rw-r--r-- | docs/migration.txt | 303 | ||||
| -rw-r--r-- | elf.h | 44 | ||||
| -rw-r--r-- | hw/e1000.c | 12 | ||||
| -rw-r--r-- | hw/hw.h | 6 | ||||
| -rw-r--r-- | hw/ide/core.c | 71 | ||||
| -rw-r--r-- | hw/ide/pci.c | 38 | ||||
| -rw-r--r-- | hw/vhost.c | 21 | ||||
| -rw-r--r-- | linux-user/elfload.c | 1799 | ||||
| -rw-r--r-- | linux-user/linuxload.c | 17 | ||||
| -rw-r--r-- | linux-user/mmap.c | 14 | ||||
| -rw-r--r-- | linux-user/qemu.h | 7 | ||||
| -rw-r--r-- | qemu-options.hx | 7 | ||||
| -rw-r--r-- | qemu-thread.c | 22 | ||||
| -rw-r--r-- | qemu-thread.h | 4 | ||||
| -rw-r--r-- | savevm.c | 86 | ||||
| -rw-r--r-- | tests/cris/check_addo.c | 4 | ||||
| -rw-r--r-- | tests/cris/check_addoq.c | 2 | ||||
| -rw-r--r-- | tests/cris/check_settls1.c | 10 | ||||
| -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.c | 1717 | ||||
| -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.c | 331 | ||||
| -rw-r--r-- | ui/vnc-jobs-sync.c | 73 | ||||
| -rw-r--r-- | ui/vnc-jobs.h | 87 | ||||
| -rw-r--r-- | ui/vnc-palette.c | 136 | ||||
| -rw-r--r-- | ui/vnc-palette.h | 63 | ||||
| -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.c | 959 |
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); -} |