diff options
author | Avi Kivity <avi@redhat.com> | 2010-08-01 17:29:21 +0300 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2010-08-01 17:29:21 +0300 |
commit | ab8c54b5a08ed8ad3d7bb930c306785b471b2d7e (patch) | |
tree | 7adefa1a3150575571da77f6e382aa8a2a6bdd3b | |
parent | f229c5df0796c02fc39279db24b114cc04757c4a (diff) | |
parent | fd2f659ee3f6f991f4f194f3fde5c9f957fd663d (diff) |
Merge commit 'fd2f659ee3f6f991f4f194f3fde5c9f957fd663d' into upstream-merge
* commit 'fd2f659ee3f6f991f4f194f3fde5c9f957fd663d': (58 commits)
Update version for 0.13.0-rc0
vnc: better default values for VNC options
vnc: tight: split send_sub_rect
vnc: tight: fix rgb_prepare_row
vnc: add missing lock for vnc_cursor_define()
vnc: threaded VNC server
qemu-thread: add qemu_mutex/cond_destroy and qemu_mutex_exit
vnc: fix tight png memory leak
vnc: encapsulate encoding members
vnc: tight: stop using qdict for palette stuff
vnc: tight: specific zlib level and filters for each compression level
vnc: tight add PNG encoding
vnc: tight: remove a memleak in send_jpeg_rect()
vnc: tight: don't forget do at the last color
vnc: rename vnc-encoding-* vnc-enc-*
ui: move all ui components in ui/
vnc: add lossy option
vnc: JPEG should be disabled if the client don't set tight quality
vnc: tight: add JPEG and gradient subencoding with smooth image detection
Initial documentation for migration
...
Merge 0.13 branch point.
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 38 | ||||
-rw-r--r-- | Makefile.objs | 29 | ||||
-rw-r--r-- | Makefile.target | 2 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | block/raw-posix.c | 2 | ||||
-rwxr-xr-x | configure | 85 | ||||
-rw-r--r-- | cpu-exec.c | 15 | ||||
-rw-r--r-- | cpus.c | 58 | ||||
-rw-r--r-- | cpus.h | 2 | ||||
-rw-r--r-- | docs/migration.txt | 303 | ||||
-rw-r--r-- | exec-all.h | 5 | ||||
-rw-r--r-- | exec.c | 4 | ||||
-rw-r--r-- | hw/e1000.c | 29 | ||||
-rw-r--r-- | hw/etraxfs_eth.c | 1 | ||||
-rw-r--r-- | hw/hw.h | 6 | ||||
-rw-r--r-- | hw/ide/core.c | 72 | ||||
-rw-r--r-- | hw/ide/pci.c | 38 | ||||
-rw-r--r-- | hw/mips_int.c | 32 | ||||
-rw-r--r-- | hw/pc_piix.c | 6 | ||||
-rw-r--r-- | hw/ppc440_bamboo.c | 2 | ||||
-rw-r--r-- | hw/scsi-bus.c | 12 | ||||
-rw-r--r-- | hw/scsi.h | 1 | ||||
-rw-r--r-- | hw/vhost.c | 21 | ||||
-rw-r--r-- | kvm-all.c | 2 | ||||
-rw-r--r-- | linux-user/main.c | 2 | ||||
-rw-r--r-- | linux-user/signal.c | 26 | ||||
-rw-r--r-- | migration-tcp.c | 5 | ||||
-rw-r--r-- | qemu-barrier.h | 3 | ||||
-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-- | slirp/cksum.c | 20 | ||||
-rw-r--r-- | slirp/ip.h | 40 | ||||
-rw-r--r-- | slirp/ip_icmp.h | 2 | ||||
-rw-r--r-- | slirp/ip_input.c | 2 | ||||
-rw-r--r-- | slirp/ip_output.c | 22 | ||||
-rw-r--r-- | slirp/main.h | 2 | ||||
-rw-r--r-- | slirp/misc.h | 20 | ||||
-rw-r--r-- | slirp/slirp.h | 39 | ||||
-rw-r--r-- | slirp/slirp_config.h | 6 | ||||
-rw-r--r-- | slirp/socket.c | 2 | ||||
-rw-r--r-- | slirp/socket.h | 10 | ||||
-rw-r--r-- | slirp/tcp.h | 14 | ||||
-rw-r--r-- | slirp/tcp_input.c | 4 | ||||
-rw-r--r-- | slirp/tcp_output.c | 10 | ||||
-rw-r--r-- | slirp/tcp_subr.c | 12 | ||||
-rw-r--r-- | slirp/tcp_var.h | 14 | ||||
-rw-r--r-- | slirp/tftp.c | 8 | ||||
-rw-r--r-- | slirp/tftp.h | 14 | ||||
-rw-r--r-- | slirp/udp.c | 8 | ||||
-rw-r--r-- | slirp/udp.h | 10 | ||||
-rw-r--r-- | target-i386/translate.c | 60 | ||||
-rw-r--r-- | target-microblaze/translate.c | 79 | ||||
-rw-r--r-- | target-mips/cpu.h | 2 | ||||
-rw-r--r-- | target-mips/helper.h | 1 | ||||
-rw-r--r-- | target-mips/op_helper.c | 27 | ||||
-rw-r--r-- | target-mips/translate.c | 12 | ||||
-rw-r--r-- | tests/sha1.c | 24 | ||||
-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 | 1715 | ||||
-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-- | vl.c | 2 | ||||
-rw-r--r-- | vnc-encoding-tight.c | 959 |
95 files changed, 3630 insertions, 1486 deletions
diff --git a/.gitignore b/.gitignore index 26eba2050..c6466df6e 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ pc-bios/optionrom/multiboot.raw pc-bios/optionrom/extboot.bin pc-bios/optionrom/vapic.bin .stgit-* +cscope.* @@ -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 \ @@ -353,4 +325,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 cc067063b..dbee21020 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -103,17 +103,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 2c9244863..9e13d99cb 100644 --- a/Makefile.target +++ b/Makefile.target @@ -192,6 +192,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 @@ -1 +1 @@ -0.12.50 +0.12.90 diff --git a/block/raw-posix.c b/block/raw-posix.c index 3a3cfc050..b72a08256 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -218,7 +218,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) } #endif #ifdef CONFIG_COCOA - u_int32_t blockSize = 512; + uint32_t blockSize = 512; if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) { bufsize = blockSize; } @@ -278,6 +278,9 @@ uuid="" vde="" vnc_tls="" vnc_sasl="" +vnc_jpeg="" +vnc_png="" +vnc_thread="no" xen="" linux_aio="" attr="" @@ -595,6 +598,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" @@ -855,6 +870,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" @@ -1281,6 +1302,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 @@ -2230,6 +2297,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 @@ -2370,6 +2440,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 @@ -2446,6 +2528,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 @@ -2987,7 +3070,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-exec.c b/cpu-exec.c index 240bc287d..0a030da7d 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -23,6 +23,7 @@ #include "tcg.h" #endif #include "kvm.h" +#include "qemu-barrier.h" #if !defined(CONFIG_SOFTMMU) #undef EAX @@ -237,12 +238,11 @@ int cpu_exec(CPUState *env1) use it. */ QEMU_BUILD_BUG_ON (sizeof (saved_env_reg) != sizeof (env)); saved_env_reg = (host_reg_t) env; - asm(""); + barrier(); env = env1; - if (exit_request) { + if (unlikely(exit_request)) { env->exit_request = 1; - exit_request = 0; } #if defined(TARGET_I386) @@ -606,8 +606,9 @@ int cpu_exec(CPUState *env1) TB, but before it is linked into a potentially infinite loop and becomes env->current_tb. Avoid starting execution if there is a pending interrupt. */ - if (!unlikely (env->exit_request)) { - env->current_tb = tb; + env->current_tb = tb; + barrier(); + if (likely(!env->exit_request)) { tc_ptr = tb->tc_ptr; /* execute the generated code */ #if defined(__sparc__) && !defined(CONFIG_SOLARIS) @@ -616,7 +617,6 @@ int cpu_exec(CPUState *env1) #define env cpu_single_env #endif next_tb = tcg_qemu_tb_exec(tc_ptr); - env->current_tb = NULL; if ((next_tb & 3) == 2) { /* Instruction counter expired. */ int insns_left; @@ -645,6 +645,7 @@ int cpu_exec(CPUState *env1) } } } + env->current_tb = NULL; /* reset soft MMU for next block (it can currently only be set by a memory fault) */ } /* for(;;) */ @@ -677,7 +678,7 @@ int cpu_exec(CPUState *env1) #endif /* restore global registers */ - asm(""); + barrier(); env = (void *) saved_env_reg; /* fail safe : never use cpu_single_env outside cpu_exec() */ @@ -40,7 +40,6 @@ #define SIG_IPI SIGUSR1 #endif -static CPUState *cur_cpu; static CPUState *next_cpu; /***********************************************************/ @@ -132,7 +131,7 @@ static int cpu_has_work(CPUState *env) return 0; } -static int tcg_has_work(void) +static int any_cpu_has_work(void) { CPUState *env; @@ -142,6 +141,13 @@ static int tcg_has_work(void) return 0; } +static void cpu_debug_handler(CPUState *env) +{ + gdb_set_stop_cpu(env); + debug_requested = EXCP_DEBUG; + vm_stop(EXCP_DEBUG); +} + #ifndef _WIN32 static int io_thread_fd = -1; @@ -237,6 +243,8 @@ static void qemu_event_increment(void) #ifndef CONFIG_IOTHREAD int qemu_init_main_loop(void) { + cpu_set_debug_excp_handler(cpu_debug_handler); + return qemu_event_init(); } @@ -334,11 +342,14 @@ int qemu_init_main_loop(void) { int ret; + cpu_set_debug_excp_handler(cpu_debug_handler); + ret = qemu_event_init(); if (ret) return ret; qemu_cond_init(&qemu_pause_cond); + qemu_cond_init(&qemu_system_cond); qemu_mutex_init(&qemu_fair_mutex); qemu_mutex_init(&qemu_global_mutex); qemu_mutex_lock(&qemu_global_mutex); @@ -409,10 +420,12 @@ static void qemu_wait_io_event_common(CPUState *env) flush_queued_work(env); } -static void qemu_wait_io_event(CPUState *env) +static void qemu_tcg_wait_io_event(void) { - while (!tcg_has_work()) - qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); + CPUState *env; + + while (!any_cpu_has_work()) + qemu_cond_timedwait(tcg_halt_cond, &qemu_global_mutex, 1000); qemu_mutex_unlock(&qemu_global_mutex); @@ -425,7 +438,10 @@ static void qemu_wait_io_event(CPUState *env) qemu_mutex_unlock(&qemu_fair_mutex); qemu_mutex_lock(&qemu_global_mutex); - qemu_wait_io_event_common(env); + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + qemu_wait_io_event_common(env); + } } static void qemu_kvm_eat_signal(CPUState *env, int timeout) @@ -509,8 +525,8 @@ static void *tcg_cpu_thread_fn(void *arg) qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); while (1) { - tcg_cpu_exec(); - qemu_wait_io_event(cur_cpu); + cpu_exec_all(); + qemu_tcg_wait_io_event(); } return NULL; @@ -770,32 +786,28 @@ static int qemu_cpu_exec(CPUState *env) return ret; } -bool tcg_cpu_exec(void) +bool cpu_exec_all(void) { - int ret = 0; - if (next_cpu == NULL) next_cpu = first_cpu; - for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) { - CPUState *env = cur_cpu = next_cpu; + for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) { + CPUState *env = next_cpu; qemu_clock_enable(vm_clock, - (cur_cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); + (env->singlestep_enabled & SSTEP_NOTIMER) == 0); if (qemu_alarm_pending()) break; - if (cpu_can_run(env)) - ret = qemu_cpu_exec(env); - else if (env->stop) - break; - - if (ret == EXCP_DEBUG) { - gdb_set_stop_cpu(env); - debug_requested = EXCP_DEBUG; + if (cpu_can_run(env)) { + if (qemu_cpu_exec(env) == EXCP_DEBUG) { + break; + } + } else if (env->stop) { break; } } - return tcg_has_work(); + exit_request = 0; + return any_cpu_has_work(); } void set_numa_modes(void) @@ -13,7 +13,7 @@ extern int smp_threads; extern int debug_requested; extern int vmstop_requested; void vm_state_notify(int running, int reason); -bool tcg_cpu_exec(void); +bool cpu_exec_all(void); void set_numa_modes(void); void set_cpu_log(const char *optarg); void list_cpus(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...), diff --git a/docs/migration.txt b/docs/migration.txt new file mode 100644 index 000000000..69d53837c --- /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/exec-all.h b/exec-all.h index a775582be..3a53fe64c 100644 --- a/exec-all.h +++ b/exec-all.h @@ -86,9 +86,6 @@ int cpu_gen_code(CPUState *env, struct TranslationBlock *tb, int cpu_restore_state(struct TranslationBlock *tb, CPUState *env, unsigned long searched_pc, void *puc); -int cpu_restore_state_copy(struct TranslationBlock *tb, - CPUState *env, unsigned long searched_pc, - void *puc); void cpu_resume_from_signal(CPUState *env1, void *puc); void cpu_io_recompile(CPUState *env, void *retaddr); TranslationBlock *tb_gen_code(CPUState *env, @@ -191,8 +188,6 @@ void tb_link_page(TranslationBlock *tb, void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; -extern uint8_t *code_gen_ptr; -extern int code_gen_max_blocks; #if defined(USE_DIRECT_JUMP) @@ -86,7 +86,7 @@ #define SMC_BITMAP_USE_THRESHOLD 10 static TranslationBlock *tbs; -int code_gen_max_blocks; +static int code_gen_max_blocks; TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; static int nb_tbs; /* any access to the tbs or the page table must use this lock */ @@ -113,7 +113,7 @@ static uint8_t *code_gen_buffer; static unsigned long code_gen_buffer_size; /* threshold to flush the translated code buffer */ static unsigned long code_gen_buffer_max_size; -uint8_t *code_gen_ptr; +static uint8_t *code_gen_ptr; #if !defined(CONFIG_USER_ONLY) int phys_ram_fd; diff --git a/hw/e1000.c b/hw/e1000.c index 0da65f9a3..80b78bc61 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -262,21 +262,20 @@ set_eecd(E1000State *s, int index, uint32_t val) s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS | E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ); + if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do + return; + if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state + s->eecd_state.val_in = 0; + s->eecd_state.bitnum_in = 0; + s->eecd_state.bitnum_out = 0; + s->eecd_state.reading = 0; + } if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge return; if (!(E1000_EECD_SK & val)) { // falling edge s->eecd_state.bitnum_out++; return; } - if (!(val & E1000_EECD_CS)) { // rising, no CS (EEPROM reset) - memset(&s->eecd_state, 0, sizeof s->eecd_state); - /* - * restore old_eecd's E1000_EECD_SK (known to be on) - * to avoid false detection of a clock edge - */ - s->eecd_state.old_eecd = E1000_EECD_SK; - return; - } s->eecd_state.val_in <<= 1; if (val & E1000_EECD_DI) s->eecd_state.val_in |= 1; @@ -344,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) { @@ -649,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); @@ -663,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/etraxfs_eth.c b/hw/etraxfs_eth.c index 187ece19e..b897c9c16 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -437,6 +437,7 @@ eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value) eth_validate_duplex(eth); } eth->mdio_bus.mdc = !!(value & 4); + eth->regs[addr] = value; break; case RW_REC_CTRL: @@ -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 e20f2e7cb..db000831e 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2733,6 +2733,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) @@ -2756,26 +2757,29 @@ 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 < 0 || + 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; @@ -2789,12 +2793,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), @@ -2817,15 +2847,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 4d95cc5e2..4331d7723 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/mips_int.c b/hw/mips_int.c index c30954caa..477f6abf9 100644 --- a/hw/mips_int.c +++ b/hw/mips_int.c @@ -24,22 +24,6 @@ #include "mips_cpudevs.h" #include "cpu.h" -/* Raise IRQ to CPU if necessary. It must be called every time the active - IRQ may change */ -void cpu_mips_update_irq(CPUState *env) -{ - if ((env->CP0_Status & (1 << CP0St_IE)) && - !(env->CP0_Status & (1 << CP0St_EXL)) && - !(env->CP0_Status & (1 << CP0St_ERL)) && - !(env->hflags & MIPS_HFLAG_DM)) { - if ((env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) && - !(env->interrupt_request & CPU_INTERRUPT_HARD)) { - cpu_interrupt(env, CPU_INTERRUPT_HARD); - } - } else - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); -} - static void cpu_mips_irq_request(void *opaque, int irq, int level) { CPUState *env = (CPUState *)opaque; @@ -52,7 +36,12 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) } else { env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP)); } - cpu_mips_update_irq(env); + + if (env->CP0_Cause & CP0Ca_IP_mask) { + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } } void cpu_mips_irq_init_cpu(CPUState *env) @@ -65,3 +54,12 @@ void cpu_mips_irq_init_cpu(CPUState *env) env->irq[i] = qi[i]; } } + +void cpu_mips_soft_irq(CPUState *env, int irq, int level) +{ + if (irq < 0 || irq > 2) { + return; + } + + qemu_set_irq(env->irq[irq], level); +} diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 3a1c67023..9e4bac867 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -245,7 +245,7 @@ static QEMUMachine pc_machine_v0_12 = { .compat_props = (GlobalProperty[]) { { .driver = "virtio-serial-pci", - .property = "max_nr_ports", + .property = "max_ports", .value = stringify(1), },{ .driver = "virtio-serial-pci", @@ -268,7 +268,7 @@ static QEMUMachine pc_machine_v0_11 = { .value = stringify(0), },{ .driver = "virtio-serial-pci", - .property = "max_nr_ports", + .property = "max_ports", .value = stringify(1), },{ .driver = "virtio-serial-pci", @@ -307,7 +307,7 @@ static QEMUMachine pc_machine_v0_10 = { .value = stringify(PCI_CLASS_DISPLAY_OTHER), },{ .driver = "virtio-serial-pci", - .property = "max_nr_ports", + .property = "max_ports", .value = stringify(1), },{ .driver = "virtio-serial-pci", diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c index 79eb0b357..74cc56a34 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -187,7 +187,7 @@ static QEMUMachine bamboo_machine_v0_12 = { .compat_props = (GlobalProperty[]) { { .driver = "virtio-serial-pci", - .property = "max_nr_ports", + .property = "max_ports", .value = stringify(1), },{ .driver = "virtio-serial-pci", diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index d69c74c4e..b860a09ed 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -142,6 +142,7 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l req->tag = tag; req->lun = lun; req->status = -1; + req->enqueued = true; QTAILQ_INSERT_TAIL(&d->requests, req, next); return req; } @@ -158,9 +159,17 @@ SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag) return NULL; } +static void scsi_req_dequeue(SCSIRequest *req) +{ + if (req->enqueued) { + QTAILQ_REMOVE(&req->dev->requests, req, next); + req->enqueued = false; + } +} + void scsi_req_free(SCSIRequest *req) { - QTAILQ_REMOVE(&req->dev->requests, req, next); + scsi_req_dequeue(req); qemu_free(req); } @@ -512,6 +521,7 @@ void scsi_req_print(SCSIRequest *req) void scsi_req_complete(SCSIRequest *req) { assert(req->status != -1); + scsi_req_dequeue(req); req->bus->complete(req->bus, SCSI_REASON_DONE, req->tag, req->status); @@ -43,6 +43,7 @@ typedef struct SCSIRequest { enum SCSIXferMode mode; } cmd; BlockDriverAIOCB *aiocb; + bool enqueued; QTAILQ_ENTRY(SCSIRequest) next; } SCSIRequest; diff --git a/hw/vhost.c b/hw/vhost.c index d37a66e0e..65709d005 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; @@ -943,8 +943,6 @@ int kvm_cpu_exec(CPUState *env) DPRINTF("kvm_exit_debug\n"); #ifdef KVM_CAP_SET_GUEST_DEBUG if (kvm_arch_debug(&run->debug.arch)) { - gdb_set_stop_cpu(env); - vm_stop(EXCP_DEBUG); env->exception_index = EXCP_DEBUG; return 0; } diff --git a/linux-user/main.c b/linux-user/main.c index 403c8d3b9..fa29d7773 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2800,6 +2800,8 @@ int main(int argc, char **argv, char **envp) /* XXX: implement xxx_cpu_list for targets that still miss it */ #if defined(cpu_list_id) cpu_list_id(stdout, &fprintf, ""); +#elif defined(cpu_list) + cpu_list(stdout, &fprintf); /* deprecated */ #endif exit(1); } diff --git a/linux-user/signal.c b/linux-user/signal.c index cc0cb10cd..77683f753 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3064,9 +3064,23 @@ struct target_sigcontext { uint32_t oldmask; }; +struct target_stack_t { + abi_ulong ss_sp; + int ss_flags; + unsigned int ss_size; +}; + +struct target_ucontext { + abi_ulong uc_flags; + abi_ulong uc_link; + struct target_stack_t uc_stack; + struct target_sigcontext sc; + uint32_t extramask[TARGET_NSIG_WORDS - 1]; +}; + /* Signal frames. */ struct target_signal_frame { - struct target_sigcontext sc; + struct target_ucontext uc; uint32_t extramask[TARGET_NSIG_WORDS - 1]; uint32_t tramp[2]; }; @@ -3175,7 +3189,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, goto badframe; /* Save the mask. */ - err |= __put_user(set->sig[0], &frame->sc.oldmask); + err |= __put_user(set->sig[0], &frame->uc.sc.oldmask); if (err) goto badframe; @@ -3184,7 +3198,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, goto badframe; } - setup_sigcontext(&frame->sc, env); + setup_sigcontext(&frame->uc.sc, env); /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -3214,7 +3228,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* Signal handler args: */ env->regs[5] = sig; /* Arg 0: signum */ env->regs[6] = 0; - env->regs[7] = (unsigned long) &frame->sc; /* arg 1: sigcontext */ + env->regs[7] = (unsigned long) &frame->uc; /* arg 1: sigcontext */ /* Offset of 4 to handle microblaze rtid r14, 0 */ env->sregs[SR_PC] = (unsigned long)ka->_sa_handler; @@ -3247,7 +3261,7 @@ long do_sigreturn(CPUState *env) goto badframe; /* Restore blocked signals */ - if (__get_user(target_set.sig[0], &frame->sc.oldmask)) + if (__get_user(target_set.sig[0], &frame->uc.sc.oldmask)) goto badframe; for(i = 1; i < TARGET_NSIG_WORDS; i++) { if (__get_user(target_set.sig[i], &frame->extramask[i - 1])) @@ -3256,7 +3270,7 @@ long do_sigreturn(CPUState *env) target_to_host_sigset_internal(&set, &target_set); sigprocmask(SIG_SETMASK, &set, NULL); - restore_sigcontext(&frame->sc, env); + restore_sigcontext(&frame->uc.sc, env); /* We got here through a sigreturn syscall, our path back is via an rtb insn so setup r14 for that. */ env->regs[14] = env->sregs[SR_PC]; diff --git a/migration-tcp.c b/migration-tcp.c index 78b56dc3f..b55f419b6 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -151,7 +151,7 @@ static void tcp_accept_incoming_migration(void *opaque) if (c == -1) { fprintf(stderr, "could not accept migration connection\n"); - return; + goto out2; } f = qemu_fopen_socket(c); @@ -163,9 +163,10 @@ static void tcp_accept_incoming_migration(void *opaque) process_incoming_migration(f); qemu_fclose(f); out: + close(c); +out2: qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); close(s); - close(c); } int tcp_start_incoming_migration(const char *host_port) diff --git a/qemu-barrier.h b/qemu-barrier.h index 3bd1075d6..b77fce23a 100644 --- a/qemu-barrier.h +++ b/qemu-barrier.h @@ -4,4 +4,7 @@ /* FIXME: arch dependant, x86 version */ #define smp_wmb() asm volatile("" ::: "memory") +/* Compiler barrier */ +#define barrier() asm volatile("" ::: "memory") + #endif diff --git a/qemu-options.hx b/qemu-options.hx index 346e2e387..a0e3059fd 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -837,6 +837,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 faf406142..fbc78fef4 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 5ef4a3aed..19bb30c94 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 @@ -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) @@ -1221,10 +1234,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; @@ -1246,7 +1265,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) { @@ -1284,6 +1303,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); } @@ -1336,6 +1359,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) @@ -1364,6 +1388,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) @@ -1552,6 +1577,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/slirp/cksum.c b/slirp/cksum.c index 48a179219..e43867da3 100644 --- a/slirp/cksum.c +++ b/slirp/cksum.c @@ -47,23 +47,23 @@ int cksum(struct mbuf *m, int len) { - register u_int16_t *w; + register uint16_t *w; register int sum = 0; register int mlen = 0; int byte_swapped = 0; union { - u_int8_t c[2]; - u_int16_t s; + uint8_t c[2]; + uint16_t s; } s_util; union { - u_int16_t s[2]; - u_int32_t l; + uint16_t s[2]; + uint32_t l; } l_util; if (m->m_len == 0) goto cont; - w = mtod(m, u_int16_t *); + w = mtod(m, uint16_t *); mlen = m->m_len; @@ -78,8 +78,8 @@ int cksum(struct mbuf *m, int len) if ((1 & (long) w) && (mlen > 0)) { REDUCE; sum <<= 8; - s_util.c[0] = *(u_int8_t *)w; - w = (u_int16_t *)((int8_t *)w + 1); + s_util.c[0] = *(uint8_t *)w; + w = (uint16_t *)((int8_t *)w + 1); mlen--; byte_swapped = 1; } @@ -111,14 +111,14 @@ int cksum(struct mbuf *m, int len) REDUCE; sum <<= 8; if (mlen == -1) { - s_util.c[1] = *(u_int8_t *)w; + s_util.c[1] = *(uint8_t *)w; sum += s_util.s; mlen = 0; } else mlen = -1; } else if (mlen == -1) - s_util.c[0] = *(u_int8_t *)w; + s_util.c[0] = *(uint8_t *)w; cont: #ifdef DEBUG diff --git a/slirp/ip.h b/slirp/ip.h index 8d185a199..48ea38e5e 100644 --- a/slirp/ip.h +++ b/slirp/ip.h @@ -51,17 +51,17 @@ # define NTOHL(d) ((d) = ntohl((d))) # endif # ifndef NTOHS -# define NTOHS(d) ((d) = ntohs((u_int16_t)(d))) +# define NTOHS(d) ((d) = ntohs((uint16_t)(d))) # endif # ifndef HTONL # define HTONL(d) ((d) = htonl((d))) # endif # ifndef HTONS -# define HTONS(d) ((d) = htons((u_int16_t)(d))) +# define HTONS(d) ((d) = htons((uint16_t)(d))) # endif #endif -typedef u_int32_t n_long; /* long as received from the net */ +typedef uint32_t n_long; /* long as received from the net */ /* * Definitions for internet protocol version 4. @@ -80,16 +80,16 @@ struct ip { u_int ip_hl:4, /* header length */ ip_v:4; /* version */ #endif - u_int8_t ip_tos; /* type of service */ - u_int16_t ip_len; /* total length */ - u_int16_t ip_id; /* identification */ - u_int16_t ip_off; /* fragment offset field */ + uint8_t ip_tos; /* type of service */ + uint16_t ip_len; /* total length */ + uint16_t ip_id; /* identification */ + uint16_t ip_off; /* fragment offset field */ #define IP_DF 0x4000 /* don't fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ - u_int8_t ip_ttl; /* time to live */ - u_int8_t ip_p; /* protocol */ - u_int16_t ip_sum; /* checksum */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ } __attribute__((packed)); @@ -136,9 +136,9 @@ struct ip { * Time stamp option structure. */ struct ip_timestamp { - u_int8_t ipt_code; /* IPOPT_TS */ - u_int8_t ipt_len; /* size of structure (variable) */ - u_int8_t ipt_ptr; /* index of current entry */ + uint8_t ipt_code; /* IPOPT_TS */ + uint8_t ipt_len; /* size of structure (variable) */ + uint8_t ipt_ptr; /* index of current entry */ #ifdef HOST_WORDS_BIGENDIAN u_int ipt_oflw:4, /* overflow counter */ ipt_flg:4; /* flags, see below */ @@ -198,9 +198,9 @@ struct qlink { */ struct ipovly { struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */ - u_int8_t ih_x1; /* (unused) */ - u_int8_t ih_pr; /* protocol */ - u_int16_t ih_len; /* protocol length */ + uint8_t ih_x1; /* (unused) */ + uint8_t ih_pr; /* protocol */ + uint16_t ih_len; /* protocol length */ struct in_addr ih_src; /* source internet address */ struct in_addr ih_dst; /* destination internet address */ } __attribute__((packed)); @@ -215,9 +215,9 @@ struct ipovly { struct ipq { struct qlink frag_link; /* to ip headers of fragments */ struct qlink ip_link; /* to other reass headers */ - u_int8_t ipq_ttl; /* time for reass q to live */ - u_int8_t ipq_p; /* protocol of this fragment */ - u_int16_t ipq_id; /* sequence id for reassembly */ + uint8_t ipq_ttl; /* time for reass q to live */ + uint8_t ipq_p; /* protocol of this fragment */ + uint16_t ipq_id; /* sequence id for reassembly */ struct in_addr ipq_src,ipq_dst; } __attribute__((packed)); @@ -235,7 +235,7 @@ struct ipasfrag { #define ipf_tos ipf_ip.ip_tos #define ipf_len ipf_ip.ip_len #define ipf_next ipf_link.next -#define ipf_prev ipf_link.prev +#define ipf_prev ipf_link.prev /* * Structure stored in mbuf in inpcb.ip_options diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h index e793990d2..2692822f8 100644 --- a/slirp/ip_icmp.h +++ b/slirp/ip_icmp.h @@ -38,7 +38,7 @@ * Per RFC 792, September 1981. */ -typedef u_int32_t n_time; +typedef uint32_t n_time; /* * Structure of an icmp header. diff --git a/slirp/ip_input.c b/slirp/ip_input.c index bb101da1a..0fe0ff779 100644 --- a/slirp/ip_input.c +++ b/slirp/ip_input.c @@ -477,7 +477,7 @@ ip_dooptions(m) register struct in_ifaddr *ia; int opt, optlen, cnt, off, code, type, forward = 0; struct in_addr *sin, dst; -typedef u_int32_t n_time; +typedef uint32_t n_time; n_time ntime; dst = ip->ip_dst; diff --git a/slirp/ip_output.c b/slirp/ip_output.c index dba278478..542f3180b 100644 --- a/slirp/ip_output.c +++ b/slirp/ip_output.c @@ -75,9 +75,9 @@ ip_output(struct socket *so, struct mbuf *m0) /* * If small enough for interface, can just send directly. */ - if ((u_int16_t)ip->ip_len <= IF_MTU) { - ip->ip_len = htons((u_int16_t)ip->ip_len); - ip->ip_off = htons((u_int16_t)ip->ip_off); + if ((uint16_t)ip->ip_len <= IF_MTU) { + ip->ip_len = htons((uint16_t)ip->ip_len); + ip->ip_off = htons((uint16_t)ip->ip_off); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); @@ -110,7 +110,7 @@ ip_output(struct socket *so, struct mbuf *m0) */ m0 = m; mhlen = sizeof (struct ip); - for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) { + for (off = hlen + len; off < (uint16_t)ip->ip_len; off += len) { register struct ip *mhip; m = m_get(slirp); if (m == NULL) { @@ -125,18 +125,18 @@ ip_output(struct socket *so, struct mbuf *m0) mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); if (ip->ip_off & IP_MF) mhip->ip_off |= IP_MF; - if (off + len >= (u_int16_t)ip->ip_len) - len = (u_int16_t)ip->ip_len - off; + if (off + len >= (uint16_t)ip->ip_len) + len = (uint16_t)ip->ip_len - off; else mhip->ip_off |= IP_MF; - mhip->ip_len = htons((u_int16_t)(len + mhlen)); + mhip->ip_len = htons((uint16_t)(len + mhlen)); if (m_copy(m, m0, off, len) < 0) { error = -1; goto sendorfree; } - mhip->ip_off = htons((u_int16_t)mhip->ip_off); + mhip->ip_off = htons((uint16_t)mhip->ip_off); mhip->ip_sum = 0; mhip->ip_sum = cksum(m, mhlen); *mnext = m; @@ -147,9 +147,9 @@ ip_output(struct socket *so, struct mbuf *m0) * and updating header, then send each fragment (in order). */ m = m0; - m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len); - ip->ip_len = htons((u_int16_t)m->m_len); - ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF)); + m_adj(m, hlen + firstlen - (uint16_t)ip->ip_len); + ip->ip_len = htons((uint16_t)m->m_len); + ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF)); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); sendorfree: diff --git a/slirp/main.h b/slirp/main.h index 8d09df9d4..0dd8d81ce 100644 --- a/slirp/main.h +++ b/slirp/main.h @@ -14,7 +14,7 @@ extern int slirp_socket; extern int slirp_socket_unit; extern int slirp_socket_port; -extern u_int32_t slirp_socket_addr; +extern uint32_t slirp_socket_addr; extern char *slirp_socket_passwd; extern int ctty_closed; diff --git a/slirp/misc.h b/slirp/misc.h index da68d0995..ed40a103c 100644 --- a/slirp/misc.h +++ b/slirp/misc.h @@ -37,24 +37,24 @@ void do_wait(int); #define EMU_NOCONNECT 0x10 /* Don't connect */ struct tos_t { - u_int16_t lport; - u_int16_t fport; - u_int8_t tos; - u_int8_t emu; + uint16_t lport; + uint16_t fport; + uint8_t tos; + uint8_t emu; }; struct emu_t { - u_int16_t lport; - u_int16_t fport; - u_int8_t tos; - u_int8_t emu; - struct emu_t *next; + uint16_t lport; + uint16_t fport; + uint8_t tos; + uint8_t emu; + struct emu_t *next; }; extern int x_port, x_server, x_display; int show_x(char *, struct socket *); -void redir_x(u_int32_t, int, int, int); +void redir_x(uint32_t, int, int, int); void slirp_insque(void *, void *); void slirp_remque(void *); int add_exec(struct ex_list **, int, char *, struct in_addr, int); diff --git a/slirp/slirp.h b/slirp/slirp.h index 98a26442a..3a5d592fb 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -7,10 +7,6 @@ #ifdef _WIN32 # include <inttypes.h> -typedef uint8_t u_int8_t; -typedef uint16_t u_int16_t; -typedef uint32_t u_int32_t; -typedef uint64_t u_int64_t; typedef char *caddr_t; # include <windows.h> @@ -38,35 +34,6 @@ typedef char *caddr_t; #include <sys/time.h> -#ifdef NEED_TYPEDEFS -typedef char int8_t; -typedef unsigned char u_int8_t; - -# if SIZEOF_SHORT == 2 - typedef short int16_t; - typedef unsigned short u_int16_t; -# else -# if SIZEOF_INT == 2 - typedef int int16_t; - typedef unsigned int u_int16_t; -# else - #error Cannot find a type with sizeof() == 2 -# endif -# endif - -# if SIZEOF_SHORT == 4 - typedef short int32_t; - typedef unsigned short u_int32_t; -# else -# if SIZEOF_INT == 4 - typedef int int32_t; - typedef unsigned int u_int32_t; -# else - #error Cannot find a type with sizeof() == 4 -# endif -# endif -#endif /* NEED_TYPEDEFS */ - #ifdef HAVE_UNISTD_H # include <unistd.h> #endif @@ -233,7 +200,7 @@ struct Slirp { /* ip states */ struct ipq ipq; /* ip reass. queue */ - u_int16_t ip_id; /* ip packet ctr, for ids */ + uint16_t ip_id; /* ip packet ctr, for ids */ /* bootp/dhcp states */ BOOTPClient bootp_clients[NB_BOOTP_CLIENTS]; @@ -243,7 +210,7 @@ struct Slirp { struct socket tcb; struct socket *tcp_last_so; tcp_seq tcp_iss; /* tcp initial send seq # */ - u_int32_t tcp_now; /* for RFC 1323 timestamps */ + uint32_t tcp_now; /* for RFC 1323 timestamps */ /* udp states */ struct socket udb; @@ -339,7 +306,7 @@ void tcp_sockclosed(struct tcpcb *); int tcp_fconnect(struct socket *); void tcp_connect(struct socket *); int tcp_attach(struct socket *); -u_int8_t tcp_tos(struct socket *); +uint8_t tcp_tos(struct socket *); int tcp_emu(struct socket *, struct mbuf *); int tcp_ctl(struct socket *); struct tcpcb *tcp_drop(struct tcpcb *tp, int err); diff --git a/slirp/slirp_config.h b/slirp/slirp_config.h index a40248eb7..f19c7034c 100644 --- a/slirp/slirp_config.h +++ b/slirp/slirp_config.h @@ -133,12 +133,6 @@ /* Define if your compiler doesn't like prototypes */ #undef NO_PROTOTYPES -/* Define if you don't have u_int32_t etc. typedef'd */ -#undef NEED_TYPEDEFS -#ifdef __sun__ -#define NEED_TYPEDEFS -#endif - /* Define to sizeof(char) */ #define SIZEOF_CHAR 1 diff --git a/slirp/socket.c b/slirp/socket.c index eaad77af8..611923424 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -580,7 +580,7 @@ sosendto(struct socket *so, struct mbuf *m) * Listen for incoming TCP connections */ struct socket * -tcp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr, +tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, u_int lport, int flags) { struct sockaddr_in addr; diff --git a/slirp/socket.h b/slirp/socket.h index 6e85d0358..857b0da31 100644 --- a/slirp/socket.h +++ b/slirp/socket.h @@ -31,11 +31,11 @@ struct socket { int so_urgc; struct in_addr so_faddr; /* foreign host table entry */ struct in_addr so_laddr; /* local host table entry */ - u_int16_t so_fport; /* foreign port */ - u_int16_t so_lport; /* local port */ + uint16_t so_fport; /* foreign port */ + uint16_t so_lport; /* local port */ - u_int8_t so_iptos; /* Type of service */ - u_int8_t so_emu; /* Is the socket emulated? */ + uint8_t so_iptos; /* Type of service */ + uint8_t so_emu; /* Is the socket emulated? */ u_char so_type; /* Type of socket, UDP or TCP */ int so_state; /* internal state flags SS_*, below */ @@ -83,7 +83,7 @@ int sosendoob(struct socket *); int sowrite(struct socket *); void sorecvfrom(struct socket *); int sosendto(struct socket *, struct mbuf *); -struct socket * tcp_listen(Slirp *, u_int32_t, u_int, u_int32_t, u_int, +struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, int); void soisfconnecting(register struct socket *); void soisfconnected(register struct socket *); diff --git a/slirp/tcp.h b/slirp/tcp.h index c7e3457b7..9d0683662 100644 --- a/slirp/tcp.h +++ b/slirp/tcp.h @@ -33,7 +33,7 @@ #ifndef _TCP_H_ #define _TCP_H_ -typedef u_int32_t tcp_seq; +typedef uint32_t tcp_seq; #define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */ #define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */ @@ -46,8 +46,8 @@ typedef u_int32_t tcp_seq; * Per RFC 793, September, 1981. */ struct tcphdr { - u_int16_t th_sport; /* source port */ - u_int16_t th_dport; /* destination port */ + uint16_t th_sport; /* source port */ + uint16_t th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ #ifdef HOST_WORDS_BIGENDIAN @@ -57,16 +57,16 @@ struct tcphdr { u_int th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif - u_int8_t th_flags; + uint8_t th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 - u_int16_t th_win; /* window */ - u_int16_t th_sum; /* checksum */ - u_int16_t th_urp; /* urgent pointer */ + uint16_t th_win; /* window */ + uint16_t th_sum; /* checksum */ + uint16_t th_urp; /* urgent pointer */ }; #include "tcp_var.h" diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c index 2808e3e4e..e4a77310d 100644 --- a/slirp/tcp_input.c +++ b/slirp/tcp_input.c @@ -280,7 +280,7 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso) tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr)); ti->ti_x1 = 0; - ti->ti_len = htons((u_int16_t)tlen); + ti->ti_len = htons((uint16_t)tlen); len = sizeof(struct ip ) + tlen; if(cksum(m, len)) { goto drop; @@ -1289,7 +1289,7 @@ drop: static void tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti) { - u_int16_t mss; + uint16_t mss; int opt, optlen; DEBUG_CALL("tcp_dooptions"); diff --git a/slirp/tcp_output.c b/slirp/tcp_output.c index 0d6011ac6..779314bf9 100644 --- a/slirp/tcp_output.c +++ b/slirp/tcp_output.c @@ -263,11 +263,11 @@ send: if (flags & TH_SYN) { tp->snd_nxt = tp->iss; if ((tp->t_flags & TF_NOOPT) == 0) { - u_int16_t mss; + uint16_t mss; opt[0] = TCPOPT_MAXSEG; opt[1] = 4; - mss = htons((u_int16_t) tcp_mss(tp, 0)); + mss = htons((uint16_t) tcp_mss(tp, 0)); memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss)); optlen = 4; } @@ -364,10 +364,10 @@ send: win = (long)TCP_MAXWIN << tp->rcv_scale; if (win < (long)(tp->rcv_adv - tp->rcv_nxt)) win = (long)(tp->rcv_adv - tp->rcv_nxt); - ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale)); + ti->ti_win = htons((uint16_t) (win>>tp->rcv_scale)); if (SEQ_GT(tp->snd_up, tp->snd_una)) { - ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq))); + ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq))); ti->ti_flags |= TH_URG; } else /* @@ -383,7 +383,7 @@ send: * checksum extended header and data. */ if (len + optlen) - ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) + + ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) + optlen + len)); ti->ti_sum = cksum(m, (int)(hdrlen + len)); diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index 0a370f101..b661d2623 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -134,8 +134,8 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, m->m_len = sizeof (struct tcpiphdr); tlen = 0; #define xchg(a,b,type) { type t; t=a; a=b; b=t; } - xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t); - xchg(ti->ti_dport, ti->ti_sport, u_int16_t); + xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t); + xchg(ti->ti_dport, ti->ti_sport, uint16_t); #undef xchg } ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); @@ -150,9 +150,9 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, ti->ti_off = sizeof (struct tcphdr) >> 2; ti->ti_flags = flags; if (tp) - ti->ti_win = htons((u_int16_t) (win >> tp->rcv_scale)); + ti->ti_win = htons((uint16_t) (win >> tp->rcv_scale)); else - ti->ti_win = htons((u_int16_t)win); + ti->ti_win = htons((uint16_t)win); ti->ti_urp = 0; ti->ti_sum = 0; ti->ti_sum = cksum(m, tlen); @@ -491,7 +491,7 @@ static struct emu_t *tcpemu = NULL; /* * Return TOS according to the above table */ -u_int8_t +uint8_t tcp_tos(struct socket *so) { int i = 0; @@ -548,7 +548,7 @@ tcp_emu(struct socket *so, struct mbuf *m) Slirp *slirp = so->slirp; u_int n1, n2, n3, n4, n5, n6; char buff[257]; - u_int32_t laddr; + uint32_t laddr; u_int lport; char *bptr; diff --git a/slirp/tcp_var.h b/slirp/tcp_var.h index 4ffbe04a1..004193fb6 100644 --- a/slirp/tcp_var.h +++ b/slirp/tcp_var.h @@ -75,9 +75,9 @@ struct tcpcb { tcp_seq snd_wl1; /* window update seg seq number */ tcp_seq snd_wl2; /* window update seg ack number */ tcp_seq iss; /* initial send sequence number */ - u_int32_t snd_wnd; /* send window */ + uint32_t snd_wnd; /* send window */ /* receive sequence variables */ - u_int32_t rcv_wnd; /* receive window */ + uint32_t rcv_wnd; /* receive window */ tcp_seq rcv_nxt; /* receive next */ tcp_seq rcv_up; /* receive urgent pointer */ tcp_seq irs; /* initial receive sequence number */ @@ -91,8 +91,8 @@ struct tcpcb { * used to recognize retransmits */ /* congestion control (for slow start, source quench, retransmit after loss) */ - u_int32_t snd_cwnd; /* congestion-controlled window */ - u_int32_t snd_ssthresh; /* snd_cwnd size threshold for + uint32_t snd_cwnd; /* congestion-controlled window */ + uint32_t snd_ssthresh; /* snd_cwnd size threshold for * for slow start exponential to * linear switch */ @@ -106,7 +106,7 @@ struct tcpcb { short t_srtt; /* smoothed round-trip time */ short t_rttvar; /* variance in round-trip time */ u_short t_rttmin; /* minimum rtt allowed */ - u_int32_t max_sndwnd; /* largest window peer has offered */ + uint32_t max_sndwnd; /* largest window peer has offered */ /* out-of-band data */ char t_oobflags; /* have some */ @@ -120,8 +120,8 @@ struct tcpcb { u_char rcv_scale; /* window scaling for recv window */ u_char request_r_scale; /* pending window scaling */ u_char requested_s_scale; - u_int32_t ts_recent; /* timestamp echo data */ - u_int32_t ts_recent_age; /* when last updated */ + uint32_t ts_recent; /* timestamp echo data */ + uint32_t ts_recent_age; /* when last updated */ tcp_seq last_ack_sent; }; diff --git a/slirp/tftp.c b/slirp/tftp.c index 67e9f2b9d..55e4692ac 100644 --- a/slirp/tftp.c +++ b/slirp/tftp.c @@ -92,8 +92,8 @@ static int tftp_session_find(Slirp *slirp, struct tftp_t *tp) return -1; } -static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr, - u_int8_t *buf, int len) +static int tftp_read_data(struct tftp_session *spt, uint16_t block_nr, + uint8_t *buf, int len) { int fd; int bytes_read = 0; @@ -155,7 +155,7 @@ static int tftp_send_oack(struct tftp_session *spt, } static void tftp_send_error(struct tftp_session *spt, - u_int16_t errorcode, const char *msg, + uint16_t errorcode, const char *msg, struct tftp_t *recv_tp) { struct sockaddr_in saddr, daddr; @@ -194,7 +194,7 @@ out: } static int tftp_send_data(struct tftp_session *spt, - u_int16_t block_nr, + uint16_t block_nr, struct tftp_t *recv_tp) { struct sockaddr_in saddr, daddr; diff --git a/slirp/tftp.h b/slirp/tftp.h index 1415c8527..b9f0847eb 100644 --- a/slirp/tftp.h +++ b/slirp/tftp.h @@ -16,17 +16,17 @@ struct tftp_t { struct ip ip; struct udphdr udp; - u_int16_t tp_op; + uint16_t tp_op; union { struct { - u_int16_t tp_block_nr; - u_int8_t tp_buf[512]; + uint16_t tp_block_nr; + uint8_t tp_buf[512]; } tp_data; struct { - u_int16_t tp_error_code; - u_int8_t tp_msg[512]; + uint16_t tp_error_code; + uint8_t tp_msg[512]; } tp_error; - u_int8_t tp_buf[512 + 2]; + uint8_t tp_buf[512 + 2]; } x; }; @@ -35,7 +35,7 @@ struct tftp_session { char *filename; struct in_addr client_ip; - u_int16_t client_port; + uint16_t client_port; int timestamp; }; diff --git a/slirp/udp.c b/slirp/udp.c index d6c39b97b..02b3793e9 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -41,7 +41,7 @@ #include <slirp.h> #include "ip_icmp.h" -static u_int8_t udp_tos(struct socket *so); +static uint8_t udp_tos(struct socket *so); void udp_init(Slirp *slirp) @@ -88,7 +88,7 @@ udp_input(register struct mbuf *m, int iphlen) * Make mbuf data length reflect UDP length. * If not enough data to reflect UDP length, drop. */ - len = ntohs((u_int16_t)uh->uh_ulen); + len = ntohs((uint16_t)uh->uh_ulen); if (ip->ip_len != len) { if (len > ip->ip_len) { @@ -321,7 +321,7 @@ static const struct tos_t udptos[] = { {0, 0, 0, 0} }; -static u_int8_t +static uint8_t udp_tos(struct socket *so) { int i = 0; @@ -339,7 +339,7 @@ udp_tos(struct socket *so) } struct socket * -udp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr, +udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, u_int lport, int flags) { struct sockaddr_in addr; diff --git a/slirp/udp.h b/slirp/udp.h index 47d2f3d4c..9b5c3cf56 100644 --- a/slirp/udp.h +++ b/slirp/udp.h @@ -41,10 +41,10 @@ * Per RFC 768, September, 1981. */ struct udphdr { - u_int16_t uh_sport; /* source port */ - u_int16_t uh_dport; /* destination port */ - int16_t uh_ulen; /* udp length */ - u_int16_t uh_sum; /* udp checksum */ + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + int16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ }; /* @@ -78,7 +78,7 @@ void udp_input(register struct mbuf *, int); int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *); int udp_attach(struct socket *); void udp_detach(struct socket *); -struct socket * udp_listen(Slirp *, u_int32_t, u_int, u_int32_t, u_int, +struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, int); int udp_output2(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, struct sockaddr_in *daddr, diff --git a/target-i386/translate.c b/target-i386/translate.c index 2fcc02616..7b6e3c2ea 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -2310,10 +2310,7 @@ static inline void gen_jcc(DisasContext *s, int b, int l1, l2, cc_op; cc_op = s->cc_op; - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); if (s->jmp_opt) { l1 = gen_new_label(); gen_jcc1(s, cc_op, b, l1); @@ -2322,7 +2319,7 @@ static inline void gen_jcc(DisasContext *s, int b, gen_set_label(l1); gen_goto_tb(s, 1, val); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } else { l1 = gen_new_label(); @@ -2400,11 +2397,11 @@ static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip) stop as a special handling must be done to disable hardware interrupts for the next instruction */ if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS)) - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } else { gen_op_movl_seg_T0_vm(seg_reg); if (seg_reg == R_SS) - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } } @@ -2672,7 +2669,7 @@ static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) gen_op_set_cc_op(s->cc_op); gen_jmp_im(cur_eip); gen_helper_raise_exception(tcg_const_i32(trapno)); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } /* an interrupt is different from an exception because of the @@ -2685,7 +2682,7 @@ static void gen_interrupt(DisasContext *s, int intno, gen_jmp_im(cur_eip); gen_helper_raise_interrupt(tcg_const_i32(intno), tcg_const_i32(next_eip - cur_eip)); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } static void gen_debug(DisasContext *s, target_ulong cur_eip) @@ -2694,7 +2691,7 @@ static void gen_debug(DisasContext *s, target_ulong cur_eip) gen_op_set_cc_op(s->cc_op); gen_jmp_im(cur_eip); gen_helper_debug(); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } /* generate a generic end of block. Trace exception is also generated @@ -2716,7 +2713,7 @@ static void gen_eob(DisasContext *s) } else { tcg_gen_exit_tb(0); } - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } /* generate a jump to eip. No segment change must happen before as a @@ -2724,12 +2721,9 @@ static void gen_eob(DisasContext *s) static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) { if (s->jmp_opt) { - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_goto_tb(s, tb_num, eip); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } else { gen_jmp_im(eip); gen_eob(s); @@ -6901,10 +6895,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_sysenter(); gen_eob(s); @@ -6917,10 +6908,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_sysexit(tcg_const_i32(dflag)); gen_eob(s); @@ -6929,10 +6917,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) #ifdef TARGET_X86_64 case 0x105: /* syscall */ /* XXX: is it usable in real mode ? */ - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_syscall(tcg_const_i32(s->pc - pc_start)); gen_eob(s); @@ -6941,10 +6926,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_sysret(tcg_const_i32(s->dflag)); /* condition codes are modified only in long mode */ @@ -6968,7 +6950,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_op_set_cc_op(s->cc_op); gen_jmp_im(pc_start - s->cs_base); gen_helper_hlt(tcg_const_i32(s->pc - pc_start)); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } break; case 0x100: @@ -7085,10 +7067,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_mwait(tcg_const_i32(s->pc - pc_start)); gen_eob(s); @@ -7125,7 +7104,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_helper_vmrun(tcg_const_i32(s->aflag), tcg_const_i32(s->pc - pc_start)); tcg_gen_exit_tb(0); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } break; case 1: /* VMMCALL */ @@ -7613,10 +7592,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM); if (!(s->flags & HF_SMM_MASK)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(s->pc - s->cs_base); gen_helper_rsm(); gen_eob(s); diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index ca54e2c30..f61829081 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -63,8 +63,7 @@ static TCGv env_iflags; /* This is the state at translation time. */ typedef struct DisasContext { CPUState *env; - target_ulong pc, ppc; - target_ulong cache_pc; + target_ulong pc; /* Decoder. */ int type_b; @@ -153,6 +152,14 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) } } +/* True if ALU operand b is a small immediate that may deserve + faster treatment. */ +static inline int dec_alu_op_b_is_small_imm(DisasContext *dc) +{ + /* Immediate insn without the imm prefix ? */ + return dc->type_b && !(dc->tb_flags & IMM_FLAG); +} + static inline TCGv *dec_alu_op_b(DisasContext *dc) { if (dc->type_b) { @@ -780,6 +787,13 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) /* Treat the fast cases first. */ if (!dc->type_b) { + /* If any of the regs is r0, return a ptr to the other. */ + if (dc->ra == 0) { + return &cpu_R[dc->rb]; + } else if (dc->rb == 0) { + return &cpu_R[dc->ra]; + } + *t = tcg_temp_new(); tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]); return t; @@ -904,50 +918,24 @@ static void dec_store(DisasContext *dc) static inline void eval_cc(DisasContext *dc, unsigned int cc, TCGv d, TCGv a, TCGv b) { - int l1; - switch (cc) { case CC_EQ: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_EQ, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b); break; case CC_NE: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_NE, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_NE, d, a, b); break; case CC_LT: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_LT, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_LT, d, a, b); break; case CC_LE: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_LE, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_LE, d, a, b); break; case CC_GE: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_GE, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_GE, d, a, b); break; case CC_GT: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_GT, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_GT, d, a, b); break; default: cpu_abort(dc->env, "Unknown condition code %x.\n", cc); @@ -984,10 +972,16 @@ static void dec_bcc(DisasContext *dc) cpu_env, offsetof(CPUState, bimm)); } - tcg_gen_movi_tl(env_btarget, dc->pc); - tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); - eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0)); + if (dec_alu_op_b_is_small_imm(dc)) { + int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ + + tcg_gen_movi_tl(env_btarget, dc->pc + offset); + } else { + tcg_gen_movi_tl(env_btarget, dc->pc); + tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + } dc->jmp = JMP_INDIRECT; + eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0)); } static void dec_br(DisasContext *dc) @@ -1031,13 +1025,13 @@ static void dec_br(DisasContext *dc) } } } else { - if (!dc->type_b || (dc->tb_flags & IMM_FLAG)) { + if (dec_alu_op_b_is_small_imm(dc)) { + dc->jmp = JMP_DIRECT; + dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); + } else { tcg_gen_movi_tl(env_btaken, 1); tcg_gen_movi_tl(env_btarget, dc->pc); tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); - } else { - dc->jmp = JMP_DIRECT; - dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); } } } @@ -1279,9 +1273,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb, dc->is_jmp = DISAS_NEXT; dc->jmp = 0; dc->delayed_branch = !!(dc->tb_flags & D_FLAG); - dc->ppc = pc_start; dc->pc = pc_start; - dc->cache_pc = -1; dc->singlestep_enabled = env->singlestep_enabled; dc->cpustate_changed = 0; dc->abort_at_next_insn = 0; @@ -1337,7 +1329,6 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb, decode(dc); if (dc->clear_imm) dc->tb_flags &= ~IMM_FLAG; - dc->ppc = dc->pc; dc->pc += 4; num_insns++; diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 81051aa00..b8e6feefc 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -598,7 +598,7 @@ void cpu_mips_start_count(CPUState *env); void cpu_mips_stop_count(CPUState *env); /* mips_int.c */ -void cpu_mips_update_irq (CPUState *env); +void cpu_mips_soft_irq(CPUState *env, int irq, int level); /* helper.c */ int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, diff --git a/target-mips/helper.h b/target-mips/helper.h index a6ba75dfb..cb13fb235 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -2,7 +2,6 @@ DEF_HELPER_2(raise_exception_err, void, i32, int) DEF_HELPER_1(raise_exception, void, i32) -DEF_HELPER_0(interrupt_restart, void) #ifdef TARGET_MIPS64 DEF_HELPER_3(ldl, tl, tl, tl, int) diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 8ae510adc..a619b7261 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -46,18 +46,6 @@ void helper_raise_exception (uint32_t exception) helper_raise_exception_err(exception, 0); } -void helper_interrupt_restart (void) -{ - if (!(env->CP0_Status & (1 << CP0St_EXL)) && - !(env->CP0_Status & (1 << CP0St_ERL)) && - !(env->hflags & MIPS_HFLAG_DM) && - (env->CP0_Status & (1 << CP0St_IE)) && - (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask)) { - env->CP0_Cause &= ~(0x1f << CP0Ca_EC); - helper_raise_exception(EXCP_EXT_INTERRUPT); - } -} - #if !defined(CONFIG_USER_ONLY) static void do_restore_state (void *pc_ptr) { @@ -1313,7 +1301,6 @@ void helper_mtc0_status (target_ulong arg1) default: cpu_abort(env, "Invalid MMU mode!\n"); break; } } - cpu_mips_update_irq(env); } void helper_mttc0_status(target_ulong arg1) @@ -1347,6 +1334,7 @@ void helper_mtc0_cause (target_ulong arg1) { uint32_t mask = 0x00C00300; uint32_t old = env->CP0_Cause; + int i; if (env->insn_flags & ISA_MIPS32R2) mask |= 1 << CP0Ca_DC; @@ -1360,10 +1348,11 @@ void helper_mtc0_cause (target_ulong arg1) cpu_mips_start_count(env); } - /* Handle the software interrupt as an hardware one, as they - are very similar */ - if (arg1 & CP0Ca_IP_mask) { - cpu_mips_update_irq(env); + /* Set/reset software interrupts */ + for (i = 0 ; i < 2 ; i++) { + if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) { + cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i))); + } } } @@ -1793,8 +1782,6 @@ target_ulong helper_di (void) target_ulong t0 = env->CP0_Status; env->CP0_Status = t0 & ~(1 << CP0St_IE); - cpu_mips_update_irq(env); - return t0; } @@ -1803,8 +1790,6 @@ target_ulong helper_ei (void) target_ulong t0 = env->CP0_Status; env->CP0_Status = t0 | (1 << CP0St_IE); - cpu_mips_update_irq(env); - return t0; } diff --git a/target-mips/translate.c b/target-mips/translate.c index 716827338..6c72dee1b 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -5190,7 +5190,17 @@ static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int switch (sel) { case 0: save_cpu_state(ctx, 1); + /* Mark as an IO operation because we may trigger a software + interrupt. */ + if (use_icount) { + gen_io_start(); + } gen_helper_mtc0_cause(arg); + if (use_icount) { + gen_io_end(); + } + /* Stop translation as we may have triggered an intetrupt */ + ctx->bstate = BS_STOP; rn = "Cause"; break; default: @@ -12365,7 +12375,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, } else { switch (ctx.bstate) { case BS_STOP: - gen_helper_interrupt_restart(); gen_goto_tb(&ctx, 0, ctx.pc); break; case BS_NONE: @@ -12373,7 +12382,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, gen_goto_tb(&ctx, 0, ctx.pc); break; case BS_EXCP: - gen_helper_interrupt_restart(); tcg_gen_exit_tb(0); break; case BS_BRANCH: diff --git a/tests/sha1.c b/tests/sha1.c index 3a7655582..93b7c8e80 100644 --- a/tests/sha1.c +++ b/tests/sha1.c @@ -23,7 +23,7 @@ A million repetitions of "a" #include <stdio.h> #include <string.h> -#include <sys/types.h> /* for u_int*_t */ +#include <stdint.h> /* ================ sha1.h ================ */ /* @@ -33,14 +33,14 @@ By Steve Reid <steve@edmweb.com> */ typedef struct { - u_int32_t state[5]; - u_int32_t count[2]; + uint32_t state[5]; + uint32_t count[2]; unsigned char buffer[64]; } SHA1_CTX; -void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]); +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); void SHA1Init(SHA1_CTX* context); -void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); void SHA1Final(unsigned char digest[20], SHA1_CTX* context); /* ================ end of sha1.h ================ */ #include <endian.h> @@ -70,12 +70,12 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context); /* Hash a single 512-bit block. This is the core of the algorithm. */ -void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { -u_int32_t a, b, c, d, e; +uint32_t a, b, c, d, e; typedef union { unsigned char c[64]; - u_int32_t l[16]; + uint32_t l[16]; } CHAR64LONG16; #ifdef SHA1HANDSOFF CHAR64LONG16 block[1]; /* use array to appear as a pointer */ @@ -145,10 +145,10 @@ void SHA1Init(SHA1_CTX* context) /* Run your data through this. */ -void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len) +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) { -u_int32_t i; -u_int32_t j; +uint32_t i; +uint32_t j; j = context->count[0]; if ((context->count[0] += len << 3) < j) @@ -186,7 +186,7 @@ unsigned char c; for (i = 0; i < 2; i++) { - u_int32_t t = context->count[i]; + uint32_t t = context->count[i]; int j; for (j = 0; j < 4; t >>= 8, j++) diff --git a/curses_keys.h b/ui/curses_keys.h index 1decd1119..1decd1119 100644 --- a/curses_keys.h +++ b/ui/curses_keys.h diff --git a/keymaps.c b/ui/keymaps.c index 78c7ea375..78c7ea375 100644 --- a/keymaps.c +++ b/ui/keymaps.c diff --git a/keymaps.h b/ui/keymaps.h index a7600d575..a7600d575 100644 --- a/keymaps.h +++ b/ui/keymaps.h diff --git a/sdl_keysym.h b/ui/sdl_keysym.h index ee904805d..ee904805d 100644 --- a/sdl_keysym.h +++ b/ui/sdl_keysym.h diff --git a/sdl_zoom.c b/ui/sdl_zoom.c index a986c7c14..a986c7c14 100644 --- a/sdl_zoom.c +++ b/ui/sdl_zoom.c diff --git a/sdl_zoom.h b/ui/sdl_zoom.h index 74955bc94..74955bc94 100644 --- a/sdl_zoom.h +++ b/ui/sdl_zoom.h diff --git a/sdl_zoom_template.h b/ui/sdl_zoom_template.h index 64bbca849..64bbca849 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 a51ddc8a1..a51ddc8a1 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 fd9b18a8f..fd9b18a8f 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 07c169186..07c169186 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 9f674c517..9f674c517 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 b9f9f5ef8..b9f9f5ef8 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 728f25e5d..364a49115 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 000000000..86bb49a16 --- /dev/null +++ b/ui/vnc-enc-tight.c @@ -0,0 +1,1715 @@ +/* + * 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 uint +tight_detect_smooth_image24(VncState *vs, int w, int h) +{ + int off; + int x, y, d, dx; + uint c; + uint stats[256]; + int pixels = 0; + int pix, left[3]; + uint 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 uint \ + 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; \ + uint c; \ + uint stats[256]; \ + int pixels = 0; \ + int sample, sum, left[3]; \ + uint 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) +{ + uint 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 != -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 != -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 == -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); + } + 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 64d10625f..a3add788e 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 a99bc387d..3c6e6abf9 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 000000000..6e9cf08b6 --- /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 000000000..49b77afcc --- /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 000000000..b8dab8169 --- /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 000000000..bff6445cc --- /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 000000000..d0645ebde --- /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 dec626c53..dec626c53 100644 --- a/vnc-tls.c +++ b/ui/vnc-tls.c diff --git a/vnc-tls.h b/ui/vnc-tls.h index 2b9363389..2b9363389 100644 --- a/vnc-tls.h +++ b/ui/vnc-tls.h @@ -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; } } @@ -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 55cb87ede..55cb87ede 100644 --- a/vnc_keysym.h +++ b/ui/vnc_keysym.h diff --git a/x_keymap.c b/ui/x_keymap.c index b9b094418..b9b094418 100644 --- a/x_keymap.c +++ b/ui/x_keymap.c diff --git a/x_keymap.h b/ui/x_keymap.h index 2042ce0ed..2042ce0ed 100644 --- a/x_keymap.h +++ b/ui/x_keymap.h @@ -1351,7 +1351,7 @@ static void main_loop(void) int64_t ti; #endif #ifndef CONFIG_IOTHREAD - nonblocking = tcg_cpu_exec(); + nonblocking = cpu_exec_all(); #endif #ifdef CONFIG_PROFILER ti = profile_getclock(); diff --git a/vnc-encoding-tight.c b/vnc-encoding-tight.c deleted file mode 100644 index faba4834c..000000000 --- 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); -} |