aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--arch_init.c32
-rw-r--r--audio/spiceaudio.c2
-rw-r--r--audio/wavcapture.c3
-rw-r--r--blockdev-nbd.c4
-rw-r--r--bsd-user/bsdload.c2
-rw-r--r--bsd-user/elfload.c2
-rw-r--r--bsd-user/main.c14
-rwxr-xr-xconfigure58
-rw-r--r--dma-helpers.c2
-rw-r--r--docs/multiseat.txt76
-rw-r--r--hw/display/jazz_led.c1
-rw-r--r--hw/input/hid.c220
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/lm32_sys.c179
-rw-r--r--hw/net/cadence_gem.c2
-rw-r--r--hw/pci/pci.c4
-rw-r--r--hw/usb/dev-hid.c13
-rw-r--r--hw/usb/dev-mtp.c28
-rw-r--r--hw/usb/hcd-xhci.c17
-rw-r--r--hw/usb/host-libusb.c83
-rw-r--r--hw/usb/redirect.c137
-rw-r--r--include/exec/exec-all.h4
-rw-r--r--include/hw/input/hid.h4
-rw-r--r--include/qemu/bswap.h45
-rw-r--r--include/ui/console.h1
-rw-r--r--include/ui/input.h4
-rw-r--r--iohandler.c1
-rw-r--r--libcacard/cac.c38
-rw-r--r--libcacard/card_7816.c16
-rw-r--r--libcacard/event.c2
-rw-r--r--libcacard/vcard.c26
-rw-r--r--libcacard/vcard_emul_nss.c39
-rw-r--r--libcacard/vreader.c31
-rw-r--r--libcacard/vscclient.c11
-rw-r--r--nbd.c2
-rw-r--r--qemu-nbd.c6
-rw-r--r--qemu-nbd.texi2
-rw-r--r--qemu-options.hx3
-rw-r--r--target-arm/cpu.c2
-rw-r--r--target-lm32/Makefile.objs1
-rw-r--r--target-lm32/README15
-rw-r--r--target-lm32/cpu.h1
-rw-r--r--target-lm32/helper.c14
-rw-r--r--target-lm32/lm32-semi.c215
-rw-r--r--tcg/mips/tcg-target.c1849
-rw-r--r--tcg/mips/tcg-target.h14
-rw-r--r--tcg/tci/tcg-target.c3
-rw-r--r--tests/tcg/lm32/Makefile15
-rw-r--r--tests/tcg/lm32/crt.S4
-rw-r--r--tests/tcg/lm32/helper.S65
-rw-r--r--tests/tcg/lm32/macros.inc37
-rw-r--r--tests/tcg/lm32/test_lb.S4
-rw-r--r--tests/tcg/lm32/test_lbu.S4
-rw-r--r--tests/tcg/lm32/test_lh.S4
-rw-r--r--tests/tcg/lm32/test_lhu.S4
-rw-r--r--tests/tcg/lm32/test_lw.S2
-rw-r--r--tests/tcg/lm32/test_sb.S2
-rw-r--r--tests/tcg/lm32/test_scall.S4
-rw-r--r--tests/tcg/lm32/test_sh.S2
-rw-r--r--tests/tcg/lm32/test_sw.S3
-rw-r--r--trace-events12
-rw-r--r--translate-all.c103
-rw-r--r--ui/console.c233
-rw-r--r--ui/curses.c4
-rw-r--r--ui/gtk.c1108
-rw-r--r--ui/input-keymap.c13
-rw-r--r--ui/input.c49
-rw-r--r--ui/sdl2.c21
-rw-r--r--vl.c9
70 files changed, 2993 insertions, 1939 deletions
diff --git a/.gitignore b/.gitignore
index 8a5270973e..c658613560 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/config-host.*
/config-target.*
/config.status
+/config-temp
/trace/generated-tracers.h
/trace/generated-tracers.c
/trace/generated-tracers-dtrace.h
diff --git a/arch_init.c b/arch_init.c
index 685ba0e268..9f1a174d3a 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -975,12 +975,12 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
xh_len = qemu_get_be16(f);
if (xh_flags != ENCODING_FLAG_XBZRLE) {
- fprintf(stderr, "Failed to load XBZRLE page - wrong compression!\n");
+ error_report("Failed to load XBZRLE page - wrong compression!");
return -1;
}
if (xh_len > TARGET_PAGE_SIZE) {
- fprintf(stderr, "Failed to load XBZRLE page - len overflow!\n");
+ error_report("Failed to load XBZRLE page - len overflow!");
return -1;
}
/* load data and decode */
@@ -989,7 +989,7 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
/* decode RLE */
if (xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host,
TARGET_PAGE_SIZE) == -1) {
- fprintf(stderr, "Failed to load XBZRLE page - decode error!\n");
+ error_report("Failed to load XBZRLE page - decode error!");
return -1;
}
@@ -1006,7 +1006,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
if (flags & RAM_SAVE_FLAG_CONTINUE) {
if (!block) {
- fprintf(stderr, "Ack, bad migration stream!\n");
+ error_report("Ack, bad migration stream!");
return NULL;
}
@@ -1022,7 +1022,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
return memory_region_get_ram_ptr(block->mr) + offset;
}
- fprintf(stderr, "Can't find block %s!\n", id);
+ error_report("Can't find block %s!", id);
return NULL;
}
@@ -1075,10 +1075,9 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
if (!strncmp(id, block->idstr, sizeof(id))) {
if (block->length != length) {
- fprintf(stderr,
- "Length mismatch: %s: " RAM_ADDR_FMT
- " in != " RAM_ADDR_FMT "\n", id, length,
- block->length);
+ error_report("Length mismatch: %s: " RAM_ADDR_FMT
+ " in != " RAM_ADDR_FMT, id, length,
+ block->length);
ret = -EINVAL;
goto done;
}
@@ -1087,8 +1086,8 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
}
if (!block) {
- fprintf(stderr, "Unknown ramblock \"%s\", cannot "
- "accept migration\n", id);
+ error_report("Unknown ramblock \"%s\", cannot "
+ "accept migration", id);
ret = -EINVAL;
goto done;
}
@@ -1243,12 +1242,11 @@ void select_soundhw(const char *optarg)
if (!c->name) {
if (l > 80) {
- fprintf(stderr,
- "Unknown sound card name (too big to show)\n");
+ error_report("Unknown sound card name (too big to show)");
}
else {
- fprintf(stderr, "Unknown sound card name `%.*s'\n",
- (int) l, p);
+ error_report("Unknown sound card name `%.*s'",
+ (int) l, p);
}
bad_card = 1;
}
@@ -1271,13 +1269,13 @@ void audio_init(void)
if (c->enabled) {
if (c->isa) {
if (!isa_bus) {
- fprintf(stderr, "ISA bus not available for %s\n", c->name);
+ error_report("ISA bus not available for %s", c->name);
exit(1);
}
c->init.init_isa(isa_bus);
} else {
if (!pci_bus) {
- fprintf(stderr, "PCI bus not available for %s\n", c->name);
+ error_report("PCI bus not available for %s", c->name);
exit(1);
}
c->init.init_pci(pci_bus);
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index fceee50adb..7b79bedca2 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -105,7 +105,7 @@ static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
samples = (bytes - rate->bytes_sent) >> info->shift;
if (samples < 0 || samples > 65536) {
- fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples);
+ error_report("Resetting rate control (%" PRId64 " samples)", samples);
rate_start (rate);
samples = 0;
}
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
index 9d94623225..6f6d792691 100644
--- a/audio/wavcapture.c
+++ b/audio/wavcapture.c
@@ -63,8 +63,7 @@ static void wav_destroy (void *opaque)
}
doclose:
if (fclose (wav->f)) {
- fprintf (stderr, "wav_destroy: fclose failed: %s",
- strerror (errno));
+ error_report("wav_destroy: fclose failed: %s", strerror(errno));
}
}
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 922cf5657b..b60b66d66c 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -27,8 +27,8 @@ static void nbd_accept(void *opaque)
socklen_t addr_len = sizeof(addr);
int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
- if (fd >= 0) {
- nbd_client_new(NULL, fd, nbd_client_put);
+ if (fd >= 0 && !nbd_client_new(NULL, fd, nbd_client_put)) {
+ close(fd);
}
}
diff --git a/bsd-user/bsdload.c b/bsd-user/bsdload.c
index 2abc7136e0..6b52e08720 100644
--- a/bsd-user/bsdload.c
+++ b/bsd-user/bsdload.c
@@ -183,7 +183,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
&& bprm.buf[3] == 'F') {
retval = load_elf_binary(&bprm,regs,infop);
} else {
- fprintf(stderr, "Unknown binary format\n");
+ error_report("Unknown binary format");
return -1;
}
}
diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c
index 93fd9e4259..95652b1887 100644
--- a/bsd-user/elfload.c
+++ b/bsd-user/elfload.c
@@ -628,7 +628,7 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
while (argc-- > 0) {
tmp = argv[argc];
if (!tmp) {
- fprintf(stderr, "VFS: argc is wrong");
+ error_report("VFS: argc is wrong");
exit(-1);
}
tmp1 = tmp;
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 4ba61da896..de74d17cde 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -378,8 +378,8 @@ void cpu_loop(CPUX86State *env)
#endif
default:
pc = env->segs[R_CS].base + env->eip;
- fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
- (long)pc, trapnr);
+ error_report("qemu: 0x%08lx: unhandled CPU exception 0x%x"
+ " - aborting", (long)pc, trapnr);
abort();
}
process_pending_signals(env);
@@ -752,7 +752,7 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM);
if ((envlist = envlist_create()) == NULL) {
- (void) fprintf(stderr, "Unable to allocate envlist\n");
+ error_report("Unable to allocate envlist");
exit(1);
}
@@ -794,7 +794,7 @@ int main(int argc, char **argv)
} else if (!strcmp(r, "ignore-environment")) {
envlist_free(envlist);
if ((envlist = envlist_create()) == NULL) {
- (void) fprintf(stderr, "Unable to allocate envlist\n");
+ error_report("Unable to allocate envlist");
exit(1);
}
} else if (!strcmp(r, "U")) {
@@ -816,7 +816,7 @@ int main(int argc, char **argv)
qemu_host_page_size = atoi(argv[optind++]);
if (qemu_host_page_size == 0 ||
(qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
- fprintf(stderr, "page size must be a power of two\n");
+ error_report("page size must be a power of two");
exit(1);
}
} else if (!strcmp(r, "g")) {
@@ -910,7 +910,7 @@ int main(int argc, char **argv)
qemu_host_page_size */
env = cpu_init(cpu_model);
if (!env) {
- fprintf(stderr, "Unable to find CPU definition\n");
+ error_report("Unable to find CPU definition");
exit(1);
}
cpu = ENV_GET_CPU(env);
@@ -1012,7 +1012,7 @@ int main(int argc, char **argv)
#ifndef TARGET_ABI32
/* enable 64 bit mode if possible */
if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) {
- fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n");
+ error_report("The selected x86 CPU does not support 64 bit mode");
exit(1);
}
env->cr[4] |= CR4_PAE_MASK;
diff --git a/configure b/configure
index 605a0ece0c..0e516f9e2a 100755
--- a/configure
+++ b/configure
@@ -2,26 +2,28 @@
#
# qemu configure script (c) 2003 Fabrice Bellard
#
-# set temporary file name
-if test ! -z "$TMPDIR" ; then
- TMPDIR1="${TMPDIR}"
-elif test ! -z "$TEMPDIR" ; then
- TMPDIR1="${TEMPDIR}"
-else
- TMPDIR1="/tmp"
+
+# Temporary directory used for files created while
+# configure runs. Since it is in the build directory
+# we can safely blow away any previous version of it
+# (and we need not jump through hoops to try to delete
+# it when configure exits.)
+TMPDIR1="config-temp"
+rm -rf "${TMPDIR1}"
+mkdir -p "${TMPDIR1}"
+if [ $? -ne 0 ]; then
+ echo "ERROR: failed to create temporary directory"
+ exit 1
fi
-TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
-TMPB="qemu-conf-${RANDOM}-$$-${RANDOM}"
+TMPB="qemu-conf"
+TMPC="${TMPDIR1}/${TMPB}.c"
TMPO="${TMPDIR1}/${TMPB}.o"
TMPCXX="${TMPDIR1}/${TMPB}.cxx"
TMPL="${TMPDIR1}/${TMPB}.lo"
TMPA="${TMPDIR1}/lib${TMPB}.la"
-TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe"
+TMPE="${TMPDIR1}/${TMPB}.exe"
-# NB: do not call "exit" in the trap handler; this is buggy with some shells;
-# see <1285349658-3122-1-git-send-email-loic.minier@linaro.org>
-trap "rm -f $TMPC $TMPO $TMPCXX $TMPE" EXIT INT QUIT TERM
rm -f config.log
# Print a helpful header at the top of config.log
@@ -317,7 +319,7 @@ glusterfs_discard="no"
glusterfs_zerofill="no"
virtio_blk_data_plane=""
gtk=""
-gtkabi="2.0"
+gtkabi=""
vte=""
tpm="no"
libssh2=""
@@ -1970,6 +1972,18 @@ fi
##########################################
# GTK probe
+if test "$gtkabi" = ""; then
+ # The GTK ABI was not specified explicitly, so try whether 2.0 is available.
+ # Use 3.0 as a fallback if that is available.
+ if $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then
+ gtkabi=2.0
+ elif $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then
+ gtkabi=3.0
+ else
+ gtkabi=2.0
+ fi
+fi
+
if test "$gtk" != "no"; then
gtkpackage="gtk+-$gtkabi"
if test "$gtkabi" = "3.0" ; then
@@ -1983,7 +1997,7 @@ if test "$gtk" != "no"; then
libs_softmmu="$gtk_libs $libs_softmmu"
gtk="yes"
elif test "$gtk" = "yes"; then
- feature_not_found "gtk" "Install gtk2 or gtk3 (requires --with-gtkabi=3.0 option to configure) devel"
+ feature_not_found "gtk" "Install gtk2 or gtk3 devel"
else
gtk="no"
fi
@@ -2006,7 +2020,11 @@ if test "$vte" != "no"; then
libs_softmmu="$vte_libs $libs_softmmu"
vte="yes"
elif test "$vte" = "yes"; then
- feature_not_found "vte" "Install libvte or libvte-2.90 (requires --with-gtkabi=3.0 option to configure) devel"
+ if test "$gtkabi" = "3.0"; then
+ feature_not_found "vte" "Install libvte-2.90 devel"
+ else
+ feature_not_found "vte" "Install libvte devel"
+ fi
else
vte="no"
fi
@@ -4029,11 +4047,14 @@ fi
if test "$pie" = "no" ; then
textseg_addr=
case "$cpu" in
- arm | hppa | i386 | m68k | ppc | ppc64 | s390* | sparc | sparc64 | x86_64 | x32)
+ arm | i386 | ppc* | s390* | sparc* | x86_64 | x32)
+ # ??? Rationale for choosing this address
textseg_addr=0x60000000
;;
mips)
- textseg_addr=0x400000
+ # A 256M aligned address, high in the address space, with enough
+ # room for the code_gen_buffer above it before the stack.
+ textseg_addr=0x60000000
;;
esac
if [ -n "$textseg_addr" ]; then
@@ -5219,3 +5240,4 @@ printf " '%s'" "$0" "$@" >>config.status
echo >>config.status
chmod +x config.status
+rm -r "$TMPDIR1"
diff --git a/dma-helpers.c b/dma-helpers.c
index 5f421e9814..53cbe925d1 100644
--- a/dma-helpers.c
+++ b/dma-helpers.c
@@ -143,12 +143,12 @@ static void dma_bdrv_cb(void *opaque, int ret)
dbs->acb = NULL;
dbs->sector_num += dbs->iov.size / 512;
- dma_bdrv_unmap(dbs);
if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) {
dma_complete(dbs, ret);
return;
}
+ dma_bdrv_unmap(dbs);
while (dbs->sg_cur_index < dbs->sg->nsg) {
cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte;
diff --git a/docs/multiseat.txt b/docs/multiseat.txt
new file mode 100644
index 0000000000..a6c71dd74f
--- /dev/null
+++ b/docs/multiseat.txt
@@ -0,0 +1,76 @@
+
+multiseat howto (with some multihead coverage)
+==============================================
+
+host side
+---------
+
+First you must compile qemu with a user interface supporting
+multihead/multiseat and input event routing. Right now this list is
+pretty short: sdl2.
+
+ ./configure --enable-sdl --with-sdlabi=2.0
+
+
+Next put together the qemu command line:
+
+qemu -enable-kvm -usb $memory $disk $whatever \
+ -display sdl \
+ -vga std \
+ -device usb-tablet
+
+That is it for the first head, which will use the standard vga, the
+standard ps/2 keyboard (implicitly there) and the usb-tablet. Now the
+additional switches for the second head:
+
+ -device pci-bridge,addr=12.0,chassis_nr=2,id=head.2 \
+ -device secondary-vga,bus=head.2,addr=02.0,id=video.2 \
+ -device nec-usb-xhci,bus=head.2,addr=0f.0,id=usb.2 \
+ -device usb-kbd,bus=usb.2.0,port=1,display=video.2 \
+ -device usb-tablet,bus=usb.2.0,port=2,display=video.2
+
+This places a pci bridge in slot 12, connects a display adapter and
+xhci (usb) controller to the bridge. Then it adds a usb keyboard and
+usb mouse, both connected to the xhci and linked to the display.
+
+The "display=video2" sets up the input routing. Any input coming from
+the window which belongs to the video.2 display adapter will be routed
+to these input devices.
+
+
+guest side
+----------
+
+You need a pretty recent linux guest. systemd with loginctl. kernel
+3.14+ with CONFIG_DRM_BOCHS enabled. Fedora 20 will do. Must be
+fully updated for the new kernel though, i.e. the live iso doesn't cut
+it.
+
+Now we'll have to configure the guest. Boot and login. By default
+all devices belong to seat0. You can use "loginctl seat-status seat0"
+to list them all (and to get the sysfs paths for cut+paste). Now
+we'll go assign all pci devices connected the pci bridge in slot 12 to
+a new head:
+
+loginctl attach seat-qemu \
+ /sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/drm/card1
+loginctl attach seat-qemu \
+ /sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/graphics/fb1
+loginctl attach seat-qemu \
+ /sys/devices/pci0000:00/0000:00:12.0/0000:01:0f.0/usb2
+
+Use "loginctl seat-status seat-qemu" to check the result. It isn't
+needed to assign the usb devices to the head individually, assigning a
+usb (root) hub will automatically assign all usb devices connected to
+it too.
+
+BTW: loginctl writes udev rules to /etc/udev/rules.d to make these
+device assignments permanent, so you need to do this only once.
+
+Now simply restart gdm (rebooting will do too), and a login screen
+should show up on the second head.
+
+Enjoy!
+
+--
+Gerd Hoffmann <kraxel@redhat.com>
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
index e9bb005413..12b1707cb2 100644
--- a/hw/display/jazz_led.c
+++ b/hw/display/jazz_led.c
@@ -173,6 +173,7 @@ static void jazz_led_update_display(void *opaque)
case 16:
color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa);
color_led = rgb_to_pixel16(0x00, 0xff, 0x00);
+ break;
case 24:
color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa);
color_led = rgb_to_pixel24(0x00, 0xff, 0x00);
diff --git a/hw/input/hid.c b/hw/input/hid.c
index bb0fa6a619..295bdab652 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -105,70 +105,135 @@ void hid_set_next_idle(HIDState *hs)
}
}
-static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
+static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
+ InputEvent *evt)
{
- e->xdx = e->ydy = e->dz = 0;
- e->buttons_state = buttons;
-}
+ static const int bmap[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = 0x01,
+ [INPUT_BUTTON_RIGHT] = 0x02,
+ [INPUT_BUTTON_MIDDLE] = 0x04,
+ };
+ HIDState *hs = (HIDState *)dev;
+ HIDPointerEvent *e;
-static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
- int x1, int y1, int z1) {
- if (xyrel) {
- e->xdx += x1;
- e->ydy += y1;
- } else {
- e->xdx = x1;
- e->ydy = y1;
- /* Windows drivers do not like the 0/0 position and ignore such
- * events. */
- if (!(x1 | y1)) {
- e->xdx = 1;
+ assert(hs->n < QUEUE_LENGTH);
+ e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
+
+ switch (evt->kind) {
+ case INPUT_EVENT_KIND_REL:
+ if (evt->rel->axis == INPUT_AXIS_X) {
+ e->xdx += evt->rel->value;
+ } else if (evt->rel->axis == INPUT_AXIS_Y) {
+ e->ydy -= evt->rel->value;
+ }
+ break;
+
+ case INPUT_EVENT_KIND_ABS:
+ if (evt->rel->axis == INPUT_AXIS_X) {
+ e->xdx = evt->rel->value;
+ } else if (evt->rel->axis == INPUT_AXIS_Y) {
+ e->ydy = evt->rel->value;
}
+ break;
+
+ case INPUT_EVENT_KIND_BTN:
+ if (evt->btn->down) {
+ e->buttons_state |= bmap[evt->btn->button];
+ if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+ e->dz--;
+ } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+ e->dz++;
+ }
+ } else {
+ e->buttons_state &= ~bmap[evt->btn->button];
+ }
+ break;
+
+ default:
+ /* keep gcc happy */
+ break;
}
- e->dz += z1;
+
}
-static void hid_pointer_event(void *opaque,
- int x1, int y1, int z1, int buttons_state)
+static void hid_pointer_sync(DeviceState *dev)
{
- HIDState *hs = opaque;
- unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
- unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
-
- /* We combine events where feasible to keep the queue small. We shouldn't
- * combine anything with the first event of a particular button state, as
- * that would change the location of the button state change. When the
- * queue is empty, a second event is needed because we don't know if
- * the first event changed the button state. */
- if (hs->n == QUEUE_LENGTH) {
- /* Queue full. Discard old button state, combine motion normally. */
- hs->ptr.queue[use_slot].buttons_state = buttons_state;
- } else if (hs->n < 2 ||
- hs->ptr.queue[use_slot].buttons_state != buttons_state ||
- hs->ptr.queue[previous_slot].buttons_state !=
- hs->ptr.queue[use_slot].buttons_state) {
- /* Cannot or should not combine, so add an empty item to the queue. */
- QUEUE_INCR(use_slot);
+ HIDState *hs = (HIDState *)dev;
+ HIDPointerEvent *prev, *curr, *next;
+ bool event_compression = false;
+
+ if (hs->n == QUEUE_LENGTH-1) {
+ /*
+ * Queue full. We are loosing information, but we at least
+ * keep track of most recent button state.
+ */
+ return;
+ }
+
+ prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
+ curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
+ next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
+
+ if (hs->n > 0) {
+ /*
+ * No button state change between previous and current event
+ * (and previous wasn't seen by the guest yet), so there is
+ * motion information only and we can combine the two event
+ * into one.
+ */
+ if (curr->buttons_state == prev->buttons_state) {
+ event_compression = true;
+ }
+ }
+
+ if (event_compression) {
+ /* add current motion to previous, clear current */
+ if (hs->kind == HID_MOUSE) {
+ prev->xdx += curr->xdx;
+ curr->xdx = 0;
+ prev->ydy -= curr->ydy;
+ curr->ydy = 0;
+ } else {
+ prev->xdx = curr->xdx;
+ prev->ydy = curr->ydy;
+ }
+ prev->dz += curr->dz;
+ curr->dz = 0;
+ } else {
+ /* prepate next (clear rel, copy abs + btns) */
+ if (hs->kind == HID_MOUSE) {
+ next->xdx = 0;
+ next->ydy = 0;
+ } else {
+ next->xdx = curr->xdx;
+ next->ydy = curr->ydy;
+ }
+ next->dz = 0;
+ next->buttons_state = curr->buttons_state;
+ /* make current guest visible, notify guest */
hs->n++;
- hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
+ hs->event(hs);
}
- hid_pointer_event_combine(&hs->ptr.queue[use_slot],
- hs->kind == HID_MOUSE,
- x1, y1, z1);
- hs->event(hs);
}
-static void hid_keyboard_event(void *opaque, int keycode)
+static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
+ InputEvent *evt)
{
- HIDState *hs = opaque;
+ HIDState *hs = (HIDState *)dev;
+ int scancodes[3], i, count;
int slot;
- if (hs->n == QUEUE_LENGTH) {
+ count = qemu_input_key_value_to_scancode(evt->key->key,
+ evt->key->down,
+ scancodes);
+ if (hs->n + count > QUEUE_LENGTH) {
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
return;
}
- slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
- hs->kbd.keycodes[slot] = keycode;
+ for (i = 0; i < count; i++) {
+ slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
+ hs->kbd.keycodes[slot] = scancodes[i];
+ }
hs->event(hs);
}
@@ -247,14 +312,14 @@ static inline int int_clamp(int val, int vmin, int vmax)
void hid_pointer_activate(HIDState *hs)
{
if (!hs->ptr.mouse_grabbed) {
- qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
+ qemu_input_handler_activate(hs->s);
hs->ptr.mouse_grabbed = 1;
}
}
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
{
- int dx, dy, dz, b, l;
+ int dx, dy, dz, l;
int index;
HIDPointerEvent *e;
@@ -279,17 +344,6 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
dz = int_clamp(e->dz, -127, 127);
e->dz -= dz;
- b = 0;
- if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
- b |= 0x01;
- }
- if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
- b |= 0x02;
- }
- if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
- b |= 0x04;
- }
-
if (hs->n &&
!e->dz &&
(hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
@@ -304,7 +358,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
switch (hs->kind) {
case HID_MOUSE:
if (len > l) {
- buf[l++] = b;
+ buf[l++] = e->buttons_state;
}
if (len > l) {
buf[l++] = dx;
@@ -319,7 +373,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
case HID_TABLET:
if (len > l) {
- buf[l++] = b;
+ buf[l++] = e->buttons_state;
}
if (len > l) {
buf[l++] = dx & 0xff;
@@ -413,31 +467,45 @@ void hid_reset(HIDState *hs)
void hid_free(HIDState *hs)
{
- switch (hs->kind) {
- case HID_KEYBOARD:
- qemu_remove_kbd_event_handler(hs->kbd.eh_entry);
- break;
- case HID_MOUSE:
- case HID_TABLET:
- qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
- break;
- }
+ qemu_input_handler_unregister(hs->s);
hid_del_idle_timer(hs);
}
+static QemuInputHandler hid_keyboard_handler = {
+ .name = "QEMU HID Keyboard",
+ .mask = INPUT_EVENT_MASK_KEY,
+ .event = hid_keyboard_event,
+};
+
+static QemuInputHandler hid_mouse_handler = {
+ .name = "QEMU HID Mouse",
+ .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
+ .event = hid_pointer_event,
+ .sync = hid_pointer_sync,
+};
+
+static QemuInputHandler hid_tablet_handler = {
+ .name = "QEMU HID Tablet",
+ .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
+ .event = hid_pointer_event,
+ .sync = hid_pointer_sync,
+};
+
void hid_init(HIDState *hs, int kind, HIDEventFunc event)
{
hs->kind = kind;
hs->event = event;
if (hs->kind == HID_KEYBOARD) {
- hs->kbd.eh_entry = qemu_add_kbd_event_handler(hid_keyboard_event, hs);
+ hs->s = qemu_input_handler_register((DeviceState *)hs,
+ &hid_keyboard_handler);
+ qemu_input_handler_activate(hs->s);
} else if (hs->kind == HID_MOUSE) {
- hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
- 0, "QEMU HID Mouse");
+ hs->s = qemu_input_handler_register((DeviceState *)hs,
+ &hid_mouse_handler);
} else if (hs->kind == HID_TABLET) {
- hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
- 1, "QEMU HID Tablet");
+ hs->s = qemu_input_handler_register((DeviceState *)hs,
+ &hid_tablet_handler);
}
}
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index f6743659f7..979e532fdf 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -29,7 +29,6 @@ obj-$(CONFIG_NSERIES) += cbus.o
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
obj-$(CONFIG_IMX) += imx_ccm.o
-obj-$(CONFIG_LM32) += lm32_sys.o
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
obj-$(CONFIG_MAINSTONE) += mst_fpga.o
diff --git a/hw/misc/lm32_sys.c b/hw/misc/lm32_sys.c
deleted file mode 100644
index 778eb6e042..0000000000
--- a/hw/misc/lm32_sys.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * QEMU model of the LatticeMico32 system control block.
- *
- * Copyright (c) 2010 Michael Walle <michael@walle.cc>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * This model is mainly intended for testing purposes and doesn't fit to any
- * real hardware. On the one hand it provides a control register (R_CTRL) on
- * the other hand it supports the lm32 tests.
- *
- * A write to the control register causes a system shutdown.
- * Tests first write the pointer to a test name to the test name register
- * (R_TESTNAME) and then write a zero to the pass/fail register (R_PASSFAIL) if
- * the test is passed or any non-zero value to it if the test is failed.
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "trace.h"
-#include "qemu/log.h"
-#include "qemu/error-report.h"
-#include "sysemu/sysemu.h"
-
-enum {
- R_CTRL = 0,
- R_PASSFAIL,
- R_TESTNAME,
- R_MAX
-};
-
-#define MAX_TESTNAME_LEN 32
-
-#define TYPE_LM32_SYS "lm32-sys"
-#define LM32_SYS(obj) OBJECT_CHECK(LM32SysState, (obj), TYPE_LM32_SYS)
-
-struct LM32SysState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint32_t base;
- uint32_t regs[R_MAX];
- uint8_t testname[MAX_TESTNAME_LEN];
-};
-typedef struct LM32SysState LM32SysState;
-
-static void copy_testname(LM32SysState *s)
-{
- cpu_physical_memory_read(s->regs[R_TESTNAME], s->testname,
- MAX_TESTNAME_LEN);
- s->testname[MAX_TESTNAME_LEN - 1] = '\0';
-}
-
-static void sys_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- LM32SysState *s = opaque;
- char *testname;
-
- trace_lm32_sys_memory_write(addr, value);
-
- addr >>= 2;
- switch (addr) {
- case R_CTRL:
- qemu_system_shutdown_request();
- break;
- case R_PASSFAIL:
- s->regs[addr] = value;
- testname = (char *)s->testname;
- fprintf(stderr, "TC %-*s %s\n", MAX_TESTNAME_LEN,
- testname, (value) ? "FAILED" : "OK");
- if (value) {
- cpu_dump_state(qemu_get_cpu(0), stderr, fprintf, 0);
- }
- break;
- case R_TESTNAME:
- s->regs[addr] = value;
- copy_testname(s);
- break;
-
- default:
- error_report("lm32_sys: write access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
-}
-
-static bool sys_ops_accepts(void *opaque, hwaddr addr,
- unsigned size, bool is_write)
-{
- return is_write && size == 4;
-}
-
-static const MemoryRegionOps sys_ops = {
- .write = sys_write,
- .valid.accepts = sys_ops_accepts,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void sys_reset(DeviceState *d)
-{
- LM32SysState *s = LM32_SYS(d);
- int i;
-
- for (i = 0; i < R_MAX; i++) {
- s->regs[i] = 0;
- }
- memset(s->testname, 0, MAX_TESTNAME_LEN);
-}
-
-static int lm32_sys_init(SysBusDevice *dev)
-{
- LM32SysState *s = LM32_SYS(dev);
-
- memory_region_init_io(&s->iomem, OBJECT(dev), &sys_ops , s,
- "sys", R_MAX * 4);
- sysbus_init_mmio(dev, &s->iomem);
-
- /* Note: This device is not created in the board initialization,
- * instead it has to be added with the -device parameter. Therefore,
- * the device maps itself. */
- sysbus_mmio_map(dev, 0, s->base);
-
- return 0;
-}
-
-static const VMStateDescription vmstate_lm32_sys = {
- .name = "lm32-sys",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, LM32SysState, R_MAX),
- VMSTATE_BUFFER(testname, LM32SysState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static Property lm32_sys_properties[] = {
- DEFINE_PROP_UINT32("base", LM32SysState, base, 0xffff0000),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void lm32_sys_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = lm32_sys_init;
- dc->reset = sys_reset;
- dc->vmsd = &vmstate_lm32_sys;
- dc->props = lm32_sys_properties;
-}
-
-static const TypeInfo lm32_sys_info = {
- .name = TYPE_LM32_SYS,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(LM32SysState),
- .class_init = lm32_sys_class_init,
-};
-
-static void lm32_sys_register_types(void)
-{
- type_register_static(&lm32_sys_info);
-}
-
-type_init(lm32_sys_register_types)
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 47e70381fe..a26861e2ae 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1,5 +1,5 @@
/*
- * QEMU Xilinx GEM emulation
+ * QEMU Cadence GEM emulation
*
* Copyright (c) 2011 Xilinx, Inc.
*
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 22fe5eec36..8d6a8d4e74 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -605,13 +605,13 @@ PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr)
int dom, bus;
unsigned slot;
- assert(!root->parent_dev);
-
if (!root) {
fprintf(stderr, "No primary PCI bus\n");
return NULL;
}
+ assert(!root->parent_dev);
+
if (!devaddr) {
*devfnp = -1;
return pci_find_bus_nr(root, 0);
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index d097d937ea..67a57f1dcd 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -47,6 +47,8 @@ typedef struct USBHIDState {
USBEndpoint *intr;
HIDState hid;
uint32_t usb_version;
+ char *display;
+ uint32_t head;
} USBHIDState;
enum {
@@ -574,6 +576,9 @@ static int usb_hid_initfn(USBDevice *dev, int kind)
usb_desc_init(dev);
us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
hid_init(&us->hid, kind, usb_hid_changed);
+ if (us->display && us->hid.s) {
+ qemu_input_handler_bind(us->hid.s, us->display, us->head, NULL);
+ }
return 0;
}
@@ -653,6 +658,8 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data)
static Property usb_tablet_properties[] = {
DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
+ DEFINE_PROP_STRING("display", USBHIDState, display),
+ DEFINE_PROP_UINT32("head", USBHIDState, head, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -696,6 +703,11 @@ static const TypeInfo usb_mouse_info = {
.class_init = usb_mouse_class_initfn,
};
+static Property usb_keyboard_properties[] = {
+ DEFINE_PROP_STRING("display", USBHIDState, display),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -706,6 +718,7 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
uc->product_desc = "QEMU USB Keyboard";
uc->usb_desc = &desc_keyboard;
dc->vmsd = &vmstate_usb_kbd;
+ dc->props = usb_keyboard_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 943f930404..380b465621 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -46,6 +46,7 @@ enum mtp_code {
/* response codes */
RES_OK = 0x2001,
+ RES_GENERAL_ERROR = 0x2002,
RES_SESSION_NOT_OPEN = 0x2003,
RES_INVALID_TRANSACTION_ID = 0x2004,
RES_OPERATION_NOT_SUPPORTED = 0x2005,
@@ -109,7 +110,8 @@ struct MTPObject {
struct stat stat;
MTPObject *parent;
MTPObject **children;
- int32_t nchildren;
+ uint32_t nchildren;
+ bool have_children;
QTAILQ_ENTRY(MTPObject) next;
};
@@ -273,7 +275,6 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
o->handle = handle;
o->parent = parent;
o->name = g_strdup(name);
- o->nchildren = -1;
if (parent == NULL) {
o->path = g_strdup(name);
} else {
@@ -340,7 +341,11 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
struct dirent *entry;
DIR *dir;
- o->nchildren = 0;
+ if (o->have_children) {
+ return;
+ }
+ o->have_children = true;
+
dir = opendir(o->path);
if (!dir) {
return;
@@ -698,7 +703,10 @@ static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c,
if (offset > o->stat.st_size) {
offset = o->stat.st_size;
}
- lseek(d->fd, offset, SEEK_SET);
+ if (lseek(d->fd, offset, SEEK_SET) < 0) {
+ usb_mtp_data_free(d);
+ return NULL;
+ }
d->length = c->argv[2];
if (d->length > o->stat.st_size - offset) {
@@ -789,9 +797,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
c->trans, 0, 0, 0);
return;
}
- if (o->nchildren == -1) {
- usb_mtp_object_readdir(s, o);
- }
+ usb_mtp_object_readdir(s, o);
if (c->code == CMD_GET_NUM_OBJECTS) {
trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path);
nres = 1;
@@ -823,7 +829,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
}
data_in = usb_mtp_get_object(s, c, o);
if (NULL == data_in) {
- fprintf(stderr, "%s: TODO: handle error\n", __func__);
+ usb_mtp_queue_result(s, RES_GENERAL_ERROR,
+ c->trans, 0, 0, 0);
+ return;
}
break;
case CMD_GET_PARTIAL_OBJECT:
@@ -840,7 +848,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
}
data_in = usb_mtp_get_partial_object(s, c, o);
if (NULL == data_in) {
- fprintf(stderr, "%s: TODO: handle error\n", __func__);
+ usb_mtp_queue_result(s, RES_GENERAL_ERROR,
+ c->trans, 0, 0, 0);
+ return;
}
nres = 1;
res0 = data_in->length;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index ef3177aee9..54dea16009 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -621,6 +621,11 @@ static const char *ep_state_name(uint32_t state)
ARRAY_SIZE(ep_state_names));
}
+static bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit)
+{
+ return xhci->flags & (1 << bit);
+}
+
static uint64_t xhci_mfindex_get(XHCIState *xhci)
{
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@@ -3435,7 +3440,7 @@ static void xhci_child_detach(USBPort *uport, USBDevice *child)
USBBus *bus = usb_bus_from_device(child);
XHCIState *xhci = container_of(bus, XHCIState, bus);
- xhci_detach_slot(xhci, uport);
+ xhci_detach_slot(xhci, child->port);
}
static USBPortOps xhci_uport_ops = {
@@ -3594,13 +3599,15 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
&xhci->mem);
- ret = pcie_endpoint_cap_init(dev, 0xa0);
- assert(ret >= 0);
+ if (pci_bus_is_express(dev->bus)) {
+ ret = pcie_endpoint_cap_init(dev, 0xa0);
+ assert(ret >= 0);
+ }
- if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
+ if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) {
msi_init(dev, 0x70, xhci->numintrs, true, false);
}
- if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) {
+ if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) {
msix_init(dev, xhci->numintrs,
&xhci->mem, 0, OFF_MSIX_TABLE,
&xhci->mem, 0, OFF_MSIX_PBA,
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index 57bed09a1e..8007d1d156 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -720,6 +720,9 @@ static void usb_host_ep_update(USBHostDevice *s)
struct libusb_config_descriptor *conf;
const struct libusb_interface_descriptor *intf;
const struct libusb_endpoint_descriptor *endp;
+#if LIBUSBX_API_VERSION >= 0x01000103
+ struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
+#endif
uint8_t devep, type;
int pid, ep;
int rc, i, e;
@@ -765,6 +768,15 @@ static void usb_host_ep_update(USBHostDevice *s)
usb_ep_set_type(udev, pid, ep, type);
usb_ep_set_ifnum(udev, pid, ep, i);
usb_ep_set_halted(udev, pid, ep, 0);
+#if LIBUSBX_API_VERSION >= 0x01000103
+ if (type == LIBUSB_TRANSFER_TYPE_BULK &&
+ libusb_get_ss_endpoint_companion_descriptor(ctx, endp,
+ &endp_ss_comp) == LIBUSB_SUCCESS) {
+ usb_ep_set_max_streams(udev, pid, ep,
+ endp_ss_comp->bmAttributes);
+ libusb_free_ss_endpoint_companion_descriptor(endp_ss_comp);
+ }
+#endif
}
}
@@ -1202,10 +1214,23 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
usb_packet_copy(p, r->buffer, size);
}
ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
- libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
- r->buffer, size,
- usb_host_req_complete_data, r,
- BULK_TIMEOUT);
+ if (p->stream) {
+#if LIBUSBX_API_VERSION >= 0x01000103
+ libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream,
+ r->buffer, size,
+ usb_host_req_complete_data, r,
+ BULK_TIMEOUT);
+#else
+ usb_host_req_free(r);
+ p->status = USB_RET_STALL;
+ return;
+#endif
+ } else {
+ libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
+ r->buffer, size,
+ usb_host_req_complete_data, r,
+ BULK_TIMEOUT);
+ }
break;
case USB_ENDPOINT_XFER_INT:
r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size);
@@ -1268,6 +1293,54 @@ static void usb_host_handle_reset(USBDevice *udev)
}
}
+static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps,
+ int nr_eps, int streams)
+{
+#if LIBUSBX_API_VERSION >= 0x01000103
+ USBHostDevice *s = USB_HOST_DEVICE(udev);
+ unsigned char endpoints[30];
+ int i, rc;
+
+ for (i = 0; i < nr_eps; i++) {
+ endpoints[i] = eps[i]->nr;
+ if (eps[i]->pid == USB_TOKEN_IN) {
+ endpoints[i] |= 0x80;
+ }
+ }
+ rc = libusb_alloc_streams(s->dh, streams, endpoints, nr_eps);
+ if (rc < 0) {
+ usb_host_libusb_error("libusb_alloc_streams", rc);
+ } else if (rc != streams) {
+ fprintf(stderr,
+ "libusb_alloc_streams: got less streams then requested %d < %d\n",
+ rc, streams);
+ }
+
+ return (rc == streams) ? 0 : -1;
+#else
+ fprintf(stderr, "libusb_alloc_streams: error not implemented\n");
+ return -1;
+#endif
+}
+
+static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps,
+ int nr_eps)
+{
+#if LIBUSBX_API_VERSION >= 0x01000103
+ USBHostDevice *s = USB_HOST_DEVICE(udev);
+ unsigned char endpoints[30];
+ int i;
+
+ for (i = 0; i < nr_eps; i++) {
+ endpoints[i] = eps[i]->nr;
+ if (eps[i]->pid == USB_TOKEN_IN) {
+ endpoints[i] |= 0x80;
+ }
+ }
+ libusb_free_streams(s->dh, endpoints, nr_eps);
+#endif
+}
+
/*
* This is *NOT* about restoring state. We have absolutely no idea
* what state the host device is in at the moment and whenever it is
@@ -1349,6 +1422,8 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data)
uc->handle_reset = usb_host_handle_reset;
uc->handle_destroy = usb_host_handle_destroy;
uc->flush_ep_queue = usb_host_flush_ep_queue;
+ uc->alloc_streams = usb_host_alloc_streams;
+ uc->free_streams = usb_host_free_streams;
dc->vmsd = &vmstate_usb_host;
dc->props = usb_host_dev_properties;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 287a505b48..4c6187bebd 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -50,6 +50,10 @@
((i) & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, \
(i) & 0x0f))
+#ifndef USBREDIR_VERSION /* This is not defined in older usbredir versions */
+#define USBREDIR_VERSION 0
+#endif
+
typedef struct USBRedirDevice USBRedirDevice;
/* Struct to hold buffered packets */
@@ -68,6 +72,7 @@ struct endp_data {
uint8_t interval;
uint8_t interface; /* bInterfaceNumber this ep belongs to */
uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */
+ uint32_t max_streams;
uint8_t iso_started;
uint8_t iso_error; /* For reporting iso errors to the HC */
uint8_t interrupt_started;
@@ -106,8 +111,9 @@ struct USBRedirDevice {
int read_buf_size;
/* Active chardev-watch-tag */
guint watch;
- /* For async handling of close */
+ /* For async handling of close / reject */
QEMUBH *chardev_close_bh;
+ QEMUBH *device_reject_bh;
/* To delay the usb attach in case of quick chardev close + open */
QEMUTimer *attach_timer;
int64_t next_attach_time;
@@ -780,11 +786,12 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
dev->endpoint[EP2I(ep)].bulk_receiving_enabled = 0;
}
- DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id);
+ DPRINTF("bulk-out ep %02X stream %u len %zd id %"PRIu64"\n",
+ ep, p->stream, size, p->id);
bulk_packet.endpoint = ep;
bulk_packet.length = size;
- bulk_packet.stream_id = 0;
+ bulk_packet.stream_id = p->stream;
bulk_packet.length_high = size >> 16;
assert(bulk_packet.length_high == 0 ||
usbredirparser_peer_has_cap(dev->parser,
@@ -1091,6 +1098,66 @@ static void usbredir_handle_control(USBDevice *udev, USBPacket *p,
p->status = USB_RET_ASYNC;
}
+static int usbredir_alloc_streams(USBDevice *udev, USBEndpoint **eps,
+ int nr_eps, int streams)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+#if USBREDIR_VERSION >= 0x000700
+ struct usb_redir_alloc_bulk_streams_header alloc_streams;
+ int i;
+
+ if (!usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_bulk_streams)) {
+ ERROR("peer does not support streams\n");
+ goto reject;
+ }
+
+ if (streams == 0) {
+ ERROR("request to allocate 0 streams\n");
+ return -1;
+ }
+
+ alloc_streams.no_streams = streams;
+ alloc_streams.endpoints = 0;
+ for (i = 0; i < nr_eps; i++) {
+ alloc_streams.endpoints |= 1 << USBEP2I(eps[i]);
+ }
+ usbredirparser_send_alloc_bulk_streams(dev->parser, 0, &alloc_streams);
+ usbredirparser_do_write(dev->parser);
+
+ return 0;
+#else
+ ERROR("usbredir_alloc_streams not implemented\n");
+ goto reject;
+#endif
+reject:
+ ERROR("streams are not available, disconnecting\n");
+ qemu_bh_schedule(dev->device_reject_bh);
+ return -1;
+}
+
+static void usbredir_free_streams(USBDevice *udev, USBEndpoint **eps,
+ int nr_eps)
+{
+#if USBREDIR_VERSION >= 0x000700
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ struct usb_redir_free_bulk_streams_header free_streams;
+ int i;
+
+ if (!usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_bulk_streams)) {
+ return;
+ }
+
+ free_streams.endpoints = 0;
+ for (i = 0; i < nr_eps; i++) {
+ free_streams.endpoints |= 1 << USBEP2I(eps[i]);
+ }
+ usbredirparser_send_free_bulk_streams(dev->parser, 0, &free_streams);
+ usbredirparser_do_write(dev->parser);
+#endif
+}
+
/*
* Close events can be triggered by usbredirparser_do_write which gets called
* from within the USBDevice data / control packet callbacks and doing a
@@ -1102,6 +1169,7 @@ static void usbredir_chardev_close_bh(void *opaque)
{
USBRedirDevice *dev = opaque;
+ qemu_bh_cancel(dev->device_reject_bh);
usbredir_device_disconnect(dev);
if (dev->parser) {
@@ -1153,6 +1221,9 @@ static void usbredir_create_parser(USBRedirDevice *dev)
usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
+#if USBREDIR_VERSION >= 0x000700
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+#endif
if (runstate_check(RUN_STATE_INMIGRATE)) {
flags |= usbredirparser_fl_no_hello;
@@ -1171,6 +1242,17 @@ static void usbredir_reject_device(USBRedirDevice *dev)
}
}
+/*
+ * We may need to reject the device when the hcd calls alloc_streams, doing
+ * an usb_detach from within a hcd call is not a good idea, hence this bh.
+ */
+static void usbredir_device_reject_bh(void *opaque)
+{
+ USBRedirDevice *dev = opaque;
+
+ usbredir_reject_device(dev);
+}
+
static void usbredir_do_attach(void *opaque)
{
USBRedirDevice *dev = opaque;
@@ -1297,6 +1379,7 @@ static int usbredir_initfn(USBDevice *udev)
}
dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
+ dev->device_reject_bh = qemu_bh_new(usbredir_device_reject_bh, dev);
dev->attach_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usbredir_do_attach, dev);
packet_id_queue_init(&dev->cancelled, dev, "cancelled");
@@ -1337,6 +1420,7 @@ static void usbredir_handle_destroy(USBDevice *udev)
dev->cs = NULL;
/* Note must be done after qemu_chr_close, as that causes a close event */
qemu_bh_delete(dev->chardev_close_bh);
+ qemu_bh_delete(dev->device_reject_bh);
timer_del(dev->attach_timer);
timer_free(dev->attach_timer);
@@ -1628,6 +1712,7 @@ static void usbredir_setup_usb_eps(USBRedirDevice *dev)
usb_ep->type = dev->endpoint[i].type;
usb_ep->ifnum = dev->endpoint[i].interface;
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
+ usb_ep->max_streams = dev->endpoint[i].max_streams;
usbredir_set_pipeline(dev, usb_ep);
}
}
@@ -1646,6 +1731,12 @@ static void usbredir_ep_info(void *priv,
usb_redir_cap_ep_info_max_packet_size)) {
dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i];
}
+#if USBREDIR_VERSION >= 0x000700
+ if (usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_bulk_streams)) {
+ dev->endpoint[i].max_streams = ep_info->max_streams[i];
+ }
+#endif
switch (dev->endpoint[i].type) {
case usb_redir_type_invalid:
break;
@@ -1779,6 +1870,20 @@ static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
static void usbredir_bulk_streams_status(void *priv, uint64_t id,
struct usb_redir_bulk_streams_status_header *bulk_streams_status)
{
+#if USBREDIR_VERSION >= 0x000700
+ USBRedirDevice *dev = priv;
+
+ if (bulk_streams_status->status == usb_redir_success) {
+ DPRINTF("bulk streams status %d eps %08x\n",
+ bulk_streams_status->status, bulk_streams_status->endpoints);
+ } else {
+ ERROR("bulk streams %s failed status %d eps %08x\n",
+ (bulk_streams_status->no_streams == 0) ? "free" : "alloc",
+ bulk_streams_status->status, bulk_streams_status->endpoints);
+ ERROR("usb-redir-host does not provide streams, disconnecting\n");
+ usbredir_reject_device(dev);
+ }
+#endif
}
static void usbredir_bulk_receiving_status(void *priv, uint64_t id,
@@ -1850,8 +1955,8 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
int len = (bulk_packet->length_high << 16) | bulk_packet->length;
USBPacket *p;
- DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n",
- bulk_packet->status, ep, len, id);
+ DPRINTF("bulk-in status %d ep %02X stream %u len %d id %"PRIu64"\n",
+ bulk_packet->status, ep, bulk_packet->stream_id, len, id);
p = usbredir_find_packet_by_id(dev, ep, id);
if (p) {
@@ -2165,6 +2270,23 @@ static bool usbredir_bulk_receiving_needed(void *priv)
return endp->bulk_receiving_started;
}
+static const VMStateDescription usbredir_stream_vmstate = {
+ .name = "usb-redir-ep/stream-state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(max_streams, struct endp_data),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool usbredir_stream_needed(void *priv)
+{
+ struct endp_data *endp = priv;
+
+ return endp->max_streams;
+}
+
static const VMStateDescription usbredir_ep_vmstate = {
.name = "usb-redir-ep",
.version_id = 1,
@@ -2197,6 +2319,9 @@ static const VMStateDescription usbredir_ep_vmstate = {
.vmsd = &usbredir_bulk_receiving_vmstate,
.needed = usbredir_bulk_receiving_needed,
}, {
+ .vmsd = &usbredir_stream_vmstate,
+ .needed = usbredir_stream_needed,
+ }, {
/* empty */
}
}
@@ -2361,6 +2486,8 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
uc->handle_control = usbredir_handle_control;
uc->flush_ep_queue = usbredir_flush_ep_queue;
uc->ep_stopped = usbredir_ep_stopped;
+ uc->alloc_streams = usbredir_alloc_streams;
+ uc->free_streams = usbredir_free_streams;
dc->vmsd = &usbredir_vmstate;
dc->props = usbredir_properties;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 8bc2eb663e..c964ca4f0b 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -131,7 +131,7 @@ static inline void tlb_flush(CPUState *cpu, int flush_global)
#if defined(__arm__) || defined(_ARCH_PPC) \
|| defined(__x86_64__) || defined(__i386__) \
|| defined(__sparc__) || defined(__aarch64__) \
- || defined(__s390x__) \
+ || defined(__s390x__) || defined(__mips__) \
|| defined(CONFIG_TCG_INTERPRETER)
#define USE_DIRECT_JUMP
#endif
@@ -268,7 +268,7 @@ static inline void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr)
__asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
#endif
}
-#elif defined(__sparc__)
+#elif defined(__sparc__) || defined(__mips__)
void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr);
#else
#error tb_set_jmp_target1 is missing
diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h
index 2567879399..2127c7ce45 100644
--- a/include/hw/input/hid.h
+++ b/include/hw/input/hid.h
@@ -2,6 +2,7 @@
#define QEMU_HID_H
#include "migration/vmstate.h"
+#include "ui/input.h"
#define HID_MOUSE 1
#define HID_TABLET 2
@@ -22,7 +23,6 @@ typedef void (*HIDEventFunc)(HIDState *s);
typedef struct HIDMouseState {
HIDPointerEvent queue[QUEUE_LENGTH];
int mouse_grabbed;
- QEMUPutMouseEntry *eh_entry;
} HIDMouseState;
typedef struct HIDKeyboardState {
@@ -31,7 +31,6 @@ typedef struct HIDKeyboardState {
uint8_t leds;
uint8_t key[16];
int32_t keys;
- QEMUPutKbdEntry *eh_entry;
} HIDKeyboardState;
struct HIDState {
@@ -47,6 +46,7 @@ struct HIDState {
bool idle_pending;
QEMUTimer *idle_timer;
HIDEventFunc event;
+ QemuInputHandlerState *s;
};
void hid_init(HIDState *hs, int kind, HIDEventFunc event);
diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h
index 0f9c6cf15d..78c1ced4e7 100644
--- a/include/qemu/bswap.h
+++ b/include/qemu/bswap.h
@@ -215,9 +215,10 @@ typedef union {
* q: 64 bits
*
* endian is:
- * (empty): host endian
+ * he : host endian
* be : big endian
* le : little endian
+ * (except for byte accesses, which have no endian infix).
*/
static inline int ldub_p(const void *ptr)
@@ -239,82 +240,82 @@ static inline void stb_p(void *ptr, uint8_t v)
operations. Thus we don't need to play games with packed attributes, or
inline byte-by-byte stores. */
-static inline int lduw_p(const void *ptr)
+static inline int lduw_he_p(const void *ptr)
{
uint16_t r;
memcpy(&r, ptr, sizeof(r));
return r;
}
-static inline int ldsw_p(const void *ptr)
+static inline int ldsw_he_p(const void *ptr)
{
int16_t r;
memcpy(&r, ptr, sizeof(r));
return r;
}
-static inline void stw_p(void *ptr, uint16_t v)
+static inline void stw_he_p(void *ptr, uint16_t v)
{
memcpy(ptr, &v, sizeof(v));
}
-static inline int ldl_p(const void *ptr)
+static inline int ldl_he_p(const void *ptr)
{
int32_t r;
memcpy(&r, ptr, sizeof(r));
return r;
}
-static inline void stl_p(void *ptr, uint32_t v)
+static inline void stl_he_p(void *ptr, uint32_t v)
{
memcpy(ptr, &v, sizeof(v));
}
-static inline uint64_t ldq_p(const void *ptr)
+static inline uint64_t ldq_he_p(const void *ptr)
{
uint64_t r;
memcpy(&r, ptr, sizeof(r));
return r;
}
-static inline void stq_p(void *ptr, uint64_t v)
+static inline void stq_he_p(void *ptr, uint64_t v)
{
memcpy(ptr, &v, sizeof(v));
}
static inline int lduw_le_p(const void *ptr)
{
- return (uint16_t)le_bswap(lduw_p(ptr), 16);
+ return (uint16_t)le_bswap(lduw_he_p(ptr), 16);
}
static inline int ldsw_le_p(const void *ptr)
{
- return (int16_t)le_bswap(lduw_p(ptr), 16);
+ return (int16_t)le_bswap(lduw_he_p(ptr), 16);
}
static inline int ldl_le_p(const void *ptr)
{
- return le_bswap(ldl_p(ptr), 32);
+ return le_bswap(ldl_he_p(ptr), 32);
}
static inline uint64_t ldq_le_p(const void *ptr)
{
- return le_bswap(ldq_p(ptr), 64);
+ return le_bswap(ldq_he_p(ptr), 64);
}
static inline void stw_le_p(void *ptr, uint16_t v)
{
- stw_p(ptr, le_bswap(v, 16));
+ stw_he_p(ptr, le_bswap(v, 16));
}
static inline void stl_le_p(void *ptr, uint32_t v)
{
- stl_p(ptr, le_bswap(v, 32));
+ stl_he_p(ptr, le_bswap(v, 32));
}
static inline void stq_le_p(void *ptr, uint64_t v)
{
- stq_p(ptr, le_bswap(v, 64));
+ stq_he_p(ptr, le_bswap(v, 64));
}
/* float access */
@@ -349,37 +350,37 @@ static inline void stfq_le_p(void *ptr, float64 v)
static inline int lduw_be_p(const void *ptr)
{
- return (uint16_t)be_bswap(lduw_p(ptr), 16);
+ return (uint16_t)be_bswap(lduw_he_p(ptr), 16);
}
static inline int ldsw_be_p(const void *ptr)
{
- return (int16_t)be_bswap(lduw_p(ptr), 16);
+ return (int16_t)be_bswap(lduw_he_p(ptr), 16);
}
static inline int ldl_be_p(const void *ptr)
{
- return be_bswap(ldl_p(ptr), 32);
+ return be_bswap(ldl_he_p(ptr), 32);
}
static inline uint64_t ldq_be_p(const void *ptr)
{
- return be_bswap(ldq_p(ptr), 64);
+ return be_bswap(ldq_he_p(ptr), 64);
}
static inline void stw_be_p(void *ptr, uint16_t v)
{
- stw_p(ptr, be_bswap(v, 16));
+ stw_he_p(ptr, be_bswap(v, 16));
}
static inline void stl_be_p(void *ptr, uint32_t v)
{
- stl_p(ptr, be_bswap(v, 32));
+ stl_he_p(ptr, be_bswap(v, 32));
}
static inline void stq_be_p(void *ptr, uint64_t v)
{
- stq_p(ptr, be_bswap(v, 64));
+ stq_he_p(ptr, be_bswap(v, 64));
}
/* float access */
diff --git a/include/ui/console.h b/include/ui/console.h
index 8a866176db..b513e2082d 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -81,6 +81,7 @@ void do_mouse_set(Monitor *mon, const QDict *qdict);
#define QEMU_KEY_CTRL_PAGEUP 0xe406
#define QEMU_KEY_CTRL_PAGEDOWN 0xe407
+void kbd_put_keysym_console(QemuConsole *s, int keysym);
void kbd_put_keysym(int keysym);
/* consoles */
diff --git a/include/ui/input.h b/include/ui/input.h
index 3d3d487f18..aa99b0cac7 100644
--- a/include/ui/input.h
+++ b/include/ui/input.h
@@ -29,6 +29,9 @@ QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev,
void qemu_input_handler_activate(QemuInputHandlerState *s);
void qemu_input_handler_deactivate(QemuInputHandlerState *s);
void qemu_input_handler_unregister(QemuInputHandlerState *s);
+void qemu_input_handler_bind(QemuInputHandlerState *s,
+ const char *device_id, int head,
+ Error **errp);
void qemu_input_event_send(QemuConsole *src, InputEvent *evt);
void qemu_input_event_sync(void);
@@ -36,6 +39,7 @@ InputEvent *qemu_input_event_new_key(KeyValue *key, bool down);
void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down);
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
+int qemu_input_key_number_to_qcode(uint8_t nr);
int qemu_input_key_value_to_number(const KeyValue *value);
int qemu_input_key_value_to_qcode(const KeyValue *value);
int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
diff --git a/iohandler.c b/iohandler.c
index ae2ef8f966..cca614f087 100644
--- a/iohandler.c
+++ b/iohandler.c
@@ -191,6 +191,7 @@ static void qemu_init_child_watch(void)
struct sigaction act;
sigchld_bh = qemu_bh_new(sigchld_bh_handler, NULL);
+ memset(&act, 0, sizeof(act));
act.sa_handler = sigchld_handler;
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, NULL);
diff --git a/libcacard/cac.c b/libcacard/cac.c
index 74ef3e3cec..0a0163d3eb 100644
--- a/libcacard/cac.c
+++ b/libcacard/cac.c
@@ -93,8 +93,8 @@ cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
static VCardStatus
cac_applet_pki_reset(VCard *card, int channel)
{
- VCardAppletPrivate *applet_private = NULL;
- CACPKIAppletData *pki_applet = NULL;
+ VCardAppletPrivate *applet_private;
+ CACPKIAppletData *pki_applet;
applet_private = vcard_get_current_applet_private(card, channel);
assert(applet_private);
pki_applet = &(applet_private->u.pki_data);
@@ -113,8 +113,8 @@ static VCardStatus
cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
VCardResponse **response)
{
- CACPKIAppletData *pki_applet = NULL;
- VCardAppletPrivate *applet_private = NULL;
+ CACPKIAppletData *pki_applet;
+ VCardAppletPrivate *applet_private;
int size, next;
unsigned char *sign_buffer;
vcard_7816_status_t status;
@@ -169,17 +169,8 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
}
size = apdu->a_Lc;
- sign_buffer = realloc(pki_applet->sign_buffer,
- pki_applet->sign_buffer_len+size);
- if (sign_buffer == NULL) {
- g_free(pki_applet->sign_buffer);
- pki_applet->sign_buffer = NULL;
- pki_applet->sign_buffer_len = 0;
- *response = vcard_make_response(
- VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
- ret = VCARD_DONE;
- break;
- }
+ sign_buffer = g_realloc(pki_applet->sign_buffer,
+ pki_applet->sign_buffer_len + size);
memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size);
size += pki_applet->sign_buffer_len;
switch (apdu->a_p1) {
@@ -288,7 +279,7 @@ cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu,
static void
cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
{
- CACPKIAppletData *pki_applet_data = NULL;
+ CACPKIAppletData *pki_applet_data;
if (applet_private == NULL) {
return;
@@ -310,16 +301,11 @@ static VCardAppletPrivate *
cac_new_pki_applet_private(const unsigned char *cert,
int cert_len, VCardKey *key)
{
- CACPKIAppletData *pki_applet_data = NULL;
- VCardAppletPrivate *applet_private = NULL;
- applet_private = (VCardAppletPrivate *)g_malloc(sizeof(VCardAppletPrivate));
+ CACPKIAppletData *pki_applet_data;
+ VCardAppletPrivate *applet_private;
+ applet_private = g_new0(VCardAppletPrivate, 1);
pki_applet_data = &(applet_private->u.pki_data);
- pki_applet_data->cert_buffer = NULL;
- pki_applet_data->cert_buffer_len = 0;
- pki_applet_data->sign_buffer = NULL;
- pki_applet_data->sign_buffer_len = 0;
- pki_applet_data->key = NULL;
pki_applet_data->cert = (unsigned char *)g_malloc(cert_len+1);
/*
* if we want to support compression, then we simply change the 0 to a 1
@@ -341,8 +327,8 @@ static VCardApplet *
cac_new_pki_applet(int i, const unsigned char *cert,
int cert_len, VCardKey *key)
{
- VCardAppletPrivate *applet_private = NULL;
- VCardApplet *applet = NULL;
+ VCardAppletPrivate *applet_private;
+ VCardApplet *applet;
unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 };
int pki_aid_len = sizeof(pki_aid);
diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
index c28bb60fe6..a54f880390 100644
--- a/libcacard/card_7816.c
+++ b/libcacard/card_7816.c
@@ -51,7 +51,7 @@ vcard_response_new_data(unsigned char *buf, int len)
{
VCardResponse *new_response;
- new_response = (VCardResponse *)g_malloc(sizeof(VCardResponse));
+ new_response = g_new(VCardResponse, 1);
new_response->b_data = g_malloc(len + 2);
memcpy(new_response->b_data, buf, len);
new_response->b_total_len = len+2;
@@ -132,7 +132,7 @@ vcard_response_new_status(vcard_7816_status_t status)
{
VCardResponse *new_response;
- new_response = (VCardResponse *)g_malloc(sizeof(VCardResponse));
+ new_response = g_new(VCardResponse, 1);
new_response->b_data = &new_response->b_sw1;
new_response->b_len = 0;
new_response->b_total_len = 2;
@@ -149,7 +149,7 @@ vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
{
VCardResponse *new_response;
- new_response = (VCardResponse *)g_malloc(sizeof(VCardResponse));
+ new_response = g_new(VCardResponse, 1);
new_response->b_data = &new_response->b_sw1;
new_response->b_len = 0;
new_response->b_total_len = 2;
@@ -336,9 +336,8 @@ vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
return NULL;
}
- new_apdu = (VCardAPDU *)g_malloc(sizeof(VCardAPDU));
- new_apdu->a_data = g_malloc(len);
- memcpy(new_apdu->a_data, raw_apdu, len);
+ new_apdu = g_new(VCardAPDU, 1);
+ new_apdu->a_data = g_memdup(raw_apdu, len);
new_apdu->a_len = len;
*status = vcard_apdu_set_class(new_apdu);
if (*status != VCARD7816_STATUS_SUCCESS) {
@@ -417,7 +416,7 @@ VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)
VCardResponse *
vcard_make_response(vcard_7816_status_t status)
{
- VCardResponse *response = NULL;
+ VCardResponse *response;
switch (status) {
/* known 7816 response codes */
@@ -544,9 +543,8 @@ vcard_make_response(vcard_7816_status_t status)
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
}
+ return response;
}
- assert(response);
- return response;
}
/*
diff --git a/libcacard/event.c b/libcacard/event.c
index 2d7500fac0..a2e6c7dcd0 100644
--- a/libcacard/event.c
+++ b/libcacard/event.c
@@ -17,7 +17,7 @@ vevent_new(VEventType type, VReader *reader, VCard *card)
{
VEvent *new_vevent;
- new_vevent = (VEvent *)g_malloc(sizeof(VEvent));
+ new_vevent = g_new(VEvent, 1);
new_vevent->next = NULL;
new_vevent->type = type;
new_vevent->reader = vreader_reference(reader);
diff --git a/libcacard/vcard.c b/libcacard/vcard.c
index 539177bb4c..6aaf085ecc 100644
--- a/libcacard/vcard.c
+++ b/libcacard/vcard.c
@@ -37,9 +37,8 @@ vcard_buffer_response_new(unsigned char *buffer, int size)
{
VCardBufferResponse *new_buffer;
- new_buffer = (VCardBufferResponse *)g_malloc(sizeof(VCardBufferResponse));
- new_buffer->buffer = (unsigned char *)g_malloc(size);
- memcpy(new_buffer->buffer, buffer, size);
+ new_buffer = g_new(VCardBufferResponse, 1);
+ new_buffer->buffer = (unsigned char *)g_memdup(buffer, size);
new_buffer->buffer_len = size;
new_buffer->current = new_buffer->buffer;
new_buffer->len = size;
@@ -102,15 +101,11 @@ vcard_new_applet(VCardProcessAPDU applet_process_function,
{
VCardApplet *applet;
- applet = (VCardApplet *)g_malloc(sizeof(VCardApplet));
- applet->next = NULL;
- applet->applet_private = NULL;
- applet->applet_private_free = NULL;
+ applet = g_new0(VCardApplet, 1);
applet->process_apdu = applet_process_function;
applet->reset_applet = applet_reset_function;
- applet->aid = g_malloc(aid_len);
- memcpy(applet->aid, aid, aid_len);
+ applet->aid = g_memdup(aid, aid_len);
applet->aid_len = aid_len;
return applet;
}
@@ -149,18 +144,11 @@ VCard *
vcard_new(VCardEmul *private, VCardEmulFree private_free)
{
VCard *new_card;
- int i;
- new_card = (VCard *)g_malloc(sizeof(VCard));
- new_card->applet_list = NULL;
- for (i = 0; i < MAX_CHANNEL; i++) {
- new_card->current_applet[i] = NULL;
- }
- new_card->vcard_buffer_response = NULL;
+ new_card = g_new0(VCard, 1);
new_card->type = VCARD_VM;
new_card->vcard_private = private;
new_card->vcard_private_free = private_free;
- new_card->vcard_get_atr = NULL;
new_card->reference_count = 1;
return new_card;
}
@@ -178,8 +166,8 @@ vcard_reference(VCard *vcard)
void
vcard_free(VCard *vcard)
{
- VCardApplet *current_applet = NULL;
- VCardApplet *next_applet = NULL;
+ VCardApplet *current_applet;
+ VCardApplet *next_applet;
if (vcard == NULL) {
return;
diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
index e2b196d8c5..cefc38333f 100644
--- a/libcacard/vcard_emul_nss.c
+++ b/libcacard/vcard_emul_nss.c
@@ -94,9 +94,9 @@ static void
vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp,
VCardKey ***keysp, int cert_count)
{
- *certsp = (unsigned char **)g_malloc(sizeof(unsigned char *)*cert_count);
- *cert_lenp = (int *)g_malloc(sizeof(int)*cert_count);
- *keysp = (VCardKey **)g_malloc(sizeof(VCardKey *)*cert_count);
+ *certsp = g_new(unsigned char *, cert_count);
+ *cert_lenp = g_new(int, cert_count);
+ *keysp = g_new(VCardKey *, cert_count);
}
/*
@@ -139,7 +139,7 @@ vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert)
{
VCardKey *key;
- key = (VCardKey *)g_malloc(sizeof(VCardKey));
+ key = g_new(VCardKey, 1);
key->slot = PK11_ReferenceSlot(slot);
key->cert = CERT_DupCertificate(cert);
/* NOTE: if we aren't logged into the token, this could return NULL */
@@ -367,7 +367,7 @@ vcard_7816_status_t
vcard_emul_login(VCard *card, unsigned char *pin, int pin_len)
{
PK11SlotInfo *slot;
- unsigned char *pin_string = NULL;
+ unsigned char *pin_string;
int i;
SECStatus rv;
@@ -423,7 +423,7 @@ static VReader *
vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot)
{
VReaderList *reader_list = vreader_get_reader_list();
- VReaderListEntry *current_entry = NULL;
+ VReaderListEntry *current_entry;
if (reader_list == NULL) {
return NULL;
@@ -433,11 +433,13 @@ vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot)
VReader *reader = vreader_list_get_reader(current_entry);
VReaderEmul *reader_emul = vreader_get_private(reader);
if (reader_emul->slot == slot) {
+ vreader_list_delete(reader_list);
return reader;
}
vreader_free(reader);
}
+ vreader_list_delete(reader_list);
return NULL;
}
@@ -449,7 +451,7 @@ vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params)
{
VReaderEmul *new_reader_emul;
- new_reader_emul = (VReaderEmul *)g_malloc(sizeof(VReaderEmul));
+ new_reader_emul = g_new(VReaderEmul, 1);
new_reader_emul->slot = PK11_ReferenceSlot(slot);
new_reader_emul->default_type = type;
@@ -616,11 +618,6 @@ vcard_emul_mirror_card(VReader *vreader)
cert_count++;
}
- if (cert_count == 0) {
- PK11_DestroyGenericObjects(firstObj);
- return NULL;
- }
-
/* allocate the arrays */
vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count);
@@ -1050,7 +1047,7 @@ void
vcard_emul_replay_insertion_events(void)
{
VReaderListEntry *current_entry;
- VReaderListEntry *next_entry = NULL;
+ VReaderListEntry *next_entry;
VReaderList *list = vreader_get_reader_list();
for (current_entry = vreader_list_get_first(list); current_entry;
@@ -1059,6 +1056,8 @@ vcard_emul_replay_insertion_events(void)
next_entry = vreader_list_get_next(current_entry);
vreader_queue_card_event(vreader);
}
+
+ vreader_list_delete(list);
}
/*
@@ -1150,7 +1149,7 @@ vcard_emul_options(const char *args)
char type_str[100];
VCardEmulType type;
int count, i;
- VirtualReaderOptions *vreaderOpt = NULL;
+ VirtualReaderOptions *vreaderOpt;
args = strip(args + 5);
if (*args != '(') {
@@ -1174,14 +1173,10 @@ vcard_emul_options(const char *args)
if (opts->vreader_count >= reader_count) {
reader_count += READER_STEP;
- vreaderOpt = realloc(opts->vreader,
- reader_count * sizeof(*vreaderOpt));
- if (vreaderOpt == NULL) {
- return opts; /* we're done */
- }
+ opts->vreader = g_renew(VirtualReaderOptions, opts->vreader,
+ reader_count);
}
- opts->vreader = vreaderOpt;
- vreaderOpt = &vreaderOpt[opts->vreader_count];
+ vreaderOpt = &opts->vreader[opts->vreader_count];
vreaderOpt->name = g_strndup(name, name_length);
vreaderOpt->vname = g_strndup(vname, vname_length);
vreaderOpt->card_type = type;
@@ -1189,7 +1184,7 @@ vcard_emul_options(const char *args)
g_strndup(type_params, type_params_length);
count = count_tokens(args, ',', ')') + 1;
vreaderOpt->cert_count = count;
- vreaderOpt->cert_name = (char **)g_malloc(count*sizeof(char *));
+ vreaderOpt->cert_name = g_new(char *, count);
for (i = 0; i < count; i++) {
const char *cert = args;
args = strpbrk(args, ",)");
diff --git a/libcacard/vreader.c b/libcacard/vreader.c
index 77202951fb..d2a9b7df41 100644
--- a/libcacard/vreader.c
+++ b/libcacard/vreader.c
@@ -115,7 +115,7 @@ vreader_new(const char *name, VReaderEmul *private,
{
VReader *reader;
- reader = (VReader *)g_malloc(sizeof(VReader));
+ reader = g_new(VReader, 1);
qemu_mutex_init(&reader->lock);
reader->reference_count = 1;
reader->name = g_strdup(name);
@@ -283,12 +283,10 @@ vreader_xfr_bytes(VReader *reader,
response->b_sw2, response->b_len, response->b_total_len);
}
}
- assert(card_status == VCARD_DONE);
- if (card_status == VCARD_DONE) {
- int size = MIN(*receive_buf_len, response->b_total_len);
- memcpy(receive_buf, response->b_data, size);
- *receive_buf_len = size;
- }
+ assert(card_status == VCARD_DONE && response);
+ int size = MIN(*receive_buf_len, response->b_total_len);
+ memcpy(receive_buf, response->b_data, size);
+ *receive_buf_len = size;
vcard_response_delete(response);
vcard_apdu_delete(apdu);
vcard_free(card); /* free our reference */
@@ -312,10 +310,7 @@ vreader_list_entry_new(VReader *reader)
{
VReaderListEntry *new_reader_list_entry;
- new_reader_list_entry = (VReaderListEntry *)
- g_malloc(sizeof(VReaderListEntry));
- new_reader_list_entry->next = NULL;
- new_reader_list_entry->prev = NULL;
+ new_reader_list_entry = g_new0(VReaderListEntry, 1);
new_reader_list_entry->reader = vreader_reference(reader);
return new_reader_list_entry;
}
@@ -336,9 +331,7 @@ vreader_list_new(void)
{
VReaderList *new_reader_list;
- new_reader_list = (VReaderList *)g_malloc(sizeof(VReaderList));
- new_reader_list->head = NULL;
- new_reader_list->tail = NULL;
+ new_reader_list = g_new0(VReaderList, 1);
return new_reader_list;
}
@@ -346,7 +339,7 @@ void
vreader_list_delete(VReaderList *list)
{
VReaderListEntry *current_entry;
- VReaderListEntry *next_entry = NULL;
+ VReaderListEntry *next_entry;
for (current_entry = vreader_list_get_first(list); current_entry;
current_entry = next_entry) {
next_entry = vreader_list_get_next(current_entry);
@@ -437,8 +430,8 @@ vreader_list_unlock(void)
static VReaderList *
vreader_copy_list(VReaderList *list)
{
- VReaderList *new_list = NULL;
- VReaderListEntry *current_entry = NULL;
+ VReaderList *new_list;
+ VReaderListEntry *current_entry;
new_list = vreader_list_new();
if (new_list == NULL) {
@@ -470,7 +463,7 @@ VReader *
vreader_get_reader_by_id(vreader_id_t id)
{
VReader *reader = NULL;
- VReaderListEntry *current_entry = NULL;
+ VReaderListEntry *current_entry;
if (id == (vreader_id_t) -1) {
return NULL;
@@ -494,7 +487,7 @@ VReader *
vreader_get_reader_by_name(const char *name)
{
VReader *reader = NULL;
- VReaderListEntry *current_entry = NULL;
+ VReaderListEntry *current_entry;
vreader_list_lock();
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c
index 3477ab3e1b..6693900201 100644
--- a/libcacard/vscclient.c
+++ b/libcacard/vscclient.c
@@ -131,8 +131,8 @@ static void *
event_thread(void *arg)
{
unsigned char atr[MAX_ATR_LEN];
- int atr_len = MAX_ATR_LEN;
- VEvent *event = NULL;
+ int atr_len;
+ VEvent *event;
unsigned int reader_id;
@@ -502,8 +502,7 @@ do_command(GIOChannel *source,
if (reader != NULL) {
error = vcard_emul_force_card_insert(reader);
printf("insert %s, returned %d\n",
- reader ? vreader_get_name(reader)
- : "invalid reader", error);
+ vreader_get_name(reader), error);
} else {
printf("no reader by id %u found\n", reader_id);
}
@@ -515,8 +514,7 @@ do_command(GIOChannel *source,
if (reader != NULL) {
error = vcard_emul_force_card_remove(reader);
printf("remove %s, returned %d\n",
- reader ? vreader_get_name(reader)
- : "invalid reader", error);
+ vreader_get_name(reader), error);
} else {
printf("no reader by id %u found\n", reader_id);
}
@@ -572,6 +570,7 @@ do_command(GIOChannel *source,
"CARD_PRESENT" : " ",
vreader_get_name(reader));
}
+ vreader_list_delete(list);
} else if (*string != 0) {
printf("valid commands:\n");
printf("insert [reader_id]\n");
diff --git a/nbd.c b/nbd.c
index e5084b6e7c..e0d032c252 100644
--- a/nbd.c
+++ b/nbd.c
@@ -306,7 +306,7 @@ static int nbd_send_negotiate(NBDClient *client)
[ 8 .. 15] magic (NBD_CLIENT_MAGIC)
[16 .. 23] size
[24 .. 25] server flags (0)
- [24 .. 27] export flags
+ [26 .. 27] export flags
[28 .. 151] reserved (0)
Negotiation header with options, part 1:
diff --git a/qemu-nbd.c b/qemu-nbd.c
index eed79fa15e..cd6bd50fae 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -294,7 +294,7 @@ static void *nbd_client_thread(void *arg)
fd = open(device, O_RDWR);
if (fd < 0) {
/* Linux-only, we can use %m in printf. */
- fprintf(stderr, "Failed to open %s: %m", device);
+ fprintf(stderr, "Failed to open %s: %m\n", device);
goto out_socket;
}
@@ -369,8 +369,10 @@ static void nbd_accept(void *opaque)
return;
}
- if (fd >= 0 && nbd_client_new(exp, fd, nbd_client_closed)) {
+ if (nbd_client_new(exp, fd, nbd_client_closed)) {
nb_fds++;
+ } else {
+ close(fd);
}
}
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 0a7e01385c..46fd483eb8 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -15,7 +15,7 @@ Export QEMU disk image using NBD protocol.
@item @var{filename}
is a disk image filename
@item -p, --port=@var{port}
- port to listen on (default @samp{1024})
+ port to listen on (default @samp{10809})
@item -o, --offset=@var{offset}
offset into the image
@item -b, --bind=@var{iface}
diff --git a/qemu-options.hx b/qemu-options.hx
index c2c0823911..4011d46d62 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3071,7 +3071,8 @@ STEXI
Set OpenBIOS nvram @var{variable} to given @var{value} (PPC, SPARC only).
ETEXI
DEF("semihosting", 0, QEMU_OPTION_semihosting,
- "-semihosting semihosting mode\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA)
+ "-semihosting semihosting mode\n",
+ QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
STEXI
@item -semihosting
@findex -semihosting
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 6c6f2b3d46..794dcb9fb7 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -370,8 +370,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
init_cpreg_list(cpu);
- cpu_reset(cs);
qemu_init_vcpu(cs);
+ cpu_reset(cs);
acc->parent_realize(dev, errp);
}
diff --git a/target-lm32/Makefile.objs b/target-lm32/Makefile.objs
index 40236876c8..c3e1bd6bd6 100644
--- a/target-lm32/Makefile.objs
+++ b/target-lm32/Makefile.objs
@@ -1,3 +1,4 @@
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += gdbstub.o
+obj-y += lm32-semi.o
obj-$(CONFIG_SOFTMMU) += machine.o
diff --git a/target-lm32/README b/target-lm32/README
index a1c2c7eb1e..ba3508a711 100644
--- a/target-lm32/README
+++ b/target-lm32/README
@@ -16,14 +16,13 @@ This will make serial0 (the lm32_uart) and serial1 (the JTAG UART)
available as virtual consoles.
-Programmatically terminate the emulator
-----------------------------------------
-Originally neither the LatticeMico32 nor its peripherals support a
-mechanism to shut down the machine. Emulation aware programs can write to a
-to a special register within the system control block to shut down the
-virtual machine. For more details see hw/lm32_sys.c. The lm32-evr is the
-first BSP which instantiate this model. A (32 bit) write to 0xfff0000
-causes a vm shutdown.
+Semihosting
+-----------
+Semihosting on this target is supported. Some system calls like read, write
+and exit are executed on the host if semihosting is enabled. See
+target/lm32-semi.c for all supported system calls. Emulation aware programs
+can use this mechanism to shut down the virtual machine and print to the
+host console. See the tcg tests for an example.
Special instructions
diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h
index 24bde78502..70600aa47a 100644
--- a/target-lm32/cpu.h
+++ b/target-lm32/cpu.h
@@ -217,6 +217,7 @@ void lm32_breakpoint_remove(CPULM32State *env, int index);
void lm32_watchpoint_insert(CPULM32State *env, int index, target_ulong address,
lm32_wp_t wp_type);
void lm32_watchpoint_remove(CPULM32State *env, int index);
+bool lm32_cpu_do_semihosting(CPUState *cs);
static inline CPULM32State *cpu_init(const char *cpu_model)
{
diff --git a/target-lm32/helper.c b/target-lm32/helper.c
index 783aa16a45..1bca1961af 100644
--- a/target-lm32/helper.c
+++ b/target-lm32/helper.c
@@ -1,7 +1,7 @@
/*
* LatticeMico32 helper routines.
*
- * Copyright (c) 2010 Michael Walle <michael@walle.cc>
+ * Copyright (c) 2010-2014 Michael Walle <michael@walle.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,6 +19,7 @@
#include "cpu.h"
#include "qemu/host-utils.h"
+#include "sysemu/sysemu.h"
int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
int mmu_idx)
@@ -159,11 +160,20 @@ void lm32_cpu_do_interrupt(CPUState *cs)
"exception at pc=%x type=%x\n", env->pc, cs->exception_index);
switch (cs->exception_index) {
+ case EXCP_SYSTEMCALL:
+ if (unlikely(semihosting_enabled)) {
+ /* do_semicall() returns true if call was handled. Otherwise
+ * do the normal exception handling. */
+ if (lm32_cpu_do_semihosting(cs)) {
+ env->pc += 4;
+ break;
+ }
+ }
+ /* fall through */
case EXCP_INSN_BUS_ERROR:
case EXCP_DATA_BUS_ERROR:
case EXCP_DIVIDE_BY_ZERO:
case EXCP_IRQ:
- case EXCP_SYSTEMCALL:
/* non-debug exceptions */
env->regs[R_EA] = env->pc;
env->ie |= (env->ie & IE_IE) ? IE_EIE : 0;
diff --git a/target-lm32/lm32-semi.c b/target-lm32/lm32-semi.c
new file mode 100644
index 0000000000..fc9d2d1c73
--- /dev/null
+++ b/target-lm32/lm32-semi.c
@@ -0,0 +1,215 @@
+/*
+ * Lattice Mico32 semihosting syscall interface
+ *
+ * Copyright (c) 2014 Michael Walle <michael@walle.cc>
+ *
+ * Based on target-m68k/m68k-semi.c, which is
+ * Copyright (c) 2005-2007 CodeSourcery.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stddef.h>
+#include "cpu.h"
+#include "helper.h"
+#include "qemu/log.h"
+#include "exec/softmmu-semi.h"
+
+enum {
+ TARGET_SYS_exit = 1,
+ TARGET_SYS_open = 2,
+ TARGET_SYS_close = 3,
+ TARGET_SYS_read = 4,
+ TARGET_SYS_write = 5,
+ TARGET_SYS_lseek = 6,
+ TARGET_SYS_fstat = 10,
+ TARGET_SYS_stat = 15,
+};
+
+enum {
+ NEWLIB_O_RDONLY = 0x0,
+ NEWLIB_O_WRONLY = 0x1,
+ NEWLIB_O_RDWR = 0x2,
+ NEWLIB_O_APPEND = 0x8,
+ NEWLIB_O_CREAT = 0x200,
+ NEWLIB_O_TRUNC = 0x400,
+ NEWLIB_O_EXCL = 0x800,
+};
+
+static int translate_openflags(int flags)
+{
+ int hf;
+
+ if (flags & NEWLIB_O_WRONLY) {
+ hf = O_WRONLY;
+ } else if (flags & NEWLIB_O_RDWR) {
+ hf = O_RDWR;
+ } else {
+ hf = O_RDONLY;
+ }
+
+ if (flags & NEWLIB_O_APPEND) {
+ hf |= O_APPEND;
+ }
+
+ if (flags & NEWLIB_O_CREAT) {
+ hf |= O_CREAT;
+ }
+
+ if (flags & NEWLIB_O_TRUNC) {
+ hf |= O_TRUNC;
+ }
+
+ if (flags & NEWLIB_O_EXCL) {
+ hf |= O_EXCL;
+ }
+
+ return hf;
+}
+
+struct newlib_stat {
+ int16_t newlib_st_dev; /* device */
+ uint16_t newlib_st_ino; /* inode */
+ uint16_t newlib_st_mode; /* protection */
+ uint16_t newlib_st_nlink; /* number of hard links */
+ uint16_t newlib_st_uid; /* user ID of owner */
+ uint16_t newlib_st_gid; /* group ID of owner */
+ int16_t newlib_st_rdev; /* device type (if inode device) */
+ int32_t newlib_st_size; /* total size, in bytes */
+ int32_t newlib_st_atime; /* time of last access */
+ uint32_t newlib_st_spare1;
+ int32_t newlib_st_mtime; /* time of last modification */
+ uint32_t newlib_st_spare2;
+ int32_t newlib_st_ctime; /* time of last change */
+ uint32_t newlib_st_spare3;
+} QEMU_PACKED;
+
+static int translate_stat(CPULM32State *env, target_ulong addr,
+ struct stat *s)
+{
+ struct newlib_stat *p;
+
+ p = lock_user(VERIFY_WRITE, addr, sizeof(struct newlib_stat), 0);
+ if (!p) {
+ return 0;
+ }
+ p->newlib_st_dev = cpu_to_be16(s->st_dev);
+ p->newlib_st_ino = cpu_to_be16(s->st_ino);
+ p->newlib_st_mode = cpu_to_be16(s->st_mode);
+ p->newlib_st_nlink = cpu_to_be16(s->st_nlink);
+ p->newlib_st_uid = cpu_to_be16(s->st_uid);
+ p->newlib_st_gid = cpu_to_be16(s->st_gid);
+ p->newlib_st_rdev = cpu_to_be16(s->st_rdev);
+ p->newlib_st_size = cpu_to_be32(s->st_size);
+ p->newlib_st_atime = cpu_to_be32(s->st_atime);
+ p->newlib_st_mtime = cpu_to_be32(s->st_mtime);
+ p->newlib_st_ctime = cpu_to_be32(s->st_ctime);
+ unlock_user(p, addr, sizeof(struct newlib_stat));
+
+ return 1;
+}
+
+bool lm32_cpu_do_semihosting(CPUState *cs)
+{
+ LM32CPU *cpu = LM32_CPU(cs);
+ CPULM32State *env = &cpu->env;
+
+ int ret = -1;
+ target_ulong nr, arg0, arg1, arg2;
+ void *p;
+ struct stat s;
+
+ nr = env->regs[R_R8];
+ arg0 = env->regs[R_R1];
+ arg1 = env->regs[R_R2];
+ arg2 = env->regs[R_R3];
+
+ switch (nr) {
+ case TARGET_SYS_exit:
+ /* void _exit(int rc) */
+ exit(arg0);
+
+ case TARGET_SYS_open:
+ /* int open(const char *pathname, int flags) */
+ p = lock_user_string(arg0);
+ if (!p) {
+ ret = -1;
+ } else {
+ ret = open(p, translate_openflags(arg2));
+ unlock_user(p, arg0, 0);
+ }
+ break;
+
+ case TARGET_SYS_read:
+ /* ssize_t read(int fd, const void *buf, size_t count) */
+ p = lock_user(VERIFY_WRITE, arg1, arg2, 0);
+ if (!p) {
+ ret = -1;
+ } else {
+ ret = read(arg0, p, arg2);
+ unlock_user(p, arg1, arg2);
+ }
+ break;
+
+ case TARGET_SYS_write:
+ /* ssize_t write(int fd, const void *buf, size_t count) */
+ p = lock_user(VERIFY_READ, arg1, arg2, 1);
+ if (!p) {
+ ret = -1;
+ } else {
+ ret = write(arg0, p, arg2);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+
+ case TARGET_SYS_close:
+ /* int close(int fd) */
+ /* don't close stdin/stdout/stderr */
+ if (arg0 > 2) {
+ ret = close(arg0);
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case TARGET_SYS_lseek:
+ /* off_t lseek(int fd, off_t offset, int whence */
+ ret = lseek(arg0, arg1, arg2);
+ break;
+
+ case TARGET_SYS_stat:
+ /* int stat(const char *path, struct stat *buf) */
+ p = lock_user_string(arg0);
+ if (!p) {
+ ret = -1;
+ } else {
+ ret = stat(p, &s);
+ unlock_user(p, arg0, 0);
+ if (translate_stat(env, arg1, &s) == 0) {
+ ret = -1;
+ }
+ }
+ break;
+
+ case TARGET_SYS_fstat:
+ /* int stat(int fd, struct stat *buf) */
+ ret = fstat(arg0, &s);
+ if (ret == 0) {
+ if (translate_stat(env, arg1, &s) == 0) {
+ ret = -1;
+ }
+ }
+ break;
+
+ default:
+ /* unhandled */
+ return false;
+ }
+
+ env->regs[R_R1] = ret;
+ return true;
+}
diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
index 0ae495c586..8855d5039d 100644
--- a/tcg/mips/tcg-target.c
+++ b/tcg/mips/tcg-target.c
@@ -24,14 +24,17 @@
* THE SOFTWARE.
*/
-#include "tcg-be-null.h"
+#include "tcg-be-ldst.h"
-#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
-# define TCG_NEED_BSWAP 0
+#ifdef HOST_WORDS_BIGENDIAN
+# define MIPS_BE 1
#else
-# define TCG_NEED_BSWAP 1
+# define MIPS_BE 0
#endif
+#define LO_OFF (MIPS_BE * 4)
+#define HI_OFF (4 - LO_OFF)
+
#ifndef NDEBUG
static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
"zero",
@@ -64,13 +67,17 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
"k1",
"gp",
"sp",
- "fp",
+ "s8",
"ra",
};
#endif
+#define TCG_TMP0 TCG_REG_AT
+#define TCG_TMP1 TCG_REG_T9
+
/* check if we really need so many registers :P */
static const TCGReg tcg_target_reg_alloc_order[] = {
+ /* Call saved registers. */
TCG_REG_S0,
TCG_REG_S1,
TCG_REG_S2,
@@ -79,6 +86,10 @@ static const TCGReg tcg_target_reg_alloc_order[] = {
TCG_REG_S5,
TCG_REG_S6,
TCG_REG_S7,
+ TCG_REG_S8,
+
+ /* Call clobbered registers. */
+ TCG_REG_T0,
TCG_REG_T1,
TCG_REG_T2,
TCG_REG_T3,
@@ -88,12 +99,14 @@ static const TCGReg tcg_target_reg_alloc_order[] = {
TCG_REG_T7,
TCG_REG_T8,
TCG_REG_T9,
- TCG_REG_A0,
- TCG_REG_A1,
- TCG_REG_A2,
- TCG_REG_A3,
+ TCG_REG_V1,
TCG_REG_V0,
- TCG_REG_V1
+
+ /* Argument registers, opposite order of allocation. */
+ TCG_REG_A3,
+ TCG_REG_A2,
+ TCG_REG_A1,
+ TCG_REG_A0,
};
static const TCGReg tcg_target_call_iarg_regs[4] = {
@@ -142,6 +155,17 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type,
reloc_pc16(code_ptr, (tcg_insn_unit *)value);
}
+#define TCG_CT_CONST_ZERO 0x100
+#define TCG_CT_CONST_U16 0x200 /* Unsigned 16-bit: 0 - 0xffff. */
+#define TCG_CT_CONST_S16 0x400 /* Signed 16-bit: -32768 - 32767 */
+#define TCG_CT_CONST_P2M1 0x800 /* Power of 2 minus 1. */
+#define TCG_CT_CONST_N16 0x1000 /* "Negatable" 16-bit: -32767 - 32767 */
+
+static inline bool is_p2m1(tcg_target_long val)
+{
+ return val && ((val + 1) & val) == 0;
+}
+
/* parse target specific constraints */
static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
{
@@ -161,11 +185,11 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
case 'l': /* qemu_ld input arg constraint */
ct->ct |= TCG_CT_REG;
tcg_regset_set(ct->u.regs, 0xffffffff);
-#if defined(CONFIG_SOFTMMU)
tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0);
-# if (TARGET_LONG_BITS == 64)
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2);
-# endif
+#if defined(CONFIG_SOFTMMU)
+ if (TARGET_LONG_BITS == 64) {
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2);
+ }
#endif
break;
case 'S': /* qemu_st constraint */
@@ -173,13 +197,12 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
tcg_regset_set(ct->u.regs, 0xffffffff);
tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0);
#if defined(CONFIG_SOFTMMU)
-# if (TARGET_LONG_BITS == 32)
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_A1);
-# endif
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2);
-# if TARGET_LONG_BITS == 64
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_A3);
-# endif
+ if (TARGET_LONG_BITS == 32) {
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_A1);
+ } else {
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_A3);
+ }
#endif
break;
case 'I':
@@ -188,6 +211,12 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
case 'J':
ct->ct |= TCG_CT_CONST_S16;
break;
+ case 'K':
+ ct->ct |= TCG_CT_CONST_P2M1;
+ break;
+ case 'N':
+ ct->ct |= TCG_CT_CONST_N16;
+ break;
case 'Z':
/* We are cheating a bit here, using the fact that the register
ZERO is also the register number 0. Hence there is no need
@@ -208,20 +237,27 @@ static inline int tcg_target_const_match(tcg_target_long val, TCGType type,
{
int ct;
ct = arg_ct->ct;
- if (ct & TCG_CT_CONST)
+ if (ct & TCG_CT_CONST) {
+ return 1;
+ } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) {
return 1;
- else if ((ct & TCG_CT_CONST_ZERO) && val == 0)
+ } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) {
return 1;
- else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val)
+ } else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) {
return 1;
- else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val)
+ } else if ((ct & TCG_CT_CONST_N16) && val >= -32767 && val <= 32767) {
return 1;
- else
- return 0;
+ } else if ((ct & TCG_CT_CONST_P2M1)
+ && use_mips32r2_instructions && is_p2m1(val)) {
+ return 1;
+ }
+ return 0;
}
/* instruction opcodes */
-enum {
+typedef enum {
+ OPC_J = 0x02 << 26,
+ OPC_JAL = 0x03 << 26,
OPC_BEQ = 0x04 << 26,
OPC_BNE = 0x05 << 26,
OPC_BLEZ = 0x06 << 26,
@@ -279,16 +315,17 @@ enum {
OPC_MUL = OPC_SPECIAL2 | 0x002,
OPC_SPECIAL3 = 0x1f << 26,
+ OPC_EXT = OPC_SPECIAL3 | 0x000,
OPC_INS = OPC_SPECIAL3 | 0x004,
OPC_WSBH = OPC_SPECIAL3 | 0x0a0,
OPC_SEB = OPC_SPECIAL3 | 0x420,
OPC_SEH = OPC_SPECIAL3 | 0x620,
-};
+} MIPSInsn;
/*
* Type reg
*/
-static inline void tcg_out_opc_reg(TCGContext *s, int opc,
+static inline void tcg_out_opc_reg(TCGContext *s, MIPSInsn opc,
TCGReg rd, TCGReg rs, TCGReg rt)
{
int32_t inst;
@@ -303,7 +340,7 @@ static inline void tcg_out_opc_reg(TCGContext *s, int opc,
/*
* Type immediate
*/
-static inline void tcg_out_opc_imm(TCGContext *s, int opc,
+static inline void tcg_out_opc_imm(TCGContext *s, MIPSInsn opc,
TCGReg rt, TCGReg rs, TCGArg imm)
{
int32_t inst;
@@ -316,9 +353,25 @@ static inline void tcg_out_opc_imm(TCGContext *s, int opc,
}
/*
+ * Type bitfield
+ */
+static inline void tcg_out_opc_bf(TCGContext *s, MIPSInsn opc, TCGReg rt,
+ TCGReg rs, int msb, int lsb)
+{
+ int32_t inst;
+
+ inst = opc;
+ inst |= (rs & 0x1F) << 21;
+ inst |= (rt & 0x1F) << 16;
+ inst |= (msb & 0x1F) << 11;
+ inst |= (lsb & 0x1F) << 6;
+ tcg_out32(s, inst);
+}
+
+/*
* Type branch
*/
-static inline void tcg_out_opc_br(TCGContext *s, int opc,
+static inline void tcg_out_opc_br(TCGContext *s, MIPSInsn opc,
TCGReg rt, TCGReg rs)
{
/* We pay attention here to not modify the branch target by reading
@@ -332,7 +385,7 @@ static inline void tcg_out_opc_br(TCGContext *s, int opc,
/*
* Type sa
*/
-static inline void tcg_out_opc_sa(TCGContext *s, int opc,
+static inline void tcg_out_opc_sa(TCGContext *s, MIPSInsn opc,
TCGReg rd, TCGReg rt, TCGArg sa)
{
int32_t inst;
@@ -345,6 +398,29 @@ static inline void tcg_out_opc_sa(TCGContext *s, int opc,
}
+/*
+ * Type jump.
+ * Returns true if the branch was in range and the insn was emitted.
+ */
+static bool tcg_out_opc_jmp(TCGContext *s, MIPSInsn opc, void *target)
+{
+ uintptr_t dest = (uintptr_t)target;
+ uintptr_t from = (uintptr_t)s->code_ptr + 4;
+ int32_t inst;
+
+ /* The pc-region branch happens within the 256MB region of
+ the delay slot (thus the +4). */
+ if ((from ^ dest) & -(1 << 28)) {
+ return false;
+ }
+ assert((dest & 3) == 0);
+
+ inst = opc;
+ inst |= (dest >> 2) & 0x3ffffff;
+ tcg_out32(s, inst);
+ return true;
+}
+
static inline void tcg_out_nop(TCGContext *s)
{
tcg_out32(s, 0);
@@ -367,8 +443,10 @@ static inline void tcg_out_movi(TCGContext *s, TCGType type,
} else if (arg == (uint16_t)arg) {
tcg_out_opc_imm(s, OPC_ORI, reg, TCG_REG_ZERO, arg);
} else {
- tcg_out_opc_imm(s, OPC_LUI, reg, 0, arg >> 16);
- tcg_out_opc_imm(s, OPC_ORI, reg, reg, arg & 0xffff);
+ tcg_out_opc_imm(s, OPC_LUI, reg, TCG_REG_ZERO, arg >> 16);
+ if (arg & 0xffff) {
+ tcg_out_opc_imm(s, OPC_ORI, reg, reg, arg & 0xffff);
+ }
}
}
@@ -378,14 +456,14 @@ static inline void tcg_out_bswap16(TCGContext *s, TCGReg ret, TCGReg arg)
tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg);
} else {
/* ret and arg can't be register at */
- if (ret == TCG_REG_AT || arg == TCG_REG_AT) {
+ if (ret == TCG_TMP0 || arg == TCG_TMP0) {
tcg_abort();
}
- tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8);
+ tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8);
tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8);
tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00);
- tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0);
}
}
@@ -396,14 +474,14 @@ static inline void tcg_out_bswap16s(TCGContext *s, TCGReg ret, TCGReg arg)
tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret);
} else {
/* ret and arg can't be register at */
- if (ret == TCG_REG_AT || arg == TCG_REG_AT) {
+ if (ret == TCG_TMP0 || arg == TCG_TMP0) {
tcg_abort();
}
- tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8);
+ tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8);
tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24);
tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16);
- tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0);
}
}
@@ -414,22 +492,22 @@ static inline void tcg_out_bswap32(TCGContext *s, TCGReg ret, TCGReg arg)
tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16);
} else {
/* ret and arg must be different and can't be register at */
- if (ret == arg || ret == TCG_REG_AT || arg == TCG_REG_AT) {
+ if (ret == arg || ret == TCG_TMP0 || arg == TCG_TMP0) {
tcg_abort();
}
tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24);
- tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 24);
- tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+ tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 24);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0);
- tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, arg, 0xff00);
- tcg_out_opc_sa(s, OPC_SLL, TCG_REG_AT, TCG_REG_AT, 8);
- tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, arg, 0xff00);
+ tcg_out_opc_sa(s, OPC_SLL, TCG_TMP0, TCG_TMP0, 8);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0);
- tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8);
- tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0xff00);
- tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+ tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8);
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, TCG_TMP0, 0xff00);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0);
}
}
@@ -453,16 +531,18 @@ static inline void tcg_out_ext16s(TCGContext *s, TCGReg ret, TCGReg arg)
}
}
-static inline void tcg_out_ldst(TCGContext *s, int opc, TCGArg arg,
- TCGReg arg1, TCGArg arg2)
+static void tcg_out_ldst(TCGContext *s, MIPSInsn opc, TCGReg data,
+ TCGReg addr, intptr_t ofs)
{
- if (arg2 == (int16_t) arg2) {
- tcg_out_opc_imm(s, opc, arg, arg1, arg2);
- } else {
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, arg2);
- tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_AT, TCG_REG_AT, arg1);
- tcg_out_opc_imm(s, opc, arg, TCG_REG_AT, 0);
+ int16_t lo = ofs;
+ if (ofs != lo) {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs - lo);
+ if (addr != TCG_REG_ZERO) {
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_TMP0, TCG_TMP0, addr);
+ }
+ addr = TCG_TMP0;
}
+ tcg_out_opc_imm(s, opc, data, addr, lo);
}
static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg,
@@ -482,1051 +562,1008 @@ static inline void tcg_out_addi(TCGContext *s, TCGReg reg, TCGArg val)
if (val == (int16_t)val) {
tcg_out_opc_imm(s, OPC_ADDIU, reg, reg, val);
} else {
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, val);
- tcg_out_opc_reg(s, OPC_ADDU, reg, reg, TCG_REG_AT);
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, val);
+ tcg_out_opc_reg(s, OPC_ADDU, reg, reg, TCG_TMP0);
}
}
-/* Helper routines for marshalling helper function arguments into
- * the correct registers and stack.
- * arg_num is where we want to put this argument, and is updated to be ready
- * for the next call. arg is the argument itself. Note that arg_num 0..3 is
- * real registers, 4+ on stack.
- *
- * We provide routines for arguments which are: immediate, 32 bit
- * value in register, 16 and 8 bit values in register (which must be zero
- * extended before use) and 64 bit value in a lo:hi register pair.
- */
-#define DEFINE_TCG_OUT_CALL_IARG(NAME, ARGPARAM) \
- static inline void NAME(TCGContext *s, int *arg_num, ARGPARAM) \
- { \
- if (*arg_num < 4) { \
- DEFINE_TCG_OUT_CALL_IARG_GET_ARG(tcg_target_call_iarg_regs[*arg_num]); \
- } else { \
- DEFINE_TCG_OUT_CALL_IARG_GET_ARG(TCG_REG_AT); \
- tcg_out_st(s, TCG_TYPE_I32, TCG_REG_AT, TCG_REG_SP, 4 * (*arg_num)); \
- } \
- (*arg_num)++; \
-}
-#define DEFINE_TCG_OUT_CALL_IARG_GET_ARG(A) \
- tcg_out_opc_imm(s, OPC_ANDI, A, arg, 0xff);
-DEFINE_TCG_OUT_CALL_IARG(tcg_out_call_iarg_reg8, TCGReg arg)
-#undef DEFINE_TCG_OUT_CALL_IARG_GET_ARG
-#define DEFINE_TCG_OUT_CALL_IARG_GET_ARG(A) \
- tcg_out_opc_imm(s, OPC_ANDI, A, arg, 0xffff);
-DEFINE_TCG_OUT_CALL_IARG(tcg_out_call_iarg_reg16, TCGReg arg)
-#undef DEFINE_TCG_OUT_CALL_IARG_GET_ARG
-#define DEFINE_TCG_OUT_CALL_IARG_GET_ARG(A) \
- tcg_out_movi(s, TCG_TYPE_I32, A, arg);
-DEFINE_TCG_OUT_CALL_IARG(tcg_out_call_iarg_imm32, TCGArg arg)
-#undef DEFINE_TCG_OUT_CALL_IARG_GET_ARG
-
-/* We don't use the macro for this one to avoid an unnecessary reg-reg
- move when storing to the stack. */
-static inline void tcg_out_call_iarg_reg32(TCGContext *s, int *arg_num,
- TCGReg arg)
-{
- if (*arg_num < 4) {
- tcg_out_mov(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[*arg_num], arg);
- } else {
- tcg_out_st(s, TCG_TYPE_I32, arg, TCG_REG_SP, 4 * (*arg_num));
- }
- (*arg_num)++;
-}
-
-static inline void tcg_out_call_iarg_reg64(TCGContext *s, int *arg_num,
- TCGReg arg_low, TCGReg arg_high)
-{
- (*arg_num) = (*arg_num + 1) & ~1;
-
-#if defined(HOST_WORDS_BIGENDIAN)
- tcg_out_call_iarg_reg32(s, arg_num, arg_high);
- tcg_out_call_iarg_reg32(s, arg_num, arg_low);
-#else
- tcg_out_call_iarg_reg32(s, arg_num, arg_low);
- tcg_out_call_iarg_reg32(s, arg_num, arg_high);
-#endif
-}
+/* Bit 0 set if inversion required; bit 1 set if swapping required. */
+#define MIPS_CMP_INV 1
+#define MIPS_CMP_SWAP 2
+
+static const uint8_t mips_cmp_map[16] = {
+ [TCG_COND_LT] = 0,
+ [TCG_COND_LTU] = 0,
+ [TCG_COND_GE] = MIPS_CMP_INV,
+ [TCG_COND_GEU] = MIPS_CMP_INV,
+ [TCG_COND_LE] = MIPS_CMP_INV | MIPS_CMP_SWAP,
+ [TCG_COND_LEU] = MIPS_CMP_INV | MIPS_CMP_SWAP,
+ [TCG_COND_GT] = MIPS_CMP_SWAP,
+ [TCG_COND_GTU] = MIPS_CMP_SWAP,
+};
-static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGArg arg1,
- TCGArg arg2, int label_index)
+static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret,
+ TCGReg arg1, TCGReg arg2)
{
- TCGLabel *l = &s->labels[label_index];
+ MIPSInsn s_opc = OPC_SLTU;
+ int cmp_map;
switch (cond) {
case TCG_COND_EQ:
- tcg_out_opc_br(s, OPC_BEQ, arg1, arg2);
+ if (arg2 != 0) {
+ tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2);
+ arg1 = ret;
+ }
+ tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1);
break;
+
case TCG_COND_NE:
- tcg_out_opc_br(s, OPC_BNE, arg1, arg2);
- break;
- case TCG_COND_LT:
- if (arg2 == 0) {
- tcg_out_opc_br(s, OPC_BLTZ, 0, arg1);
- } else {
- tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg1, arg2);
- tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO);
+ if (arg2 != 0) {
+ tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2);
+ arg1 = ret;
}
+ tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1);
break;
- case TCG_COND_LTU:
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg1, arg2);
- tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO);
- break;
+
+ case TCG_COND_LT:
case TCG_COND_GE:
- if (arg2 == 0) {
- tcg_out_opc_br(s, OPC_BGEZ, 0, arg1);
- } else {
- tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg1, arg2);
- tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO);
- }
- break;
- case TCG_COND_GEU:
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg1, arg2);
- tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO);
- break;
case TCG_COND_LE:
- if (arg2 == 0) {
- tcg_out_opc_br(s, OPC_BLEZ, 0, arg1);
- } else {
- tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg2, arg1);
- tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO);
- }
- break;
- case TCG_COND_LEU:
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg2, arg1);
- tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO);
- break;
case TCG_COND_GT:
- if (arg2 == 0) {
- tcg_out_opc_br(s, OPC_BGTZ, 0, arg1);
- } else {
- tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg2, arg1);
- tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO);
- }
- break;
+ s_opc = OPC_SLT;
+ /* FALLTHRU */
+
+ case TCG_COND_LTU:
+ case TCG_COND_GEU:
+ case TCG_COND_LEU:
case TCG_COND_GTU:
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg2, arg1);
- tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO);
- break;
- default:
- tcg_abort();
+ cmp_map = mips_cmp_map[cond];
+ if (cmp_map & MIPS_CMP_SWAP) {
+ TCGReg t = arg1;
+ arg1 = arg2;
+ arg2 = t;
+ }
+ tcg_out_opc_reg(s, s_opc, ret, arg1, arg2);
+ if (cmp_map & MIPS_CMP_INV) {
+ tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
+ }
break;
- }
- if (l->has_value) {
- reloc_pc16(s->code_ptr - 1, l->u.value_ptr);
- } else {
- tcg_out_reloc(s, s->code_ptr - 1, R_MIPS_PC16, label_index, 0);
- }
- tcg_out_nop(s);
+
+ default:
+ tcg_abort();
+ break;
+ }
}
-/* XXX: we implement it at the target level to avoid having to
- handle cross basic blocks temporaries */
-static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGArg arg1,
- TCGArg arg2, TCGArg arg3, TCGArg arg4,
- int label_index)
+static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1,
+ TCGReg arg2, int label_index)
{
- tcg_insn_unit *label_ptr;
+ static const MIPSInsn b_zero[16] = {
+ [TCG_COND_LT] = OPC_BLTZ,
+ [TCG_COND_GT] = OPC_BGTZ,
+ [TCG_COND_LE] = OPC_BLEZ,
+ [TCG_COND_GE] = OPC_BGEZ,
+ };
+
+ TCGLabel *l;
+ MIPSInsn s_opc = OPC_SLTU;
+ MIPSInsn b_opc;
+ int cmp_map;
- switch(cond) {
- case TCG_COND_NE:
- tcg_out_brcond(s, TCG_COND_NE, arg2, arg4, label_index);
- tcg_out_brcond(s, TCG_COND_NE, arg1, arg3, label_index);
- return;
+ switch (cond) {
case TCG_COND_EQ:
+ b_opc = OPC_BEQ;
break;
- case TCG_COND_LT:
- case TCG_COND_LE:
- tcg_out_brcond(s, TCG_COND_LT, arg2, arg4, label_index);
+ case TCG_COND_NE:
+ b_opc = OPC_BNE;
break;
+
+ case TCG_COND_LT:
case TCG_COND_GT:
+ case TCG_COND_LE:
case TCG_COND_GE:
- tcg_out_brcond(s, TCG_COND_GT, arg2, arg4, label_index);
- break;
- case TCG_COND_LTU:
- case TCG_COND_LEU:
- tcg_out_brcond(s, TCG_COND_LTU, arg2, arg4, label_index);
- break;
- case TCG_COND_GTU:
- case TCG_COND_GEU:
- tcg_out_brcond(s, TCG_COND_GTU, arg2, arg4, label_index);
- break;
- default:
- tcg_abort();
- }
-
- label_ptr = s->code_ptr;
- tcg_out_opc_br(s, OPC_BNE, arg2, arg4);
- tcg_out_nop(s);
+ if (arg2 == 0) {
+ b_opc = b_zero[cond];
+ arg2 = arg1;
+ arg1 = 0;
+ break;
+ }
+ s_opc = OPC_SLT;
+ /* FALLTHRU */
- switch(cond) {
- case TCG_COND_EQ:
- tcg_out_brcond(s, TCG_COND_EQ, arg1, arg3, label_index);
- break;
- case TCG_COND_LT:
case TCG_COND_LTU:
- tcg_out_brcond(s, TCG_COND_LTU, arg1, arg3, label_index);
- break;
- case TCG_COND_LE:
- case TCG_COND_LEU:
- tcg_out_brcond(s, TCG_COND_LEU, arg1, arg3, label_index);
- break;
- case TCG_COND_GT:
case TCG_COND_GTU:
- tcg_out_brcond(s, TCG_COND_GTU, arg1, arg3, label_index);
- break;
- case TCG_COND_GE:
+ case TCG_COND_LEU:
case TCG_COND_GEU:
- tcg_out_brcond(s, TCG_COND_GEU, arg1, arg3, label_index);
+ cmp_map = mips_cmp_map[cond];
+ if (cmp_map & MIPS_CMP_SWAP) {
+ TCGReg t = arg1;
+ arg1 = arg2;
+ arg2 = t;
+ }
+ tcg_out_opc_reg(s, s_opc, TCG_TMP0, arg1, arg2);
+ b_opc = (cmp_map & MIPS_CMP_INV ? OPC_BEQ : OPC_BNE);
+ arg1 = TCG_TMP0;
+ arg2 = TCG_REG_ZERO;
break;
+
default:
tcg_abort();
+ break;
}
- reloc_pc16(label_ptr, s->code_ptr);
+ tcg_out_opc_br(s, b_opc, arg1, arg2);
+ l = &s->labels[label_index];
+ if (l->has_value) {
+ reloc_pc16(s->code_ptr - 1, l->u.value_ptr);
+ } else {
+ tcg_out_reloc(s, s->code_ptr - 1, R_MIPS_PC16, label_index, 0);
+ }
+ tcg_out_nop(s);
}
-static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret,
- TCGArg c1, TCGArg c2, TCGArg v)
+static TCGReg tcg_out_reduce_eq2(TCGContext *s, TCGReg tmp0, TCGReg tmp1,
+ TCGReg al, TCGReg ah,
+ TCGReg bl, TCGReg bh)
{
- switch (cond) {
- case TCG_COND_EQ:
- if (c1 == 0) {
- tcg_out_opc_reg(s, OPC_MOVZ, ret, v, c2);
- } else if (c2 == 0) {
- tcg_out_opc_reg(s, OPC_MOVZ, ret, v, c1);
+ /* Merge highpart comparison into AH. */
+ if (bh != 0) {
+ if (ah != 0) {
+ tcg_out_opc_reg(s, OPC_XOR, tmp0, ah, bh);
+ ah = tmp0;
} else {
- tcg_out_opc_reg(s, OPC_XOR, TCG_REG_AT, c1, c2);
- tcg_out_opc_reg(s, OPC_MOVZ, ret, v, TCG_REG_AT);
+ ah = bh;
}
- break;
- case TCG_COND_NE:
- if (c1 == 0) {
- tcg_out_opc_reg(s, OPC_MOVN, ret, v, c2);
- } else if (c2 == 0) {
- tcg_out_opc_reg(s, OPC_MOVN, ret, v, c1);
+ }
+ /* Merge lowpart comparison into AL. */
+ if (bl != 0) {
+ if (al != 0) {
+ tcg_out_opc_reg(s, OPC_XOR, tmp1, al, bl);
+ al = tmp1;
} else {
- tcg_out_opc_reg(s, OPC_XOR, TCG_REG_AT, c1, c2);
- tcg_out_opc_reg(s, OPC_MOVN, ret, v, TCG_REG_AT);
+ al = bl;
+ }
+ }
+ /* Merge high and low part comparisons into AL. */
+ if (ah != 0) {
+ if (al != 0) {
+ tcg_out_opc_reg(s, OPC_OR, tmp0, ah, al);
+ al = tmp0;
+ } else {
+ al = ah;
}
- break;
- case TCG_COND_LT:
- tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, c1, c2);
- tcg_out_opc_reg(s, OPC_MOVN, ret, v, TCG_REG_AT);
- break;
- case TCG_COND_LTU:
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, c1, c2);
- tcg_out_opc_reg(s, OPC_MOVN, ret, v, TCG_REG_AT);
- break;
- case TCG_COND_GE:
- tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, c1, c2);
- tcg_out_opc_reg(s, OPC_MOVZ, ret, v, TCG_REG_AT);
- break;
- case TCG_COND_GEU:
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, c1, c2);
- tcg_out_opc_reg(s, OPC_MOVZ, ret, v, TCG_REG_AT);
- break;
- case TCG_COND_LE:
- tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, c2, c1);
- tcg_out_opc_reg(s, OPC_MOVZ, ret, v, TCG_REG_AT);
- break;
- case TCG_COND_LEU:
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, c2, c1);
- tcg_out_opc_reg(s, OPC_MOVZ, ret, v, TCG_REG_AT);
- break;
- case TCG_COND_GT:
- tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, c2, c1);
- tcg_out_opc_reg(s, OPC_MOVN, ret, v, TCG_REG_AT);
- break;
- case TCG_COND_GTU:
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, c2, c1);
- tcg_out_opc_reg(s, OPC_MOVN, ret, v, TCG_REG_AT);
- break;
- default:
- tcg_abort();
- break;
}
+ return al;
}
-static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret,
- TCGArg arg1, TCGArg arg2)
+static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret,
+ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh)
{
+ TCGReg tmp0 = TCG_TMP0;
+ TCGReg tmp1 = ret;
+
+ assert(ret != TCG_TMP0);
+ if (ret == ah || ret == bh) {
+ assert(ret != TCG_TMP1);
+ tmp1 = TCG_TMP1;
+ }
+
switch (cond) {
case TCG_COND_EQ:
- if (arg1 == 0) {
- tcg_out_opc_imm(s, OPC_SLTIU, ret, arg2, 1);
- } else if (arg2 == 0) {
- tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1);
- } else {
- tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2);
- tcg_out_opc_imm(s, OPC_SLTIU, ret, ret, 1);
- }
- break;
case TCG_COND_NE:
- if (arg1 == 0) {
- tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg2);
- } else if (arg2 == 0) {
- tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1);
- } else {
- tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2);
- tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, ret);
- }
- break;
- case TCG_COND_LT:
- tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2);
- break;
- case TCG_COND_LTU:
- tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2);
- break;
- case TCG_COND_GE:
- tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2);
- tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
- break;
- case TCG_COND_GEU:
- tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2);
- tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
+ tmp1 = tcg_out_reduce_eq2(s, tmp0, tmp1, al, ah, bl, bh);
+ tcg_out_setcond(s, cond, ret, tmp1, TCG_REG_ZERO);
break;
- case TCG_COND_LE:
- tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1);
- tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
- break;
- case TCG_COND_LEU:
- tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1);
- tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
- break;
- case TCG_COND_GT:
- tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1);
+
+ default:
+ tcg_out_setcond(s, TCG_COND_EQ, tmp0, ah, bh);
+ tcg_out_setcond(s, tcg_unsigned_cond(cond), tmp1, al, bl);
+ tcg_out_opc_reg(s, OPC_AND, tmp1, tmp1, tmp0);
+ tcg_out_setcond(s, tcg_high_cond(cond), tmp0, ah, bh);
+ tcg_out_opc_reg(s, OPC_OR, ret, tmp1, tmp0);
break;
- case TCG_COND_GTU:
- tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1);
+ }
+}
+
+static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah,
+ TCGReg bl, TCGReg bh, int label_index)
+{
+ TCGCond b_cond = TCG_COND_NE;
+ TCGReg tmp = TCG_TMP1;
+
+ /* With branches, we emit between 4 and 9 insns with 2 or 3 branches.
+ With setcond, we emit between 3 and 10 insns and only 1 branch,
+ which ought to get better branch prediction. */
+ switch (cond) {
+ case TCG_COND_EQ:
+ case TCG_COND_NE:
+ b_cond = cond;
+ tmp = tcg_out_reduce_eq2(s, TCG_TMP0, TCG_TMP1, al, ah, bl, bh);
break;
+
default:
- tcg_abort();
+ /* Minimize code size by prefering a compare not requiring INV. */
+ if (mips_cmp_map[cond] & MIPS_CMP_INV) {
+ cond = tcg_invert_cond(cond);
+ b_cond = TCG_COND_EQ;
+ }
+ tcg_out_setcond2(s, cond, tmp, al, ah, bl, bh);
break;
}
+
+ tcg_out_brcond(s, b_cond, tmp, TCG_REG_ZERO, label_index);
}
-/* XXX: we implement it at the target level to avoid having to
- handle cross basic blocks temporaries */
-static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret,
- TCGArg arg1, TCGArg arg2, TCGArg arg3, TCGArg arg4)
+static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret,
+ TCGReg c1, TCGReg c2, TCGReg v)
{
+ MIPSInsn m_opc = OPC_MOVN;
+
switch (cond) {
case TCG_COND_EQ:
- tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_AT, arg2, arg4);
- tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_T0, arg1, arg3);
- tcg_out_opc_reg(s, OPC_AND, ret, TCG_REG_AT, TCG_REG_T0);
- return;
+ m_opc = OPC_MOVZ;
+ /* FALLTHRU */
case TCG_COND_NE:
- tcg_out_setcond(s, TCG_COND_NE, TCG_REG_AT, arg2, arg4);
- tcg_out_setcond(s, TCG_COND_NE, TCG_REG_T0, arg1, arg3);
- tcg_out_opc_reg(s, OPC_OR, ret, TCG_REG_AT, TCG_REG_T0);
- return;
- case TCG_COND_LT:
- case TCG_COND_LE:
- tcg_out_setcond(s, TCG_COND_LT, TCG_REG_AT, arg2, arg4);
- break;
- case TCG_COND_GT:
- case TCG_COND_GE:
- tcg_out_setcond(s, TCG_COND_GT, TCG_REG_AT, arg2, arg4);
- break;
- case TCG_COND_LTU:
- case TCG_COND_LEU:
- tcg_out_setcond(s, TCG_COND_LTU, TCG_REG_AT, arg2, arg4);
- break;
- case TCG_COND_GTU:
- case TCG_COND_GEU:
- tcg_out_setcond(s, TCG_COND_GTU, TCG_REG_AT, arg2, arg4);
+ if (c2 != 0) {
+ tcg_out_opc_reg(s, OPC_XOR, TCG_TMP0, c1, c2);
+ c1 = TCG_TMP0;
+ }
break;
+
default:
- tcg_abort();
+ /* Minimize code size by prefering a compare not requiring INV. */
+ if (mips_cmp_map[cond] & MIPS_CMP_INV) {
+ cond = tcg_invert_cond(cond);
+ m_opc = OPC_MOVZ;
+ }
+ tcg_out_setcond(s, cond, TCG_TMP0, c1, c2);
+ c1 = TCG_TMP0;
break;
}
- tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_T0, arg2, arg4);
+ tcg_out_opc_reg(s, m_opc, ret, v, c1);
+}
- switch(cond) {
- case TCG_COND_LT:
- case TCG_COND_LTU:
- tcg_out_setcond(s, TCG_COND_LTU, ret, arg1, arg3);
- break;
- case TCG_COND_LE:
- case TCG_COND_LEU:
- tcg_out_setcond(s, TCG_COND_LEU, ret, arg1, arg3);
- break;
- case TCG_COND_GT:
- case TCG_COND_GTU:
- tcg_out_setcond(s, TCG_COND_GTU, ret, arg1, arg3);
- break;
- case TCG_COND_GE:
- case TCG_COND_GEU:
- tcg_out_setcond(s, TCG_COND_GEU, ret, arg1, arg3);
- break;
- default:
- tcg_abort();
+static void tcg_out_call_int(TCGContext *s, tcg_insn_unit *arg, bool tail)
+{
+ /* Note that the ABI requires the called function's address to be
+ loaded into T9, even if a direct branch is in range. */
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T9, (uintptr_t)arg);
+
+ /* But do try a direct branch, allowing the cpu better insn prefetch. */
+ if (tail) {
+ if (!tcg_out_opc_jmp(s, OPC_J, arg)) {
+ tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_T9, 0);
+ }
+ } else {
+ if (!tcg_out_opc_jmp(s, OPC_JAL, arg)) {
+ tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0);
+ }
}
+}
- tcg_out_opc_reg(s, OPC_AND, ret, ret, TCG_REG_T0);
- tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+static void tcg_out_call(TCGContext *s, tcg_insn_unit *arg)
+{
+ tcg_out_call_int(s, arg, false);
+ tcg_out_nop(s);
}
#if defined(CONFIG_SOFTMMU)
-/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr,
- int mmu_idx) */
-static const void * const qemu_ld_helpers[4] = {
- helper_ldb_mmu,
- helper_ldw_mmu,
- helper_ldl_mmu,
- helper_ldq_mmu,
+static void * const qemu_ld_helpers[16] = {
+ [MO_UB] = helper_ret_ldub_mmu,
+ [MO_SB] = helper_ret_ldsb_mmu,
+ [MO_LEUW] = helper_le_lduw_mmu,
+ [MO_LESW] = helper_le_ldsw_mmu,
+ [MO_LEUL] = helper_le_ldul_mmu,
+ [MO_LEQ] = helper_le_ldq_mmu,
+ [MO_BEUW] = helper_be_lduw_mmu,
+ [MO_BESW] = helper_be_ldsw_mmu,
+ [MO_BEUL] = helper_be_ldul_mmu,
+ [MO_BEQ] = helper_be_ldq_mmu,
};
-/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr,
- uintxx_t val, int mmu_idx) */
-static const void * const qemu_st_helpers[4] = {
- helper_stb_mmu,
- helper_stw_mmu,
- helper_stl_mmu,
- helper_stq_mmu,
+static void * const qemu_st_helpers[16] = {
+ [MO_UB] = helper_ret_stb_mmu,
+ [MO_LEUW] = helper_le_stw_mmu,
+ [MO_LEUL] = helper_le_stl_mmu,
+ [MO_LEQ] = helper_le_stq_mmu,
+ [MO_BEUW] = helper_be_stw_mmu,
+ [MO_BEUL] = helper_be_stl_mmu,
+ [MO_BEQ] = helper_be_stq_mmu,
};
-#endif
-static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
- int opc)
+/* Helper routines for marshalling helper function arguments into
+ * the correct registers and stack.
+ * I is where we want to put this argument, and is updated and returned
+ * for the next call. ARG is the argument itself.
+ *
+ * We provide routines for arguments which are: immediate, 32 bit
+ * value in register, 16 and 8 bit values in register (which must be zero
+ * extended before use) and 64 bit value in a lo:hi register pair.
+ */
+
+static int tcg_out_call_iarg_reg(TCGContext *s, int i, TCGReg arg)
{
- TCGReg addr_regl, data_regl, data_regh, data_reg1, data_reg2;
-#if defined(CONFIG_SOFTMMU)
- tcg_insn_unit *label1_ptr, *label2_ptr;
- int arg_num;
- int mem_index, s_bits;
- int addr_meml;
-# if TARGET_LONG_BITS == 64
- tcg_insn_unit *label3_ptr;
- TCGReg addr_regh;
- int addr_memh;
-# endif
-#endif
- data_regl = *args++;
- if (opc == 3)
- data_regh = *args++;
- else
- data_regh = 0;
- addr_regl = *args++;
-#if defined(CONFIG_SOFTMMU)
-# if TARGET_LONG_BITS == 64
- addr_regh = *args++;
-# if defined(HOST_WORDS_BIGENDIAN)
- addr_memh = 0;
- addr_meml = 4;
-# else
- addr_memh = 4;
- addr_meml = 0;
-# endif
-# else
- addr_meml = 0;
-# endif
- mem_index = *args;
- s_bits = opc & 3;
-#endif
+ if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) {
+ tcg_out_mov(s, TCG_TYPE_REG, tcg_target_call_iarg_regs[i], arg);
+ } else {
+ tcg_out_st(s, TCG_TYPE_REG, arg, TCG_REG_SP, 4 * i);
+ }
+ return i + 1;
+}
- if (opc == 3) {
-#if defined(HOST_WORDS_BIGENDIAN)
- data_reg1 = data_regh;
- data_reg2 = data_regl;
-#else
- data_reg1 = data_regl;
- data_reg2 = data_regh;
-#endif
+static int tcg_out_call_iarg_reg8(TCGContext *s, int i, TCGReg arg)
+{
+ TCGReg tmp = TCG_TMP0;
+ if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) {
+ tmp = tcg_target_call_iarg_regs[i];
+ }
+ tcg_out_opc_imm(s, OPC_ANDI, tmp, arg, 0xff);
+ return tcg_out_call_iarg_reg(s, i, tmp);
+}
+
+static int tcg_out_call_iarg_reg16(TCGContext *s, int i, TCGReg arg)
+{
+ TCGReg tmp = TCG_TMP0;
+ if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) {
+ tmp = tcg_target_call_iarg_regs[i];
+ }
+ tcg_out_opc_imm(s, OPC_ANDI, tmp, arg, 0xffff);
+ return tcg_out_call_iarg_reg(s, i, tmp);
+}
+
+static int tcg_out_call_iarg_imm(TCGContext *s, int i, TCGArg arg)
+{
+ TCGReg tmp = TCG_TMP0;
+ if (arg == 0) {
+ tmp = TCG_REG_ZERO;
} else {
- data_reg1 = data_regl;
- data_reg2 = 0;
+ if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) {
+ tmp = tcg_target_call_iarg_regs[i];
+ }
+ tcg_out_movi(s, TCG_TYPE_REG, tmp, arg);
}
-#if defined(CONFIG_SOFTMMU)
- tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addr_regl, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
- tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+ return tcg_out_call_iarg_reg(s, i, tmp);
+}
+
+static int tcg_out_call_iarg_reg2(TCGContext *s, int i, TCGReg al, TCGReg ah)
+{
+ i = (i + 1) & ~1;
+ i = tcg_out_call_iarg_reg(s, i, (MIPS_BE ? ah : al));
+ i = tcg_out_call_iarg_reg(s, i, (MIPS_BE ? al : ah));
+ return i;
+}
+
+/* Perform the tlb comparison operation. The complete host address is
+ placed in BASE. Clobbers AT, T0, A0. */
+static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl,
+ TCGReg addrh, int mem_index, TCGMemOp s_bits,
+ tcg_insn_unit *label_ptr[2], bool is_load)
+{
+ int cmp_off
+ = (is_load
+ ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read)
+ : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write));
+ int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend);
+
+ tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addrl,
+ TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0,
+ (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, TCG_AREG0);
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0,
- offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + addr_meml);
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T0, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
- tcg_out_opc_reg(s, OPC_AND, TCG_REG_T0, TCG_REG_T0, addr_regl);
-
-# if TARGET_LONG_BITS == 64
- label3_ptr = s->code_ptr;
- tcg_out_opc_br(s, OPC_BNE, TCG_REG_T0, TCG_REG_AT);
- tcg_out_nop(s);
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0,
- offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + addr_memh);
+ /* Compensate for very large offsets. */
+ if (add_off >= 0x8000) {
+ /* Most target env are smaller than 32k; none are larger than 64k.
+ Simplify the logic here merely to offset by 0x7ff0, giving us a
+ range just shy of 64k. Check this assumption. */
+ QEMU_BUILD_BUG_ON(offsetof(CPUArchState,
+ tlb_table[NB_MMU_MODES - 1][1])
+ > 0x7ff0 + 0x7fff);
+ tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_A0, TCG_REG_A0, 0x7ff0);
+ cmp_off -= 0x7ff0;
+ add_off -= 0x7ff0;
+ }
- label1_ptr = s->code_ptr;
- tcg_out_opc_br(s, OPC_BEQ, addr_regh, TCG_REG_AT);
- tcg_out_nop(s);
+ /* Load the tlb comparator. */
+ tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off + LO_OFF);
+ if (TARGET_LONG_BITS == 64) {
+ tcg_out_opc_imm(s, OPC_LW, base, TCG_REG_A0, cmp_off + HI_OFF);
+ }
- reloc_pc16(label3_ptr, s->code_ptr);
-# else
- label1_ptr = s->code_ptr;
- tcg_out_opc_br(s, OPC_BEQ, TCG_REG_T0, TCG_REG_AT);
- tcg_out_nop(s);
-# endif
-
- /* slow path */
- arg_num = 0;
- tcg_out_call_iarg_reg32(s, &arg_num, TCG_AREG0);
-# if TARGET_LONG_BITS == 64
- tcg_out_call_iarg_reg64(s, &arg_num, addr_regl, addr_regh);
-# else
- tcg_out_call_iarg_reg32(s, &arg_num, addr_regl);
-# endif
- tcg_out_call_iarg_imm32(s, &arg_num, mem_index);
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T9, (tcg_target_long)qemu_ld_helpers[s_bits]);
- tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0);
- tcg_out_nop(s);
+ /* Mask the page bits, keeping the alignment bits to compare against.
+ In between, load the tlb addend for the fast path. */
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_TMP1,
+ TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0, add_off);
+ tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrl);
- switch(opc) {
- case 0:
- tcg_out_opc_imm(s, OPC_ANDI, data_reg1, TCG_REG_V0, 0xff);
- break;
- case 0 | 4:
- tcg_out_ext8s(s, data_reg1, TCG_REG_V0);
- break;
- case 1:
- tcg_out_opc_imm(s, OPC_ANDI, data_reg1, TCG_REG_V0, 0xffff);
- break;
- case 1 | 4:
- tcg_out_ext16s(s, data_reg1, TCG_REG_V0);
- break;
- case 2:
- tcg_out_mov(s, TCG_TYPE_I32, data_reg1, TCG_REG_V0);
- break;
- case 3:
- tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_V1);
- tcg_out_mov(s, TCG_TYPE_I32, data_reg1, TCG_REG_V0);
- break;
- default:
- tcg_abort();
+ label_ptr[0] = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0);
+
+ if (TARGET_LONG_BITS == 64) {
+ /* delay slot */
+ tcg_out_nop(s);
+
+ label_ptr[1] = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BNE, addrh, base);
}
- label2_ptr = s->code_ptr;
+ /* delay slot */
+ tcg_out_opc_reg(s, OPC_ADDU, base, TCG_REG_A0, addrl);
+}
+
+static void add_qemu_ldst_label(TCGContext *s, int is_ld, TCGMemOp opc,
+ TCGReg datalo, TCGReg datahi,
+ TCGReg addrlo, TCGReg addrhi,
+ int mem_index, void *raddr,
+ tcg_insn_unit *label_ptr[2])
+{
+ TCGLabelQemuLdst *label = new_ldst_label(s);
+
+ label->is_ld = is_ld;
+ label->opc = opc;
+ label->datalo_reg = datalo;
+ label->datahi_reg = datahi;
+ label->addrlo_reg = addrlo;
+ label->addrhi_reg = addrhi;
+ label->mem_index = mem_index;
+ label->raddr = raddr;
+ label->label_ptr[0] = label_ptr[0];
+ if (TARGET_LONG_BITS == 64) {
+ label->label_ptr[1] = label_ptr[1];
+ }
+}
+
+static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
+{
+ TCGMemOp opc = l->opc;
+ TCGReg v0;
+ int i;
+
+ /* resolve label address */
+ reloc_pc16(l->label_ptr[0], s->code_ptr);
+ if (TARGET_LONG_BITS == 64) {
+ reloc_pc16(l->label_ptr[1], s->code_ptr);
+ }
+
+ i = 1;
+ if (TARGET_LONG_BITS == 64) {
+ i = tcg_out_call_iarg_reg2(s, i, l->addrlo_reg, l->addrhi_reg);
+ } else {
+ i = tcg_out_call_iarg_reg(s, i, l->addrlo_reg);
+ }
+ i = tcg_out_call_iarg_imm(s, i, l->mem_index);
+ i = tcg_out_call_iarg_imm(s, i, (intptr_t)l->raddr);
+ tcg_out_call_int(s, qemu_ld_helpers[opc], false);
+ /* delay slot */
+ tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0);
+
+ v0 = l->datalo_reg;
+ if ((opc & MO_SIZE) == MO_64) {
+ /* We eliminated V0 from the possible output registers, so it
+ cannot be clobbered here. So we must move V1 first. */
+ if (MIPS_BE) {
+ tcg_out_mov(s, TCG_TYPE_I32, v0, TCG_REG_V1);
+ v0 = l->datahi_reg;
+ } else {
+ tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_V1);
+ }
+ }
+
+ reloc_pc16(s->code_ptr, l->raddr);
tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO);
- tcg_out_nop(s);
+ /* delay slot */
+ tcg_out_mov(s, TCG_TYPE_REG, v0, TCG_REG_V0);
+}
- /* label1: fast path */
- reloc_pc16(label1_ptr, s->code_ptr);
+static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
+{
+ TCGMemOp opc = l->opc;
+ TCGMemOp s_bits = opc & MO_SIZE;
+ int i;
+
+ /* resolve label address */
+ reloc_pc16(l->label_ptr[0], s->code_ptr);
+ if (TARGET_LONG_BITS == 64) {
+ reloc_pc16(l->label_ptr[1], s->code_ptr);
+ }
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0,
- offsetof(CPUArchState, tlb_table[mem_index][0].addend));
- tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_V0, TCG_REG_A0, addr_regl);
-#else
- if (GUEST_BASE == (int16_t)GUEST_BASE) {
- tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_V0, addr_regl, GUEST_BASE);
+ i = 1;
+ if (TARGET_LONG_BITS == 64) {
+ i = tcg_out_call_iarg_reg2(s, i, l->addrlo_reg, l->addrhi_reg);
} else {
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_V0, GUEST_BASE);
- tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_V0, TCG_REG_V0, addr_regl);
+ i = tcg_out_call_iarg_reg(s, i, l->addrlo_reg);
+ }
+ switch (s_bits) {
+ case MO_8:
+ i = tcg_out_call_iarg_reg8(s, i, l->datalo_reg);
+ break;
+ case MO_16:
+ i = tcg_out_call_iarg_reg16(s, i, l->datalo_reg);
+ break;
+ case MO_32:
+ i = tcg_out_call_iarg_reg(s, i, l->datalo_reg);
+ break;
+ case MO_64:
+ i = tcg_out_call_iarg_reg2(s, i, l->datalo_reg, l->datahi_reg);
+ break;
+ default:
+ tcg_abort();
}
+ i = tcg_out_call_iarg_imm(s, i, l->mem_index);
+
+ /* Tail call to the store helper. Thus force the return address
+ computation to take place in the return address register. */
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (intptr_t)l->raddr);
+ i = tcg_out_call_iarg_reg(s, i, TCG_REG_RA);
+ tcg_out_call_int(s, qemu_st_helpers[opc], true);
+ /* delay slot */
+ tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0);
+}
#endif
- switch(opc) {
- case 0:
- tcg_out_opc_imm(s, OPC_LBU, data_reg1, TCG_REG_V0, 0);
+static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi,
+ TCGReg base, TCGMemOp opc)
+{
+ switch (opc) {
+ case MO_UB:
+ tcg_out_opc_imm(s, OPC_LBU, datalo, base, 0);
break;
- case 0 | 4:
- tcg_out_opc_imm(s, OPC_LB, data_reg1, TCG_REG_V0, 0);
+ case MO_SB:
+ tcg_out_opc_imm(s, OPC_LB, datalo, base, 0);
break;
- case 1:
- if (TCG_NEED_BSWAP) {
- tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, TCG_REG_V0, 0);
- tcg_out_bswap16(s, data_reg1, TCG_REG_T0);
- } else {
- tcg_out_opc_imm(s, OPC_LHU, data_reg1, TCG_REG_V0, 0);
- }
+ case MO_UW | MO_BSWAP:
+ tcg_out_opc_imm(s, OPC_LHU, TCG_TMP1, base, 0);
+ tcg_out_bswap16(s, datalo, TCG_TMP1);
break;
- case 1 | 4:
- if (TCG_NEED_BSWAP) {
- tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, TCG_REG_V0, 0);
- tcg_out_bswap16s(s, data_reg1, TCG_REG_T0);
- } else {
- tcg_out_opc_imm(s, OPC_LH, data_reg1, TCG_REG_V0, 0);
- }
+ case MO_UW:
+ tcg_out_opc_imm(s, OPC_LHU, datalo, base, 0);
break;
- case 2:
- if (TCG_NEED_BSWAP) {
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 0);
- tcg_out_bswap32(s, data_reg1, TCG_REG_T0);
- } else {
- tcg_out_opc_imm(s, OPC_LW, data_reg1, TCG_REG_V0, 0);
- }
+ case MO_SW | MO_BSWAP:
+ tcg_out_opc_imm(s, OPC_LHU, TCG_TMP1, base, 0);
+ tcg_out_bswap16s(s, datalo, TCG_TMP1);
break;
- case 3:
- if (TCG_NEED_BSWAP) {
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 4);
- tcg_out_bswap32(s, data_reg1, TCG_REG_T0);
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 0);
- tcg_out_bswap32(s, data_reg2, TCG_REG_T0);
- } else {
- tcg_out_opc_imm(s, OPC_LW, data_reg1, TCG_REG_V0, 0);
- tcg_out_opc_imm(s, OPC_LW, data_reg2, TCG_REG_V0, 4);
- }
+ case MO_SW:
+ tcg_out_opc_imm(s, OPC_LH, datalo, base, 0);
+ break;
+ case MO_UL | MO_BSWAP:
+ tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, 0);
+ tcg_out_bswap32(s, datalo, TCG_TMP1);
+ break;
+ case MO_UL:
+ tcg_out_opc_imm(s, OPC_LW, datalo, base, 0);
+ break;
+ case MO_Q | MO_BSWAP:
+ tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, HI_OFF);
+ tcg_out_bswap32(s, datalo, TCG_TMP1);
+ tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, LO_OFF);
+ tcg_out_bswap32(s, datahi, TCG_TMP1);
+ break;
+ case MO_Q:
+ tcg_out_opc_imm(s, OPC_LW, datalo, base, LO_OFF);
+ tcg_out_opc_imm(s, OPC_LW, datahi, base, HI_OFF);
break;
default:
tcg_abort();
}
-
-#if defined(CONFIG_SOFTMMU)
- reloc_pc16(label2_ptr, s->code_ptr);
-#endif
}
-static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
- int opc)
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64)
{
- TCGReg addr_regl, data_regl, data_regh, data_reg1, data_reg2;
+ TCGReg addr_regl, addr_regh __attribute__((unused));
+ TCGReg data_regl, data_regh;
+ TCGMemOp opc;
#if defined(CONFIG_SOFTMMU)
- tcg_insn_unit *label1_ptr, *label2_ptr;
- int arg_num;
- int mem_index, s_bits;
- int addr_meml;
-#endif
-#if TARGET_LONG_BITS == 64
-# if defined(CONFIG_SOFTMMU)
- tcg_insn_unit *label3_ptr;
- TCGReg addr_regh;
- int addr_memh;
-# endif
+ tcg_insn_unit *label_ptr[2];
+ int mem_index;
+ TCGMemOp s_bits;
#endif
+ /* Note that we've eliminated V0 from the output registers,
+ so we won't overwrite the base register during loading. */
+ TCGReg base = TCG_REG_V0;
+
data_regl = *args++;
- if (opc == 3) {
- data_regh = *args++;
- } else {
- data_regh = 0;
- }
+ data_regh = (is_64 ? *args++ : 0);
addr_regl = *args++;
+ addr_regh = (TARGET_LONG_BITS == 64 ? *args++ : 0);
+ opc = *args++;
+
#if defined(CONFIG_SOFTMMU)
-# if TARGET_LONG_BITS == 64
- addr_regh = *args++;
-# if defined(HOST_WORDS_BIGENDIAN)
- addr_memh = 0;
- addr_meml = 4;
-# else
- addr_memh = 4;
- addr_meml = 0;
-# endif
-# else
- addr_meml = 0;
-# endif
mem_index = *args;
- s_bits = opc;
-#endif
+ s_bits = opc & MO_SIZE;
- if (opc == 3) {
-#if defined(HOST_WORDS_BIGENDIAN)
- data_reg1 = data_regh;
- data_reg2 = data_regl;
+ tcg_out_tlb_load(s, base, addr_regl, addr_regh, mem_index,
+ s_bits, label_ptr, 1);
+ tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc);
+ add_qemu_ldst_label(s, 1, opc, data_regl, data_regh, addr_regl, addr_regh,
+ mem_index, s->code_ptr, label_ptr);
#else
- data_reg1 = data_regl;
- data_reg2 = data_regh;
-#endif
+ if (GUEST_BASE == 0 && data_regl != addr_regl) {
+ base = addr_regl;
+ } else if (GUEST_BASE == (int16_t)GUEST_BASE) {
+ tcg_out_opc_imm(s, OPC_ADDIU, base, addr_regl, GUEST_BASE);
} else {
- data_reg1 = data_regl;
- data_reg2 = 0;
+ tcg_out_movi(s, TCG_TYPE_PTR, base, GUEST_BASE);
+ tcg_out_opc_reg(s, OPC_ADDU, base, base, addr_regl);
}
+ tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc);
+#endif
+}
-#if defined(CONFIG_SOFTMMU)
- tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addr_regl, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
- tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
- tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, TCG_AREG0);
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0,
- offsetof(CPUArchState, tlb_table[mem_index][0].addr_write) + addr_meml);
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T0, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
- tcg_out_opc_reg(s, OPC_AND, TCG_REG_T0, TCG_REG_T0, addr_regl);
-
-# if TARGET_LONG_BITS == 64
- label3_ptr = s->code_ptr;
- tcg_out_opc_br(s, OPC_BNE, TCG_REG_T0, TCG_REG_AT);
- tcg_out_nop(s);
-
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0,
- offsetof(CPUArchState, tlb_table[mem_index][0].addr_write) + addr_memh);
-
- label1_ptr = s->code_ptr;
- tcg_out_opc_br(s, OPC_BEQ, addr_regh, TCG_REG_AT);
- tcg_out_nop(s);
-
- reloc_pc16(label3_ptr, s->code_ptr);
-# else
- label1_ptr = s->code_ptr;
- tcg_out_opc_br(s, OPC_BEQ, TCG_REG_T0, TCG_REG_AT);
- tcg_out_nop(s);
-# endif
+static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi,
+ TCGReg base, TCGMemOp opc)
+{
+ switch (opc) {
+ case MO_8:
+ tcg_out_opc_imm(s, OPC_SB, datalo, base, 0);
+ break;
- /* slow path */
- arg_num = 0;
- tcg_out_call_iarg_reg32(s, &arg_num, TCG_AREG0);
-# if TARGET_LONG_BITS == 64
- tcg_out_call_iarg_reg64(s, &arg_num, addr_regl, addr_regh);
-# else
- tcg_out_call_iarg_reg32(s, &arg_num, addr_regl);
-# endif
- switch(opc) {
- case 0:
- tcg_out_call_iarg_reg8(s, &arg_num, data_regl);
+ case MO_16 | MO_BSWAP:
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP1, datalo, 0xffff);
+ tcg_out_bswap16(s, TCG_TMP1, TCG_TMP1);
+ datalo = TCG_TMP1;
+ /* FALLTHRU */
+ case MO_16:
+ tcg_out_opc_imm(s, OPC_SH, datalo, base, 0);
break;
- case 1:
- tcg_out_call_iarg_reg16(s, &arg_num, data_regl);
+
+ case MO_32 | MO_BSWAP:
+ tcg_out_bswap32(s, TCG_TMP1, datalo);
+ datalo = TCG_TMP1;
+ /* FALLTHRU */
+ case MO_32:
+ tcg_out_opc_imm(s, OPC_SW, datalo, base, 0);
break;
- case 2:
- tcg_out_call_iarg_reg32(s, &arg_num, data_regl);
+
+ case MO_64 | MO_BSWAP:
+ tcg_out_bswap32(s, TCG_TMP1, datalo);
+ tcg_out_opc_imm(s, OPC_SW, TCG_TMP1, base, HI_OFF);
+ tcg_out_bswap32(s, TCG_TMP1, datahi);
+ tcg_out_opc_imm(s, OPC_SW, TCG_TMP1, base, LO_OFF);
break;
- case 3:
- tcg_out_call_iarg_reg64(s, &arg_num, data_regl, data_regh);
+ case MO_64:
+ tcg_out_opc_imm(s, OPC_SW, datalo, base, LO_OFF);
+ tcg_out_opc_imm(s, OPC_SW, datahi, base, HI_OFF);
break;
+
default:
tcg_abort();
}
- tcg_out_call_iarg_imm32(s, &arg_num, mem_index);
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T9, (tcg_target_long)qemu_st_helpers[s_bits]);
- tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0);
- tcg_out_nop(s);
-
- label2_ptr = s->code_ptr;
- tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO);
- tcg_out_nop(s);
+}
- /* label1: fast path */
- reloc_pc16(label1_ptr, s->code_ptr);
+static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al,
+ TCGReg ah, TCGArg bl, TCGArg bh, bool cbl,
+ bool cbh, bool is_sub)
+{
+ TCGReg th = TCG_TMP1;
+
+ /* If we have a negative constant such that negating it would
+ make the high part zero, we can (usually) eliminate one insn. */
+ if (cbl && cbh && bh == -1 && bl != 0) {
+ bl = -bl;
+ bh = 0;
+ is_sub = !is_sub;
+ }
- tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0,
- offsetof(CPUArchState, tlb_table[mem_index][0].addend));
- tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, addr_regl);
-#else
- if (GUEST_BASE == (int16_t)GUEST_BASE) {
- tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_A0, addr_regl, GUEST_BASE);
+ /* By operating on the high part first, we get to use the final
+ carry operation to move back from the temporary. */
+ if (!cbh) {
+ tcg_out_opc_reg(s, (is_sub ? OPC_SUBU : OPC_ADDU), th, ah, bh);
+ } else if (bh != 0 || ah == rl) {
+ tcg_out_opc_imm(s, OPC_ADDIU, th, ah, (is_sub ? -bh : bh));
} else {
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, GUEST_BASE);
- tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, addr_regl);
+ th = ah;
}
-#endif
-
- switch(opc) {
- case 0:
- tcg_out_opc_imm(s, OPC_SB, data_reg1, TCG_REG_A0, 0);
- break;
- case 1:
- if (TCG_NEED_BSWAP) {
- tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_T0, data_reg1, 0xffff);
- tcg_out_bswap16(s, TCG_REG_T0, TCG_REG_T0);
- tcg_out_opc_imm(s, OPC_SH, TCG_REG_T0, TCG_REG_A0, 0);
+ /* Note that tcg optimization should eliminate the bl == 0 case. */
+ if (is_sub) {
+ if (cbl) {
+ tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, al, bl);
+ tcg_out_opc_imm(s, OPC_ADDIU, rl, al, -bl);
} else {
- tcg_out_opc_imm(s, OPC_SH, data_reg1, TCG_REG_A0, 0);
+ tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, al, bl);
+ tcg_out_opc_reg(s, OPC_SUBU, rl, al, bl);
}
- break;
- case 2:
- if (TCG_NEED_BSWAP) {
- tcg_out_bswap32(s, TCG_REG_T0, data_reg1);
- tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 0);
- } else {
- tcg_out_opc_imm(s, OPC_SW, data_reg1, TCG_REG_A0, 0);
- }
- break;
- case 3:
- if (TCG_NEED_BSWAP) {
- tcg_out_bswap32(s, TCG_REG_T0, data_reg2);
- tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 0);
- tcg_out_bswap32(s, TCG_REG_T0, data_reg1);
- tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 4);
+ tcg_out_opc_reg(s, OPC_SUBU, rh, th, TCG_TMP0);
+ } else {
+ if (cbl) {
+ tcg_out_opc_imm(s, OPC_ADDIU, rl, al, bl);
+ tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, rl, bl);
} else {
- tcg_out_opc_imm(s, OPC_SW, data_reg1, TCG_REG_A0, 0);
- tcg_out_opc_imm(s, OPC_SW, data_reg2, TCG_REG_A0, 4);
+ tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl);
+ tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, rl, (rl == bl ? al : bl));
}
- break;
- default:
- tcg_abort();
+ tcg_out_opc_reg(s, OPC_ADDU, rh, th, TCG_TMP0);
}
+}
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64)
+{
+ TCGReg addr_regl, addr_regh __attribute__((unused));
+ TCGReg data_regl, data_regh, base;
+ TCGMemOp opc;
#if defined(CONFIG_SOFTMMU)
- reloc_pc16(label2_ptr, s->code_ptr);
+ tcg_insn_unit *label_ptr[2];
+ int mem_index;
+ TCGMemOp s_bits;
#endif
-}
-static void tcg_out_call(TCGContext *s, tcg_insn_unit *target)
-{
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T9, (intptr_t)target);
- tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0);
- tcg_out_nop(s);
+ data_regl = *args++;
+ data_regh = (is_64 ? *args++ : 0);
+ addr_regl = *args++;
+ addr_regh = (TARGET_LONG_BITS == 64 ? *args++ : 0);
+ opc = *args++;
+
+#if defined(CONFIG_SOFTMMU)
+ mem_index = *args;
+ s_bits = opc & 3;
+
+ /* Note that we eliminated the helper's address argument,
+ so we can reuse that for the base. */
+ base = (TARGET_LONG_BITS == 32 ? TCG_REG_A1 : TCG_REG_A2);
+ tcg_out_tlb_load(s, base, addr_regl, addr_regh, mem_index,
+ s_bits, label_ptr, 1);
+ tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc);
+ add_qemu_ldst_label(s, 0, opc, data_regl, data_regh, addr_regl, addr_regh,
+ mem_index, s->code_ptr, label_ptr);
+#else
+ if (GUEST_BASE == 0) {
+ base = addr_regl;
+ } else {
+ base = TCG_REG_A0;
+ if (GUEST_BASE == (int16_t)GUEST_BASE) {
+ tcg_out_opc_imm(s, OPC_ADDIU, base, addr_regl, GUEST_BASE);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, base, GUEST_BASE);
+ tcg_out_opc_reg(s, OPC_ADDU, base, base, addr_regl);
+ }
+ }
+ tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc);
+#endif
}
static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
const TCGArg *args, const int *const_args)
{
- switch(opc) {
+ MIPSInsn i1, i2;
+ TCGArg a0, a1, a2;
+ int c2;
+
+ a0 = args[0];
+ a1 = args[1];
+ a2 = args[2];
+ c2 = const_args[2];
+
+ switch (opc) {
case INDEX_op_exit_tb:
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_V0, args[0]);
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, (uintptr_t)tb_ret_addr);
- tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_AT, 0);
- tcg_out_nop(s);
+ {
+ TCGReg b0 = TCG_REG_ZERO;
+
+ if (a0 & ~0xffff) {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_V0, a0 & ~0xffff);
+ b0 = TCG_REG_V0;
+ }
+ if (!tcg_out_opc_jmp(s, OPC_J, tb_ret_addr)) {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0,
+ (uintptr_t)tb_ret_addr);
+ tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0);
+ }
+ tcg_out_opc_imm(s, OPC_ORI, TCG_REG_V0, b0, a0 & 0xffff);
+ }
break;
case INDEX_op_goto_tb:
if (s->tb_jmp_offset) {
/* direct jump method */
- tcg_abort();
+ s->tb_jmp_offset[a0] = tcg_current_code_size(s);
+ /* Avoid clobbering the address during retranslation. */
+ tcg_out32(s, OPC_J | (*(uint32_t *)s->code_ptr & 0x3ffffff));
} else {
/* indirect jump method */
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT,
- (uintptr_t)(s->tb_next + args[0]));
- tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_AT, TCG_REG_AT, 0);
- tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_AT, 0);
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_REG_ZERO,
+ (uintptr_t)(s->tb_next + a0));
+ tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0);
}
tcg_out_nop(s);
- s->tb_next_offset[args[0]] = tcg_current_code_size(s);
+ s->tb_next_offset[a0] = tcg_current_code_size(s);
break;
case INDEX_op_br:
- tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, args[0]);
+ tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, a0);
break;
case INDEX_op_ld8u_i32:
- tcg_out_ldst(s, OPC_LBU, args[0], args[1], args[2]);
- break;
+ i1 = OPC_LBU;
+ goto do_ldst;
case INDEX_op_ld8s_i32:
- tcg_out_ldst(s, OPC_LB, args[0], args[1], args[2]);
- break;
+ i1 = OPC_LB;
+ goto do_ldst;
case INDEX_op_ld16u_i32:
- tcg_out_ldst(s, OPC_LHU, args[0], args[1], args[2]);
- break;
+ i1 = OPC_LHU;
+ goto do_ldst;
case INDEX_op_ld16s_i32:
- tcg_out_ldst(s, OPC_LH, args[0], args[1], args[2]);
- break;
+ i1 = OPC_LH;
+ goto do_ldst;
case INDEX_op_ld_i32:
- tcg_out_ldst(s, OPC_LW, args[0], args[1], args[2]);
- break;
+ i1 = OPC_LW;
+ goto do_ldst;
case INDEX_op_st8_i32:
- tcg_out_ldst(s, OPC_SB, args[0], args[1], args[2]);
- break;
+ i1 = OPC_SB;
+ goto do_ldst;
case INDEX_op_st16_i32:
- tcg_out_ldst(s, OPC_SH, args[0], args[1], args[2]);
- break;
+ i1 = OPC_SH;
+ goto do_ldst;
case INDEX_op_st_i32:
- tcg_out_ldst(s, OPC_SW, args[0], args[1], args[2]);
+ i1 = OPC_SW;
+ do_ldst:
+ tcg_out_ldst(s, i1, a0, a1, a2);
break;
case INDEX_op_add_i32:
- if (const_args[2]) {
- tcg_out_opc_imm(s, OPC_ADDIU, args[0], args[1], args[2]);
- } else {
- tcg_out_opc_reg(s, OPC_ADDU, args[0], args[1], args[2]);
- }
- break;
- case INDEX_op_add2_i32:
- if (const_args[4]) {
- tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_AT, args[2], args[4]);
- } else {
- tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_AT, args[2], args[4]);
- }
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_T0, TCG_REG_AT, args[2]);
- if (const_args[5]) {
- tcg_out_opc_imm(s, OPC_ADDIU, args[1], args[3], args[5]);
- } else {
- tcg_out_opc_reg(s, OPC_ADDU, args[1], args[3], args[5]);
+ i1 = OPC_ADDU, i2 = OPC_ADDIU;
+ goto do_binary;
+ case INDEX_op_or_i32:
+ i1 = OPC_OR, i2 = OPC_ORI;
+ goto do_binary;
+ case INDEX_op_xor_i32:
+ i1 = OPC_XOR, i2 = OPC_XORI;
+ do_binary:
+ if (c2) {
+ tcg_out_opc_imm(s, i2, a0, a1, a2);
+ break;
}
- tcg_out_opc_reg(s, OPC_ADDU, args[1], args[1], TCG_REG_T0);
- tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT);
+ do_binaryv:
+ tcg_out_opc_reg(s, i1, a0, a1, a2);
break;
+
case INDEX_op_sub_i32:
- if (const_args[2]) {
- tcg_out_opc_imm(s, OPC_ADDIU, args[0], args[1], -args[2]);
- } else {
- tcg_out_opc_reg(s, OPC_SUBU, args[0], args[1], args[2]);
+ if (c2) {
+ tcg_out_opc_imm(s, OPC_ADDIU, a0, a1, -a2);
+ break;
}
- break;
- case INDEX_op_sub2_i32:
- if (const_args[4]) {
- tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_AT, args[2], -args[4]);
- } else {
- tcg_out_opc_reg(s, OPC_SUBU, TCG_REG_AT, args[2], args[4]);
- }
- tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_T0, args[2], TCG_REG_AT);
- if (const_args[5]) {
- tcg_out_opc_imm(s, OPC_ADDIU, args[1], args[3], -args[5]);
- } else {
- tcg_out_opc_reg(s, OPC_SUBU, args[1], args[3], args[5]);
+ i1 = OPC_SUBU;
+ goto do_binary;
+ case INDEX_op_and_i32:
+ if (c2 && a2 != (uint16_t)a2) {
+ int msb = ctz32(~a2) - 1;
+ assert(use_mips32r2_instructions);
+ assert(is_p2m1(a2));
+ tcg_out_opc_bf(s, OPC_EXT, a0, a1, msb, 0);
+ break;
}
- tcg_out_opc_reg(s, OPC_SUBU, args[1], args[1], TCG_REG_T0);
- tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT);
- break;
+ i1 = OPC_AND, i2 = OPC_ANDI;
+ goto do_binary;
+ case INDEX_op_nor_i32:
+ i1 = OPC_NOR;
+ goto do_binaryv;
+
case INDEX_op_mul_i32:
if (use_mips32_instructions) {
- tcg_out_opc_reg(s, OPC_MUL, args[0], args[1], args[2]);
- } else {
- tcg_out_opc_reg(s, OPC_MULT, 0, args[1], args[2]);
- tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
+ tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2);
+ break;
}
- break;
- case INDEX_op_muls2_i32:
- tcg_out_opc_reg(s, OPC_MULT, 0, args[2], args[3]);
- tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
- tcg_out_opc_reg(s, OPC_MFHI, args[1], 0, 0);
- break;
- case INDEX_op_mulu2_i32:
- tcg_out_opc_reg(s, OPC_MULTU, 0, args[2], args[3]);
- tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
- tcg_out_opc_reg(s, OPC_MFHI, args[1], 0, 0);
- break;
+ i1 = OPC_MULT, i2 = OPC_MFLO;
+ goto do_hilo1;
case INDEX_op_mulsh_i32:
- tcg_out_opc_reg(s, OPC_MULT, 0, args[1], args[2]);
- tcg_out_opc_reg(s, OPC_MFHI, args[0], 0, 0);
- break;
+ i1 = OPC_MULT, i2 = OPC_MFHI;
+ goto do_hilo1;
case INDEX_op_muluh_i32:
- tcg_out_opc_reg(s, OPC_MULTU, 0, args[1], args[2]);
- tcg_out_opc_reg(s, OPC_MFHI, args[0], 0, 0);
- break;
+ i1 = OPC_MULTU, i2 = OPC_MFHI;
+ goto do_hilo1;
case INDEX_op_div_i32:
- tcg_out_opc_reg(s, OPC_DIV, 0, args[1], args[2]);
- tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
- break;
+ i1 = OPC_DIV, i2 = OPC_MFLO;
+ goto do_hilo1;
case INDEX_op_divu_i32:
- tcg_out_opc_reg(s, OPC_DIVU, 0, args[1], args[2]);
- tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
- break;
+ i1 = OPC_DIVU, i2 = OPC_MFLO;
+ goto do_hilo1;
case INDEX_op_rem_i32:
- tcg_out_opc_reg(s, OPC_DIV, 0, args[1], args[2]);
- tcg_out_opc_reg(s, OPC_MFHI, args[0], 0, 0);
- break;
+ i1 = OPC_DIV, i2 = OPC_MFHI;
+ goto do_hilo1;
case INDEX_op_remu_i32:
- tcg_out_opc_reg(s, OPC_DIVU, 0, args[1], args[2]);
- tcg_out_opc_reg(s, OPC_MFHI, args[0], 0, 0);
+ i1 = OPC_DIVU, i2 = OPC_MFHI;
+ do_hilo1:
+ tcg_out_opc_reg(s, i1, 0, a1, a2);
+ tcg_out_opc_reg(s, i2, a0, 0, 0);
break;
- case INDEX_op_and_i32:
- if (const_args[2]) {
- tcg_out_opc_imm(s, OPC_ANDI, args[0], args[1], args[2]);
- } else {
- tcg_out_opc_reg(s, OPC_AND, args[0], args[1], args[2]);
- }
- break;
- case INDEX_op_or_i32:
- if (const_args[2]) {
- tcg_out_opc_imm(s, OPC_ORI, args[0], args[1], args[2]);
- } else {
- tcg_out_opc_reg(s, OPC_OR, args[0], args[1], args[2]);
- }
- break;
- case INDEX_op_nor_i32:
- tcg_out_opc_reg(s, OPC_NOR, args[0], args[1], args[2]);
+ case INDEX_op_muls2_i32:
+ i1 = OPC_MULT;
+ goto do_hilo2;
+ case INDEX_op_mulu2_i32:
+ i1 = OPC_MULTU;
+ do_hilo2:
+ tcg_out_opc_reg(s, i1, 0, a2, args[3]);
+ tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0);
+ tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0);
break;
+
case INDEX_op_not_i32:
- tcg_out_opc_reg(s, OPC_NOR, args[0], TCG_REG_ZERO, args[1]);
- break;
- case INDEX_op_xor_i32:
- if (const_args[2]) {
- tcg_out_opc_imm(s, OPC_XORI, args[0], args[1], args[2]);
- } else {
- tcg_out_opc_reg(s, OPC_XOR, args[0], args[1], args[2]);
- }
+ i1 = OPC_NOR;
+ goto do_unary;
+ case INDEX_op_bswap16_i32:
+ i1 = OPC_WSBH;
+ goto do_unary;
+ case INDEX_op_ext8s_i32:
+ i1 = OPC_SEB;
+ goto do_unary;
+ case INDEX_op_ext16s_i32:
+ i1 = OPC_SEH;
+ do_unary:
+ tcg_out_opc_reg(s, i1, a0, TCG_REG_ZERO, a1);
break;
case INDEX_op_sar_i32:
- if (const_args[2]) {
- tcg_out_opc_sa(s, OPC_SRA, args[0], args[1], args[2]);
- } else {
- tcg_out_opc_reg(s, OPC_SRAV, args[0], args[2], args[1]);
- }
- break;
+ i1 = OPC_SRAV, i2 = OPC_SRA;
+ goto do_shift;
case INDEX_op_shl_i32:
- if (const_args[2]) {
- tcg_out_opc_sa(s, OPC_SLL, args[0], args[1], args[2]);
- } else {
- tcg_out_opc_reg(s, OPC_SLLV, args[0], args[2], args[1]);
- }
- break;
+ i1 = OPC_SLLV, i2 = OPC_SLL;
+ goto do_shift;
case INDEX_op_shr_i32:
- if (const_args[2]) {
- tcg_out_opc_sa(s, OPC_SRL, args[0], args[1], args[2]);
+ i1 = OPC_SRLV, i2 = OPC_SRL;
+ goto do_shift;
+ case INDEX_op_rotr_i32:
+ i1 = OPC_ROTRV, i2 = OPC_ROTR;
+ do_shift:
+ if (c2) {
+ tcg_out_opc_sa(s, i2, a0, a1, a2);
} else {
- tcg_out_opc_reg(s, OPC_SRLV, args[0], args[2], args[1]);
+ tcg_out_opc_reg(s, i1, a0, a2, a1);
}
break;
case INDEX_op_rotl_i32:
- if (const_args[2]) {
- tcg_out_opc_sa(s, OPC_ROTR, args[0], args[1], 0x20 - args[2]);
- } else {
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, 32);
- tcg_out_opc_reg(s, OPC_SUBU, TCG_REG_AT, TCG_REG_AT, args[2]);
- tcg_out_opc_reg(s, OPC_ROTRV, args[0], TCG_REG_AT, args[1]);
- }
- break;
- case INDEX_op_rotr_i32:
- if (const_args[2]) {
- tcg_out_opc_sa(s, OPC_ROTR, args[0], args[1], args[2]);
+ if (c2) {
+ tcg_out_opc_sa(s, OPC_ROTR, a0, a1, 32 - a2);
} else {
- tcg_out_opc_reg(s, OPC_ROTRV, args[0], args[2], args[1]);
+ tcg_out_opc_reg(s, OPC_SUBU, TCG_TMP0, TCG_REG_ZERO, a2);
+ tcg_out_opc_reg(s, OPC_ROTRV, a0, TCG_TMP0, a1);
}
break;
- case INDEX_op_bswap16_i32:
- tcg_out_opc_reg(s, OPC_WSBH, args[0], 0, args[1]);
- break;
case INDEX_op_bswap32_i32:
- tcg_out_opc_reg(s, OPC_WSBH, args[0], 0, args[1]);
- tcg_out_opc_sa(s, OPC_ROTR, args[0], args[0], 16);
- break;
-
- case INDEX_op_ext8s_i32:
- tcg_out_opc_reg(s, OPC_SEB, args[0], 0, args[1]);
- break;
- case INDEX_op_ext16s_i32:
- tcg_out_opc_reg(s, OPC_SEH, args[0], 0, args[1]);
+ tcg_out_opc_reg(s, OPC_WSBH, a0, 0, a1);
+ tcg_out_opc_sa(s, OPC_ROTR, a0, a0, 16);
break;
case INDEX_op_deposit_i32:
- tcg_out_opc_imm(s, OPC_INS, args[0], args[2],
- ((args[3] + args[4] - 1) << 11) | (args[3] << 6));
+ tcg_out_opc_bf(s, OPC_INS, a0, a2, args[3] + args[4] - 1, args[3]);
break;
case INDEX_op_brcond_i32:
- tcg_out_brcond(s, args[2], args[0], args[1], args[3]);
+ tcg_out_brcond(s, a2, a0, a1, args[3]);
break;
case INDEX_op_brcond2_i32:
- tcg_out_brcond2(s, args[4], args[0], args[1], args[2], args[3], args[5]);
+ tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], args[5]);
break;
case INDEX_op_movcond_i32:
- tcg_out_movcond(s, args[5], args[0], args[1], args[2], args[3]);
+ tcg_out_movcond(s, args[5], a0, a1, a2, args[3]);
break;
case INDEX_op_setcond_i32:
- tcg_out_setcond(s, args[3], args[0], args[1], args[2]);
+ tcg_out_setcond(s, args[3], a0, a1, a2);
break;
case INDEX_op_setcond2_i32:
- tcg_out_setcond2(s, args[5], args[0], args[1], args[2], args[3], args[4]);
+ tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]);
break;
- case INDEX_op_qemu_ld8u:
- tcg_out_qemu_ld(s, args, 0);
- break;
- case INDEX_op_qemu_ld8s:
- tcg_out_qemu_ld(s, args, 0 | 4);
- break;
- case INDEX_op_qemu_ld16u:
- tcg_out_qemu_ld(s, args, 1);
+ case INDEX_op_qemu_ld_i32:
+ tcg_out_qemu_ld(s, args, false);
break;
- case INDEX_op_qemu_ld16s:
- tcg_out_qemu_ld(s, args, 1 | 4);
+ case INDEX_op_qemu_ld_i64:
+ tcg_out_qemu_ld(s, args, true);
break;
- case INDEX_op_qemu_ld32:
- tcg_out_qemu_ld(s, args, 2);
+ case INDEX_op_qemu_st_i32:
+ tcg_out_qemu_st(s, args, false);
break;
- case INDEX_op_qemu_ld64:
- tcg_out_qemu_ld(s, args, 3);
+ case INDEX_op_qemu_st_i64:
+ tcg_out_qemu_st(s, args, true);
break;
- case INDEX_op_qemu_st8:
- tcg_out_qemu_st(s, args, 0);
- break;
- case INDEX_op_qemu_st16:
- tcg_out_qemu_st(s, args, 1);
- break;
- case INDEX_op_qemu_st32:
- tcg_out_qemu_st(s, args, 2);
+
+ case INDEX_op_add2_i32:
+ tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5],
+ const_args[4], const_args[5], false);
break;
- case INDEX_op_qemu_st64:
- tcg_out_qemu_st(s, args, 3);
+ case INDEX_op_sub2_i32:
+ tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5],
+ const_args[4], const_args[5], true);
break;
case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */
@@ -1561,9 +1598,9 @@ static const TCGTargetOpDef mips_op_defs[] = {
{ INDEX_op_divu_i32, { "r", "rZ", "rZ" } },
{ INDEX_op_rem_i32, { "r", "rZ", "rZ" } },
{ INDEX_op_remu_i32, { "r", "rZ", "rZ" } },
- { INDEX_op_sub_i32, { "r", "rZ", "rJ" } },
+ { INDEX_op_sub_i32, { "r", "rZ", "rN" } },
- { INDEX_op_and_i32, { "r", "rZ", "rI" } },
+ { INDEX_op_and_i32, { "r", "rZ", "rIK" } },
{ INDEX_op_nor_i32, { "r", "rZ", "rZ" } },
{ INDEX_op_not_i32, { "r", "rZ" } },
{ INDEX_op_or_i32, { "r", "rZ", "rIZ" } },
@@ -1588,34 +1625,20 @@ static const TCGTargetOpDef mips_op_defs[] = {
{ INDEX_op_setcond_i32, { "r", "rZ", "rZ" } },
{ INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rZ", "rZ" } },
- { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rJ", "rJ" } },
- { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rJ", "rJ" } },
+ { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rN", "rN" } },
+ { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rN", "rN" } },
{ INDEX_op_brcond2_i32, { "rZ", "rZ", "rZ", "rZ" } },
#if TARGET_LONG_BITS == 32
- { INDEX_op_qemu_ld8u, { "L", "lZ" } },
- { INDEX_op_qemu_ld8s, { "L", "lZ" } },
- { INDEX_op_qemu_ld16u, { "L", "lZ" } },
- { INDEX_op_qemu_ld16s, { "L", "lZ" } },
- { INDEX_op_qemu_ld32, { "L", "lZ" } },
- { INDEX_op_qemu_ld64, { "L", "L", "lZ" } },
-
- { INDEX_op_qemu_st8, { "SZ", "SZ" } },
- { INDEX_op_qemu_st16, { "SZ", "SZ" } },
- { INDEX_op_qemu_st32, { "SZ", "SZ" } },
- { INDEX_op_qemu_st64, { "SZ", "SZ", "SZ" } },
+ { INDEX_op_qemu_ld_i32, { "L", "lZ" } },
+ { INDEX_op_qemu_st_i32, { "SZ", "SZ" } },
+ { INDEX_op_qemu_ld_i64, { "L", "L", "lZ" } },
+ { INDEX_op_qemu_st_i64, { "SZ", "SZ", "SZ" } },
#else
- { INDEX_op_qemu_ld8u, { "L", "lZ", "lZ" } },
- { INDEX_op_qemu_ld8s, { "L", "lZ", "lZ" } },
- { INDEX_op_qemu_ld16u, { "L", "lZ", "lZ" } },
- { INDEX_op_qemu_ld16s, { "L", "lZ", "lZ" } },
- { INDEX_op_qemu_ld32, { "L", "lZ", "lZ" } },
- { INDEX_op_qemu_ld64, { "L", "L", "lZ", "lZ" } },
-
- { INDEX_op_qemu_st8, { "SZ", "SZ", "SZ" } },
- { INDEX_op_qemu_st16, { "SZ", "SZ", "SZ" } },
- { INDEX_op_qemu_st32, { "SZ", "SZ", "SZ" } },
- { INDEX_op_qemu_st64, { "SZ", "SZ", "SZ", "SZ" } },
+ { INDEX_op_qemu_ld_i32, { "L", "lZ", "lZ" } },
+ { INDEX_op_qemu_st_i32, { "SZ", "SZ", "SZ" } },
+ { INDEX_op_qemu_ld_i64, { "L", "L", "lZ", "lZ" } },
+ { INDEX_op_qemu_st_i64, { "SZ", "SZ", "SZ", "SZ" } },
#endif
{ -1 },
};
@@ -1629,7 +1652,7 @@ static int tcg_target_callee_save_regs[] = {
TCG_REG_S5,
TCG_REG_S6,
TCG_REG_S7,
- TCG_REG_FP,
+ TCG_REG_S8,
TCG_REG_RA, /* should be last for ABI compliance */
};
@@ -1761,6 +1784,7 @@ static void tcg_target_init(TCGContext *s)
(1 << TCG_REG_A1) |
(1 << TCG_REG_A2) |
(1 << TCG_REG_A3) |
+ (1 << TCG_REG_T0) |
(1 << TCG_REG_T1) |
(1 << TCG_REG_T2) |
(1 << TCG_REG_T3) |
@@ -1775,11 +1799,18 @@ static void tcg_target_init(TCGContext *s)
tcg_regset_set_reg(s->reserved_regs, TCG_REG_ZERO); /* zero register */
tcg_regset_set_reg(s->reserved_regs, TCG_REG_K0); /* kernel use only */
tcg_regset_set_reg(s->reserved_regs, TCG_REG_K1); /* kernel use only */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_AT); /* internal use */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_T0); /* internal use */
+ tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); /* internal use */
+ tcg_regset_set_reg(s->reserved_regs, TCG_TMP1); /* internal use */
tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return address */
tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */
tcg_regset_set_reg(s->reserved_regs, TCG_REG_GP); /* global pointer */
tcg_add_target_add_op_defs(mips_op_defs);
}
+
+void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr)
+{
+ uint32_t *ptr = (uint32_t *)jmp_addr;
+ *ptr = deposit32(*ptr, 0, 26, addr >> 2);
+ flush_icache_range(jmp_addr, jmp_addr + 4);
+}
diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h
index c6d2267d77..b5face8b4d 100644
--- a/tcg/mips/tcg-target.h
+++ b/tcg/mips/tcg-target.h
@@ -60,16 +60,14 @@ typedef enum {
TCG_REG_K1,
TCG_REG_GP,
TCG_REG_SP,
- TCG_REG_FP,
+ TCG_REG_S8,
TCG_REG_RA,
-} TCGReg;
-#define TCG_CT_CONST_ZERO 0x100
-#define TCG_CT_CONST_U16 0x200
-#define TCG_CT_CONST_S16 0x400
+ TCG_REG_CALL_STACK = TCG_REG_SP,
+ TCG_AREG0 = TCG_REG_S0,
+} TCGReg;
/* used for function call generation */
-#define TCG_REG_CALL_STACK TCG_REG_SP
#define TCG_TARGET_STACK_ALIGN 8
#define TCG_TARGET_CALL_STACK_OFFSET 16
#define TCG_TARGET_CALL_ALIGN_ARGS 1
@@ -120,15 +118,13 @@ extern bool use_mips32r2_instructions;
#define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions
#define TCG_TARGET_HAS_rot_i32 use_mips32r2_instructions
-#define TCG_TARGET_HAS_new_ldst 0
+#define TCG_TARGET_HAS_new_ldst 1
/* optional instructions automatically implemented */
#define TCG_TARGET_HAS_neg_i32 0 /* sub rd, zero, rt */
#define TCG_TARGET_HAS_ext8u_i32 0 /* andi rt, rs, 0xff */
#define TCG_TARGET_HAS_ext16u_i32 0 /* andi rt, rs, 0xffff */
-#define TCG_AREG0 TCG_REG_S0
-
#ifdef __OpenBSD__
#include <machine/sysarch.h>
#else
diff --git a/tcg/tci/tcg-target.c b/tcg/tci/tcg-target.c
index 9b39231c15..375e590d2b 100644
--- a/tcg/tci/tcg-target.c
+++ b/tcg/tci/tcg-target.c
@@ -544,7 +544,10 @@ static void tcg_out_movi(TCGContext *s, TCGType type,
static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *arg)
{
+ uint8_t *old_code_ptr = s->code_ptr;
+ tcg_out_op_t(s, INDEX_op_call);
tcg_out_ri(s, 1, (uintptr_t)arg);
+ old_code_ptr[1] = s->code_ptr - old_code_ptr;
}
static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
diff --git a/tests/tcg/lm32/Makefile b/tests/tcg/lm32/Makefile
index 8e5d405459..57e7363b2c 100644
--- a/tests/tcg/lm32/Makefile
+++ b/tests/tcg/lm32/Makefile
@@ -3,7 +3,7 @@
CROSS=lm32-elf-
SIM = qemu-system-lm32
-SIMFLAGS = -M lm32-evr -nographic -device lm32-sys -net none -kernel
+SIMFLAGS = -M lm32-evr -nographic -semihosting -net none -kernel
CC = $(CROSS)gcc
AS = $(CROSS)as
@@ -18,6 +18,7 @@ LDFLAGS = -T$(TSRC_PATH)/linker.ld
ASFLAGS += -Wa,-I,$(TSRC_PATH)/
CRT = crt.o
+HELPER = helper.o
TESTCASES += test_add.tst
TESTCASES += test_addi.tst
TESTCASES += test_and.tst
@@ -91,15 +92,15 @@ all: build
%.o: $(TSRC_PATH)/%.S
$(AS) $(ASFLAGS) -c $< -o $@
-%.tst: %.o $(TSRC_PATH)/macros.inc $(CRT)
- $(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@
+%.tst: %.o $(TSRC_PATH)/macros.inc $(CRT) $(HELPER)
+ $(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $(HELPER) $< -o $@
-build: $(CRT) $(TESTCASES)
+build: $(TESTCASES)
check: $(TESTCASES:test_%.tst=check_%)
-check_%: test_%.tst $(CRT) $(SYS)
- $(SIM) $(SIMFLAGS) $<
+check_%: test_%.tst
+ @$(SIM) $(SIMFLAGS) $<
clean:
- $(RM) -fr $(TESTCASES) $(CRT)
+ $(RM) -fr $(TESTCASES) $(CRT) $(HELPER)
diff --git a/tests/tcg/lm32/crt.S b/tests/tcg/lm32/crt.S
index 5f9cfd95d3..fc437a3de1 100644
--- a/tests/tcg/lm32/crt.S
+++ b/tests/tcg/lm32/crt.S
@@ -8,9 +8,9 @@ _reset_handler:
ori r1, r1, lo(_start)
wcsr eba, r1
wcsr deba, r1
+ mvhi sp, hi(_fstack)
+ ori sp, sp, lo(_fstack)
bi _main
- nop
- nop
_breakpoint_handler:
ori r25, r25, 1
diff --git a/tests/tcg/lm32/helper.S b/tests/tcg/lm32/helper.S
new file mode 100644
index 0000000000..3351d41e84
--- /dev/null
+++ b/tests/tcg/lm32/helper.S
@@ -0,0 +1,65 @@
+.text
+.global _start, _write, _exit
+.global _tc_fail, _tc_pass
+
+_write:
+ addi sp, sp, -4
+ sw (sp+4), r8
+ mvi r8, 5
+ scall
+ lw r8, (sp+4)
+ addi sp, sp, 4
+ ret
+
+_exit:
+ mvi r8, 1
+ scall
+1:
+ bi 1b
+
+_tc_pass:
+.data
+1:
+ .ascii "OK\n"
+2:
+.text
+ addi sp, sp, -16
+ sw (sp+4), ra
+ sw (sp+8), r1
+ sw (sp+12), r2
+ sw (sp+16), r3
+ mvi r1, 1
+ mvhi r2, hi(1b)
+ ori r2, r2, lo(1b)
+ mvi r3, (2b - 1b)
+ calli _write
+ lw r3, (sp+16)
+ lw r2, (sp+12)
+ lw r1, (sp+8)
+ lw ra, (sp+4)
+ addi sp, sp, 16
+ ret
+
+_tc_fail:
+.data
+1:
+ .ascii "FAILED\n"
+2:
+.text
+ addi sp, sp, -16
+ sw (sp+4), ra
+ sw (sp+8), r1
+ sw (sp+12), r2
+ sw (sp+16), r3
+ sw (sp+4), ra
+ mvi r1, 1
+ mvhi r2, hi(1b)
+ ori r2, r2, lo(1b)
+ mvi r3, (2b - 1b)
+ calli _write
+ lw r3, (sp+16)
+ lw r2, (sp+12)
+ lw r1, (sp+8)
+ lw ra, (sp+4)
+ addi sp, sp, 16
+ ret
diff --git a/tests/tcg/lm32/macros.inc b/tests/tcg/lm32/macros.inc
index 367c7c50d8..360ad53c9f 100644
--- a/tests/tcg/lm32/macros.inc
+++ b/tests/tcg/lm32/macros.inc
@@ -1,12 +1,26 @@
+.equ MAX_TESTNAME_LEN, 32
.macro test_name name
.data
tn_\name:
- .asciz "\name"
+ .ascii "\name"
+ .space MAX_TESTNAME_LEN - (. - tn_\name), ' '
.text
- mvhi r13, hi(tn_\name)
- ori r13, r13, lo(tn_\name)
- sw (r12+8), r13
+ .global \name
+\name:
+ addi sp, sp, -12
+ sw (sp+4), r1
+ sw (sp+8), r2
+ sw (sp+12), r3
+ mvi r1, 1
+ mvhi r2, hi(tn_\name)
+ ori r2, r2, lo(tn_\name)
+ mvi r3, MAX_TESTNAME_LEN
+ calli _write
+ lw r3, (sp+12)
+ lw r2, (sp+8)
+ lw r1, (sp+4)
+ addi sp, sp, 12
.endm
.macro load reg val
@@ -15,13 +29,12 @@ tn_\name:
.endm
.macro tc_pass
- mvi r13, 0
- sw (r12+4), r13
+ calli _tc_pass
.endm
.macro tc_fail
- mvi r13, 1
- sw (r12+4), r13
+ addi r12, r12, 1
+ calli _tc_fail
.endm
.macro check_r3 val
@@ -63,14 +76,12 @@ tn_\name:
.global _main
.text
_main:
- mvhi r12, hi(0xffff0000) # base address of test block
- ori r12, r12, lo(0xffff0000)
+ mvi r12, 0
.endm
.macro end
- sw (r12+0), r0
-1:
- bi 1b
+ mv r1, r12
+ calli _exit
.endm
# base +
diff --git a/tests/tcg/lm32/test_lb.S b/tests/tcg/lm32/test_lb.S
index f84d21ead9..d677eea4c4 100644
--- a/tests/tcg/lm32/test_lb.S
+++ b/tests/tcg/lm32/test_lb.S
@@ -8,10 +8,12 @@ lb r3, (r1+0)
check_r3 0x7e
test_name LB_2
+load r1 data
lb r3, (r1+1)
check_r3 0x7f
test_name LB_3
+load r1 data
lb r3, (r1+-1)
check_r3 0x7d
@@ -21,10 +23,12 @@ lb r3, (r1+0)
check_r3 0xfffffffe
test_name LB_5
+load r1 data_msb
lb r3, (r1+1)
check_r3 0xffffffff
test_name LB_6
+load r1 data_msb
lb r3, (r1+-1)
check_r3 0xfffffffd
diff --git a/tests/tcg/lm32/test_lbu.S b/tests/tcg/lm32/test_lbu.S
index 4c1786ad71..dc5d5f67d3 100644
--- a/tests/tcg/lm32/test_lbu.S
+++ b/tests/tcg/lm32/test_lbu.S
@@ -8,10 +8,12 @@ lbu r3, (r1+0)
check_r3 0x7e
test_name LBU_2
+load r1 data
lbu r3, (r1+1)
check_r3 0x7f
test_name LBU_3
+load r1 data
lbu r3, (r1+-1)
check_r3 0x7d
@@ -21,10 +23,12 @@ lbu r3, (r1+0)
check_r3 0xfe
test_name LBU_5
+load r1 data_msb
lbu r3, (r1+1)
check_r3 0xff
test_name LBU_6
+load r1 data_msb
lbu r3, (r1+-1)
check_r3 0xfd
diff --git a/tests/tcg/lm32/test_lh.S b/tests/tcg/lm32/test_lh.S
index e57d9e35cf..397996bddd 100644
--- a/tests/tcg/lm32/test_lh.S
+++ b/tests/tcg/lm32/test_lh.S
@@ -8,10 +8,12 @@ lh r3, (r1+0)
check_r3 0x7e7f
test_name LH_2
+load r1 data
lh r3, (r1+2)
check_r3 0x7071
test_name LH_3
+load r1 data
lh r3, (r1+-2)
check_r3 0x7c7d
@@ -21,10 +23,12 @@ lh r3, (r1+0)
check_r3 0xfffffeff
test_name LH_5
+load r1 data_msb
lh r3, (r1+2)
check_r3 0xfffff0f1
test_name LH_6
+load r1 data_msb
lh r3, (r1+-2)
check_r3 0xfffffcfd
diff --git a/tests/tcg/lm32/test_lhu.S b/tests/tcg/lm32/test_lhu.S
index e648775d94..8de7c52560 100644
--- a/tests/tcg/lm32/test_lhu.S
+++ b/tests/tcg/lm32/test_lhu.S
@@ -8,10 +8,12 @@ lhu r3, (r1+0)
check_r3 0x7e7f
test_name LHU_2
+load r1 data
lhu r3, (r1+2)
check_r3 0x7071
test_name LHU_3
+load r1 data
lhu r3, (r1+-2)
check_r3 0x7c7d
@@ -21,10 +23,12 @@ lhu r3, (r1+0)
check_r3 0xfeff
test_name LHU_5
+load r1 data_msb
lhu r3, (r1+2)
check_r3 0xf0f1
test_name LHU_6
+load r1 data_msb
lhu r3, (r1+-2)
check_r3 0xfcfd
diff --git a/tests/tcg/lm32/test_lw.S b/tests/tcg/lm32/test_lw.S
index f8c919d2b8..996e5f8c88 100644
--- a/tests/tcg/lm32/test_lw.S
+++ b/tests/tcg/lm32/test_lw.S
@@ -8,10 +8,12 @@ lw r3, (r1+0)
check_r3 0x7e7f7071
test_name LW_2
+load r1 data
lw r3, (r1+4)
check_r3 0x72737475
test_name LW_3
+load r1 data
lw r3, (r1+-4)
check_r3 0x7a7b7c7d
diff --git a/tests/tcg/lm32/test_sb.S b/tests/tcg/lm32/test_sb.S
index 89e39d621d..b15a89d342 100644
--- a/tests/tcg/lm32/test_sb.S
+++ b/tests/tcg/lm32/test_sb.S
@@ -9,11 +9,13 @@ sb (r1+0), r2
check_mem data 0xaa000000
test_name SB_2
+load r1 data
load r2 0xf0f1f2bb
sb (r1+1), r2
check_mem data 0xaabb0000
test_name SB_3
+load r1 data
load r2 0xf0f1f2cc
sb (r1+-1), r2
check_mem data0 0x000000cc
diff --git a/tests/tcg/lm32/test_scall.S b/tests/tcg/lm32/test_scall.S
index b442e32374..46032f841d 100644
--- a/tests/tcg/lm32/test_scall.S
+++ b/tests/tcg/lm32/test_scall.S
@@ -5,6 +5,10 @@ start
test_name SCALL_1
mvi r1, 1
wcsr IE, r1
+# we are running in a semi hosted environment
+# therefore we have to set r8 to some unused system
+# call
+mvi r8, 0
insn:
scall
check_excp 64
diff --git a/tests/tcg/lm32/test_sh.S b/tests/tcg/lm32/test_sh.S
index ea8b3f2067..bba10224f6 100644
--- a/tests/tcg/lm32/test_sh.S
+++ b/tests/tcg/lm32/test_sh.S
@@ -9,11 +9,13 @@ sh (r1+0), r2
check_mem data 0xaaaa0000
test_name SH_2
+load r1 data
load r2 0xf0f1bbbb
sh (r1+2), r2
check_mem data 0xaaaabbbb
test_name SH_3
+load r1 data
load r2 0xf0f1cccc
sh (r1+-2), r2
check_mem data0 0x0000cccc
diff --git a/tests/tcg/lm32/test_sw.S b/tests/tcg/lm32/test_sw.S
index d1fdadce61..2b1c017e7b 100644
--- a/tests/tcg/lm32/test_sw.S
+++ b/tests/tcg/lm32/test_sw.S
@@ -9,16 +9,19 @@ sw (r1+0), r2
check_mem data 0xaabbccdd
test_name SW_2
+load r1 data
load r2 0x00112233
sw (r1+4), r2
check_mem data1 0x00112233
test_name SW_3
+load r1 data
load r2 0x44556677
sw (r1+-4), r2
check_mem data0 0x44556677
test_name SW_4
+load r1 data
sw (r1+0), r1
lw r3, (r1+0)
check_r3 data
diff --git a/trace-events b/trace-events
index b6d289d720..f256ca51b0 100644
--- a/trace-events
+++ b/trace-events
@@ -627,9 +627,6 @@ lm32_uart_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
lm32_uart_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
lm32_uart_irq_state(int level) "irq state %d"
-# hw/misc/lm32_sys.c
-lm32_sys_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
-
# hw/scsi/megasas.c
megasas_init_firmware(uint64_t pa) "pa %" PRIx64 " "
megasas_init_queue(uint64_t queue_pa, int queue_len, uint64_t head, uint64_t tail, uint32_t flags) "queue at %" PRIx64 " len %d head %" PRIx64 " tail %" PRIx64 " flags %x"
@@ -1045,12 +1042,13 @@ displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]"
ppm_save(const char *filename, void *display_surface) "%s surface=%p"
# ui/gtk.c
-gd_switch(int width, int height) "width=%d, height=%d"
-gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d"
-gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)"
+gd_switch(const char *tab, int width, int height) "tab=%s, width=%d, height=%d"
+gd_update(const char *tab, int x, int y, int w, int h) "tab=%s, x=%d, y=%d, w=%d, h=%d"
+gd_key_event(const char *tab, int gdk_keycode, int qemu_keycode, const char *action) "tab=%s, translated GDK keycode %d to QEMU keycode %d (%s)"
+gd_grab(const char *tab, const char *device, bool on) "tab=%s, %s %d"
# ui/input.c
-input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%x, down %d"
+input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d"
input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d"
input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d"
input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d"
diff --git a/translate-all.c b/translate-all.c
index 5549a85ed5..6b7b46e761 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -475,6 +475,10 @@ static inline PageDesc *page_find(tb_page_addr_t index)
#elif defined(__s390x__)
/* We have a +- 4GB range on the branches; leave some slop. */
# define MAX_CODE_GEN_BUFFER_SIZE (3ul * 1024 * 1024 * 1024)
+#elif defined(__mips__)
+ /* We have a 256MB branch region, but leave room to make sure the
+ main executable is also within that region. */
+# define MAX_CODE_GEN_BUFFER_SIZE (128ul * 1024 * 1024)
#else
# define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
#endif
@@ -509,14 +513,47 @@ static inline size_t size_code_gen_buffer(size_t tb_size)
return tb_size;
}
+#ifdef __mips__
+/* In order to use J and JAL within the code_gen_buffer, we require
+ that the buffer not cross a 256MB boundary. */
+static inline bool cross_256mb(void *addr, size_t size)
+{
+ return ((uintptr_t)addr ^ ((uintptr_t)addr + size)) & 0xf0000000;
+}
+
+/* We weren't able to allocate a buffer without crossing that boundary,
+ so make do with the larger portion of the buffer that doesn't cross.
+ Returns the new base of the buffer, and adjusts code_gen_buffer_size. */
+static inline void *split_cross_256mb(void *buf1, size_t size1)
+{
+ void *buf2 = (void *)(((uintptr_t)buf1 + size1) & 0xf0000000);
+ size_t size2 = buf1 + size1 - buf2;
+
+ size1 = buf2 - buf1;
+ if (size1 < size2) {
+ size1 = size2;
+ buf1 = buf2;
+ }
+
+ tcg_ctx.code_gen_buffer_size = size1;
+ return buf1;
+}
+#endif
+
#ifdef USE_STATIC_CODE_GEN_BUFFER
static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]
__attribute__((aligned(CODE_GEN_ALIGN)));
static inline void *alloc_code_gen_buffer(void)
{
- map_exec(static_code_gen_buffer, tcg_ctx.code_gen_buffer_size);
- return static_code_gen_buffer;
+ void *buf = static_code_gen_buffer;
+#ifdef __mips__
+ if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) {
+ buf = split_cross_256mb(buf, tcg_ctx.code_gen_buffer_size);
+ }
+#endif
+ map_exec(buf, tcg_ctx.code_gen_buffer_size);
+ return buf;
}
#elif defined(USE_MMAP)
static inline void *alloc_code_gen_buffer(void)
@@ -545,20 +582,76 @@ static inline void *alloc_code_gen_buffer(void)
start = 0x40000000ul;
# elif defined(__s390x__)
start = 0x90000000ul;
+# elif defined(__mips__)
+ /* ??? We ought to more explicitly manage layout for softmmu too. */
+# ifdef CONFIG_USER_ONLY
+ start = 0x68000000ul;
+# elif _MIPS_SIM == _ABI64
+ start = 0x128000000ul;
+# else
+ start = 0x08000000ul;
+# endif
# endif
buf = mmap((void *)start, tcg_ctx.code_gen_buffer_size,
PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0);
- return buf == MAP_FAILED ? NULL : buf;
+ if (buf == MAP_FAILED) {
+ return NULL;
+ }
+
+#ifdef __mips__
+ if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) {
+ /* Try again, with the original still mapped, to avoid re-aquiring
+ that 256mb crossing. This time don't specify an address. */
+ size_t size2, size1 = tcg_ctx.code_gen_buffer_size;
+ void *buf2 = mmap(NULL, size1, PROT_WRITE | PROT_READ | PROT_EXEC,
+ flags, -1, 0);
+ if (buf2 != MAP_FAILED) {
+ if (!cross_256mb(buf2, size1)) {
+ /* Success! Use the new buffer. */
+ munmap(buf, size1);
+ return buf2;
+ }
+ /* Failure. Work with what we had. */
+ munmap(buf2, size1);
+ }
+
+ /* Split the original buffer. Free the smaller half. */
+ buf2 = split_cross_256mb(buf, size1);
+ size2 = tcg_ctx.code_gen_buffer_size;
+ munmap(buf + (buf == buf2 ? size2 : 0), size1 - size2);
+ return buf2;
+ }
+#endif
+
+ return buf;
}
#else
static inline void *alloc_code_gen_buffer(void)
{
void *buf = g_malloc(tcg_ctx.code_gen_buffer_size);
- if (buf) {
- map_exec(buf, tcg_ctx.code_gen_buffer_size);
+ if (buf == NULL) {
+ return NULL;
}
+
+#ifdef __mips__
+ if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) {
+ void *buf2 = g_malloc(tcg_ctx.code_gen_buffer_size);
+ if (buf2 != NULL && !cross_256mb(buf2, size1)) {
+ /* Success! Use the new buffer. */
+ free(buf);
+ buf = buf2;
+ } else {
+ /* Failure. Work with what we had. Since this is malloc
+ and not mmap, we can't free the other half. */
+ free(buf2);
+ buf = split_cross_256mb(buf, tcg_ctx.code_gen_buffer_size);
+ }
+ }
+#endif
+
+ map_exec(buf, tcg_ctx.code_gen_buffer_size);
return buf;
}
#endif /* USE_STATIC_CODE_GEN_BUFFER, USE_MMAP */
diff --git a/ui/console.c b/ui/console.c
index 34d1eaa955..75ec3afcf7 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -143,8 +143,6 @@ struct QemuConsole {
TextCell *cells;
int text_x[2], text_y[2], cursor_invalidate;
int echo;
- bool cursor_visible_phase;
- QEMUTimer *cursor_timer;
int update_x0;
int update_y0;
@@ -177,10 +175,14 @@ static DisplayState *display_state;
static QemuConsole *active_console;
static QemuConsole *consoles[MAX_CONSOLES];
static int nb_consoles = 0;
+static bool cursor_visible_phase;
+static QEMUTimer *cursor_timer;
static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
static void dpy_refresh(DisplayState *s);
static DisplayState *get_alloc_displaystate(void);
+static void text_console_update_cursor_timer(void);
+static void text_console_update_cursor(void *opaque);
static void gui_update(void *opaque)
{
@@ -475,6 +477,9 @@ static inline void text_update_xy(QemuConsole *s, int x, int y)
static void invalidate_xy(QemuConsole *s, int x, int y)
{
+ if (!qemu_console_is_visible(s)) {
+ return;
+ }
if (s->update_x0 > x * FONT_WIDTH)
s->update_x0 = x * FONT_WIDTH;
if (s->update_y0 > y * FONT_HEIGHT)
@@ -490,25 +495,20 @@ static void update_xy(QemuConsole *s, int x, int y)
TextCell *c;
int y1, y2;
- if (!qemu_console_is_visible(s)) {
- return;
- }
-
if (s->ds->have_text) {
text_update_xy(s, x, y);
}
- if (s->ds->have_gfx) {
- y1 = (s->y_base + y) % s->total_height;
- y2 = y1 - s->y_displayed;
- if (y2 < 0)
- y2 += s->total_height;
- if (y2 < s->height) {
- c = &s->cells[y1 * s->width + x];
- vga_putcharxy(s, x, y2, c->ch,
- &(c->t_attrib));
- invalidate_xy(s, x, y2);
- }
+ y1 = (s->y_base + y) % s->total_height;
+ y2 = y1 - s->y_displayed;
+ if (y2 < 0) {
+ y2 += s->total_height;
+ }
+ if (y2 < s->height) {
+ c = &s->cells[y1 * s->width + x];
+ vga_putcharxy(s, x, y2, c->ch,
+ &(c->t_attrib));
+ invalidate_xy(s, x, y2);
}
}
@@ -518,33 +518,28 @@ static void console_show_cursor(QemuConsole *s, int show)
int y, y1;
int x = s->x;
- if (!qemu_console_is_visible(s)) {
- return;
- }
-
if (s->ds->have_text) {
s->cursor_invalidate = 1;
}
- if (s->ds->have_gfx) {
- if (x >= s->width) {
- x = s->width - 1;
- }
- y1 = (s->y_base + s->y) % s->total_height;
- y = y1 - s->y_displayed;
- if (y < 0)
- y += s->total_height;
- if (y < s->height) {
- c = &s->cells[y1 * s->width + x];
- if (show && s->cursor_visible_phase) {
- TextAttributes t_attrib = s->t_attrib_default;
- t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
- vga_putcharxy(s, x, y, c->ch, &t_attrib);
- } else {
- vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
- }
- invalidate_xy(s, x, y);
+ if (x >= s->width) {
+ x = s->width - 1;
+ }
+ y1 = (s->y_base + s->y) % s->total_height;
+ y = y1 - s->y_displayed;
+ if (y < 0) {
+ y += s->total_height;
+ }
+ if (y < s->height) {
+ c = &s->cells[y1 * s->width + x];
+ if (show && cursor_visible_phase) {
+ TextAttributes t_attrib = s->t_attrib_default;
+ t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
+ vga_putcharxy(s, x, y, c->ch, &t_attrib);
+ } else {
+ vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
}
+ invalidate_xy(s, x, y);
}
}
@@ -554,10 +549,6 @@ static void console_refresh(QemuConsole *s)
TextCell *c;
int x, y, y1;
- if (!qemu_console_is_visible(s)) {
- return;
- }
-
if (s->ds->have_text) {
s->text_x[0] = 0;
s->text_y[0] = 0;
@@ -566,25 +557,23 @@ static void console_refresh(QemuConsole *s)
s->cursor_invalidate = 1;
}
- if (s->ds->have_gfx) {
- vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
- color_table_rgb[0][COLOR_BLACK]);
- y1 = s->y_displayed;
- for (y = 0; y < s->height; y++) {
- c = s->cells + y1 * s->width;
- for (x = 0; x < s->width; x++) {
- vga_putcharxy(s, x, y, c->ch,
- &(c->t_attrib));
- c++;
- }
- if (++y1 == s->total_height) {
- y1 = 0;
- }
+ vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
+ color_table_rgb[0][COLOR_BLACK]);
+ y1 = s->y_displayed;
+ for (y = 0; y < s->height; y++) {
+ c = s->cells + y1 * s->width;
+ for (x = 0; x < s->width; x++) {
+ vga_putcharxy(s, x, y, c->ch,
+ &(c->t_attrib));
+ c++;
+ }
+ if (++y1 == s->total_height) {
+ y1 = 0;
}
- console_show_cursor(s, 1);
- dpy_gfx_update(s, 0, 0,
- surface_width(surface), surface_height(surface));
}
+ console_show_cursor(s, 1);
+ dpy_gfx_update(s, 0, 0,
+ surface_width(surface), surface_height(surface));
}
static void console_scroll(QemuConsole *s, int ydelta)
@@ -640,7 +629,7 @@ static void console_put_lf(QemuConsole *s)
c->t_attrib = s->t_attrib_default;
c++;
}
- if (qemu_console_is_visible(s) && s->y_displayed == s->y_base) {
+ if (s->y_displayed == s->y_base) {
if (s->ds->have_text) {
s->text_x[0] = 0;
s->text_y[0] = 0;
@@ -648,18 +637,16 @@ static void console_put_lf(QemuConsole *s)
s->text_y[1] = s->height - 1;
}
- if (s->ds->have_gfx) {
- vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
- s->width * FONT_WIDTH,
- (s->height - 1) * FONT_HEIGHT);
- vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
- s->width * FONT_WIDTH, FONT_HEIGHT,
- color_table_rgb[0][s->t_attrib_default.bgcol]);
- s->update_x0 = 0;
- s->update_y0 = 0;
- s->update_x1 = s->width * FONT_WIDTH;
- s->update_y1 = s->height * FONT_HEIGHT;
- }
+ vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
+ s->width * FONT_WIDTH,
+ (s->height - 1) * FONT_HEIGHT);
+ vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
+ s->width * FONT_WIDTH, FONT_HEIGHT,
+ color_table_rgb[0][s->t_attrib_default.bgcol]);
+ s->update_x0 = 0;
+ s->update_y0 = 0;
+ s->update_x1 = s->width * FONT_WIDTH;
+ s->update_y1 = s->height * FONT_HEIGHT;
}
}
}
@@ -1004,9 +991,6 @@ void console_select(unsigned int index)
if (s) {
DisplayState *ds = s->ds;
- if (active_console && active_console->cursor_timer) {
- timer_del(active_console->cursor_timer);
- }
active_console = s;
if (ds->have_gfx) {
QLIST_FOREACH(dcl, &ds->listeners, next) {
@@ -1023,10 +1007,7 @@ void console_select(unsigned int index)
if (ds->have_text) {
dpy_text_resize(s, s->width, s->height);
}
- if (s->cursor_timer) {
- timer_mod(s->cursor_timer,
- qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
- }
+ text_console_update_cursor(NULL);
}
}
@@ -1075,13 +1056,11 @@ static void kbd_send_chars(void *opaque)
}
/* called when an ascii key is pressed */
-void kbd_put_keysym(int keysym)
+void kbd_put_keysym_console(QemuConsole *s, int keysym)
{
- QemuConsole *s;
uint8_t buf[16], *q;
int c;
- s = active_console;
if (!s || (s->console_type == GRAPHIC_CONSOLE))
return;
@@ -1130,6 +1109,11 @@ void kbd_put_keysym(int keysym)
}
}
+void kbd_put_keysym(int keysym)
+{
+ kbd_put_keysym_console(active_console, keysym);
+}
+
static void text_console_invalidate(void *opaque)
{
QemuConsole *s = (QemuConsole *) opaque;
@@ -1167,9 +1151,9 @@ static void text_console_update(void *opaque, console_ch_t *chardata)
}
}
-static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
+static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
+ uint32_t head)
{
- Error *local_err = NULL;
Object *obj;
QemuConsole *s;
int i;
@@ -1179,13 +1163,14 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
obj = object_new(TYPE_QEMU_CONSOLE);
s = QEMU_CONSOLE(obj);
+ s->head = head;
object_property_add_link(obj, "device", TYPE_DEVICE,
(Object **)&s->device,
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE,
- &local_err);
+ &error_abort);
object_property_add_uint32_ptr(obj, "head",
- &s->head, &local_err);
+ &s->head, &error_abort);
if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
(console_type == GRAPHIC_CONSOLE))) {
@@ -1270,19 +1255,18 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
return surface;
}
-static DisplaySurface *qemu_create_dummy_surface(void)
+static DisplaySurface *qemu_create_message_surface(int w, int h,
+ const char *msg)
{
- static const char msg[] =
- "This VM has no graphic display device.";
- DisplaySurface *surface = qemu_create_displaysurface(640, 480);
+ DisplaySurface *surface = qemu_create_displaysurface(w, h);
pixman_color_t bg = color_table_rgb[0][COLOR_BLACK];
pixman_color_t fg = color_table_rgb[0][COLOR_WHITE];
pixman_image_t *glyph;
int len, x, y, i;
len = strlen(msg);
- x = (640/FONT_WIDTH - len) / 2;
- y = (480/FONT_HEIGHT - 1) / 2;
+ x = (w / FONT_WIDTH - len) / 2;
+ y = (h / FONT_HEIGHT - 1) / 2;
for (i = 0; i < len; i++) {
glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
@@ -1304,6 +1288,8 @@ void qemu_free_displaysurface(DisplaySurface *surface)
void register_displaychangelistener(DisplayChangeListener *dcl)
{
+ static const char nodev[] =
+ "This VM has no graphic display device.";
static DisplaySurface *dummy;
QemuConsole *con;
@@ -1322,11 +1308,12 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
dcl->ops->dpy_gfx_switch(dcl, con->surface);
} else {
if (!dummy) {
- dummy = qemu_create_dummy_surface();
+ dummy = qemu_create_message_surface(640, 480, nodev);
}
dcl->ops->dpy_gfx_switch(dcl, dummy);
}
}
+ text_console_update_cursor(NULL);
}
void update_displaychangelistener(DisplayChangeListener *dcl,
@@ -1550,6 +1537,8 @@ static DisplayState *get_alloc_displaystate(void)
{
if (!display_state) {
display_state = g_new0(DisplayState, 1);
+ cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+ text_console_update_cursor, NULL);
}
return display_state;
}
@@ -1560,7 +1549,6 @@ static DisplayState *get_alloc_displaystate(void)
*/
DisplayState *init_displaystate(void)
{
- Error *local_err = NULL;
gchar *name;
int i;
@@ -1579,7 +1567,7 @@ DisplayState *init_displaystate(void)
* doesn't change any more */
name = g_strdup_printf("console[%d]", i);
object_property_add_child(container_get(object_get_root(), "/backend"),
- name, OBJECT(consoles[i]), &local_err);
+ name, OBJECT(consoles[i]), &error_abort);
g_free(name);
}
@@ -1590,7 +1578,8 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
const GraphicHwOps *hw_ops,
void *opaque)
{
- Error *local_err = NULL;
+ static const char noinit[] =
+ "Guest has not initialized the display (yet).";
int width = 640;
int height = 480;
QemuConsole *s;
@@ -1598,17 +1587,15 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
ds = get_alloc_displaystate();
trace_console_gfx_new();
- s = new_console(ds, GRAPHIC_CONSOLE);
+ s = new_console(ds, GRAPHIC_CONSOLE, head);
s->hw_ops = hw_ops;
s->hw = opaque;
if (dev) {
- object_property_set_link(OBJECT(s), OBJECT(dev),
- "device", &local_err);
- object_property_set_int(OBJECT(s), head,
- "head", &local_err);
+ object_property_set_link(OBJECT(s), OBJECT(dev), "device",
+ &error_abort);
}
- s->surface = qemu_create_displaysurface(width, height);
+ s->surface = qemu_create_message_surface(width, height, noinit);
return s;
}
@@ -1622,7 +1609,6 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index)
QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
{
- Error *local_err = NULL;
Object *obj;
uint32_t h;
int i;
@@ -1632,12 +1618,12 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
continue;
}
obj = object_property_get_link(OBJECT(consoles[i]),
- "device", &local_err);
+ "device", &error_abort);
if (DEVICE(obj) != dev) {
continue;
}
h = object_property_get_int(OBJECT(consoles[i]),
- "head", &local_err);
+ "head", &error_abort);
if (h != head) {
continue;
}
@@ -1712,14 +1698,32 @@ static void text_console_set_echo(CharDriverState *chr, bool echo)
s->echo = echo;
}
+static void text_console_update_cursor_timer(void)
+{
+ timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
+ + CONSOLE_CURSOR_PERIOD / 2);
+}
+
static void text_console_update_cursor(void *opaque)
{
- QemuConsole *s = opaque;
+ QemuConsole *s;
+ int i, count = 0;
+
+ cursor_visible_phase = !cursor_visible_phase;
- s->cursor_visible_phase = !s->cursor_visible_phase;
- graphic_hw_invalidate(s);
- timer_mod(s->cursor_timer,
- qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
+ for (i = 0; i < nb_consoles; i++) {
+ s = consoles[i];
+ if (qemu_console_is_graphic(s) ||
+ !qemu_console_is_visible(s)) {
+ continue;
+ }
+ count++;
+ graphic_hw_invalidate(s);
+ }
+
+ if (count) {
+ text_console_update_cursor_timer();
+ }
}
static const GraphicHwOps text_console_ops = {
@@ -1755,9 +1759,6 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
s->surface = qemu_create_displaysurface(g_width, g_height);
}
- s->cursor_timer =
- timer_new_ms(QEMU_CLOCK_REALTIME, text_console_update_cursor, s);
-
s->hw_ops = &text_console_ops;
s->hw = s;
@@ -1811,9 +1812,9 @@ static CharDriverState *text_console_init(ChardevVC *vc)
trace_console_txt_new(width, height);
if (width == 0 || height == 0) {
- s = new_console(NULL, TEXT_CONSOLE);
+ s = new_console(NULL, TEXT_CONSOLE, 0);
} else {
- s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
+ s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0);
s->surface = qemu_create_displaysurface(width, height);
}
diff --git a/ui/curses.c b/ui/curses.c
index b044790e43..de85f7610f 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -288,8 +288,8 @@ static void curses_refresh(DisplayChangeListener *dcl)
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
}
- qemu_input_event_send_key_number(NULL, keycode, true);
- qemu_input_event_send_key_number(NULL, keycode, false);
+ qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
+ qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
if (keycode & ALTGR) {
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
diff --git a/ui/gtk.c b/ui/gtk.c
index 9f5061a1be..b9089360f0 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -67,13 +67,38 @@
#include "x_keymap.h"
#include "keymaps.h"
#include "sysemu/char.h"
+#include "qom/object.h"
+#ifndef _WIN32
+#include <gdk/gdkx.h>
+#include <X11/XKBlib.h>
+#endif
#define MAX_VCS 10
+#define VC_WINDOW_X_MIN 320
+#define VC_WINDOW_Y_MIN 240
+#define VC_TERM_X_MIN 80
+#define VC_TERM_Y_MIN 25
+#define VC_SCALE_MIN 0.25
+#define VC_SCALE_STEP 0.25
#if !defined(CONFIG_VTE)
# define VTE_CHECK_VERSION(a, b, c) 0
#endif
+#if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
+/*
+ * The gtk2 vte terminal widget seriously messes up the window resize
+ * for some reason. You basically can't make the qemu window smaller
+ * any more because the toplevel window geoemtry hints are overridden.
+ *
+ * Workaround that by hiding all vte widgets, except the one in the
+ * current tab.
+ *
+ * Luckily everything works smooth in gtk3.
+ */
+# define VTE_RESIZE_HACK 1
+#endif
+
/* Compatibility define to let us build on both Gtk2 and Gtk3 */
#if GTK_CHECK_VERSION(3, 0, 0)
static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
@@ -105,18 +130,48 @@ static const int modifier_keycode[] = {
0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
};
-typedef struct VirtualConsole
-{
- GtkWidget *menu_item;
- GtkWidget *terminal;
+typedef struct GtkDisplayState GtkDisplayState;
+
+typedef struct VirtualGfxConsole {
+ GtkWidget *drawing_area;
+ DisplayChangeListener dcl;
+ DisplaySurface *ds;
+ pixman_image_t *convert;
+ cairo_surface_t *surface;
+ double scale_x;
+ double scale_y;
+} VirtualGfxConsole;
+
#if defined(CONFIG_VTE)
- GtkWidget *scrolled_window;
+typedef struct VirtualVteConsole {
+ GtkWidget *box;
+ GtkWidget *scrollbar;
+ GtkWidget *terminal;
CharDriverState *chr;
+} VirtualVteConsole;
#endif
+
+typedef enum VirtualConsoleType {
+ GD_VC_GFX,
+ GD_VC_VTE,
+} VirtualConsoleType;
+
+typedef struct VirtualConsole {
+ GtkDisplayState *s;
+ char *label;
+ GtkWidget *window;
+ GtkWidget *menu_item;
+ GtkWidget *tab_item;
+ VirtualConsoleType type;
+ union {
+ VirtualGfxConsole gfx;
+#if defined(CONFIG_VTE)
+ VirtualVteConsole vte;
+#endif
+ };
} VirtualConsole;
-typedef struct GtkDisplayState
-{
+struct GtkDisplayState {
GtkWidget *window;
GtkWidget *menu_bar;
@@ -139,29 +194,24 @@ typedef struct GtkDisplayState
GtkWidget *zoom_fit_item;
GtkWidget *grab_item;
GtkWidget *grab_on_hover_item;
- GtkWidget *vga_item;
int nb_vcs;
VirtualConsole vc[MAX_VCS];
GtkWidget *show_tabs_item;
+ GtkWidget *untabify_item;
GtkWidget *vbox;
GtkWidget *notebook;
- GtkWidget *drawing_area;
- cairo_surface_t *surface;
- pixman_image_t *convert;
- DisplayChangeListener dcl;
- DisplaySurface *ds;
int button_mask;
gboolean last_set;
int last_x;
int last_y;
int grab_x_root;
int grab_y_root;
+ VirtualConsole *kbd_owner;
+ VirtualConsole *ptr_owner;
- double scale_x;
- double scale_y;
gboolean full_screen;
GdkCursor *null_cursor;
@@ -171,12 +221,52 @@ typedef struct GtkDisplayState
bool external_pause_update;
bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
-} GtkDisplayState;
+ bool has_evdev;
+};
-static GtkDisplayState *global_state;
+static void gd_grab_pointer(VirtualConsole *vc);
+static void gd_ungrab_pointer(GtkDisplayState *s);
/** Utility Functions **/
+static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
+{
+ VirtualConsole *vc;
+ gint i;
+
+ for (i = 0; i < s->nb_vcs; i++) {
+ vc = &s->vc[i];
+ if (gtk_check_menu_item_get_active
+ (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
+ return vc;
+ }
+ }
+ return NULL;
+}
+
+static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
+{
+ VirtualConsole *vc;
+ gint i, p;
+
+ for (i = 0; i < s->nb_vcs; i++) {
+ vc = &s->vc[i];
+ p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
+ if (p == page) {
+ return vc;
+ }
+ }
+ return NULL;
+}
+
+static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
+{
+ gint page;
+
+ page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
+ return gd_vc_find_by_page(s, page);
+}
+
static bool gd_is_grab_active(GtkDisplayState *s)
{
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
@@ -187,22 +277,17 @@ static bool gd_grab_on_hover(GtkDisplayState *s)
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
}
-static bool gd_on_vga(GtkDisplayState *s)
-{
- return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0;
-}
-
-static void gd_update_cursor(GtkDisplayState *s, gboolean override)
+static void gd_update_cursor(VirtualConsole *vc)
{
+ GtkDisplayState *s = vc->s;
GdkWindow *window;
- bool on_vga;
- window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area));
-
- on_vga = gd_on_vga(s);
+ if (vc->type != GD_VC_GFX) {
+ return;
+ }
- if ((override || on_vga) &&
- (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s))) {
+ window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
+ if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
gdk_window_set_cursor(window, s->null_cursor);
} else {
gdk_window_set_cursor(window, NULL);
@@ -212,11 +297,20 @@ static void gd_update_cursor(GtkDisplayState *s, gboolean override)
static void gd_update_caption(GtkDisplayState *s)
{
const char *status = "";
+ gchar *prefix;
gchar *title;
const char *grab = "";
bool is_paused = !runstate_is_running();
+ int i;
- if (gd_is_grab_active(s)) {
+ if (qemu_name) {
+ prefix = g_strdup_printf("QEMU (%s)", qemu_name);
+ } else {
+ prefix = g_strdup_printf("QEMU");
+ }
+
+ if (s->ptr_owner != NULL &&
+ s->ptr_owner->window == NULL) {
grab = _(" - Press Ctrl+Alt+G to release grab");
}
@@ -228,60 +322,100 @@ static void gd_update_caption(GtkDisplayState *s)
is_paused);
s->external_pause_update = false;
- if (qemu_name) {
- title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab);
- } else {
- title = g_strdup_printf("QEMU%s%s", status, grab);
- }
-
+ title = g_strdup_printf("%s%s%s", prefix, status, grab);
gtk_window_set_title(GTK_WINDOW(s->window), title);
-
g_free(title);
+
+ for (i = 0; i < s->nb_vcs; i++) {
+ VirtualConsole *vc = &s->vc[i];
+
+ if (!vc->window) {
+ continue;
+ }
+ title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
+ vc == s->kbd_owner ? " +kbd" : "",
+ vc == s->ptr_owner ? " +ptr" : "");
+ gtk_window_set_title(GTK_WINDOW(vc->window), title);
+ g_free(title);
+ }
+
+ g_free(prefix);
}
-static void gd_update_windowsize(GtkDisplayState *s)
+static void gd_update_geometry_hints(VirtualConsole *vc)
{
- if (!s->full_screen) {
- GtkRequisition req;
- double sx, sy;
+ GtkDisplayState *s = vc->s;
+ GdkWindowHints mask = 0;
+ GdkGeometry geo = {};
+ GtkWidget *geo_widget = NULL;
+ GtkWindow *geo_window;
+ if (vc->type == GD_VC_GFX) {
if (s->free_scale) {
- sx = s->scale_x;
- sy = s->scale_y;
-
- s->scale_y = 1.0;
- s->scale_x = 1.0;
+ geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
+ geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
+ mask |= GDK_HINT_MIN_SIZE;
} else {
- sx = 1.0;
- sy = 1.0;
+ geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
+ geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
+ mask |= GDK_HINT_MIN_SIZE;
}
+ geo_widget = vc->gfx.drawing_area;
+ gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
- gtk_widget_set_size_request(s->drawing_area,
- surface_width(s->ds) * s->scale_x,
- surface_height(s->ds) * s->scale_y);
-#if GTK_CHECK_VERSION(3, 0, 0)
- gtk_widget_get_preferred_size(s->vbox, NULL, &req);
-#else
- gtk_widget_size_request(s->vbox, &req);
+#if defined(CONFIG_VTE)
+ } else if (vc->type == GD_VC_VTE) {
+ VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
+ GtkBorder *ib;
+
+ geo.width_inc = vte_terminal_get_char_width(term);
+ geo.height_inc = vte_terminal_get_char_height(term);
+ mask |= GDK_HINT_RESIZE_INC;
+ geo.base_width = geo.width_inc;
+ geo.base_height = geo.height_inc;
+ mask |= GDK_HINT_BASE_SIZE;
+ geo.min_width = geo.width_inc * VC_TERM_X_MIN;
+ geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
+ mask |= GDK_HINT_MIN_SIZE;
+ gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
+ geo.base_width += ib->left + ib->right;
+ geo.base_height += ib->top + ib->bottom;
+ geo.min_width += ib->left + ib->right;
+ geo.min_height += ib->top + ib->bottom;
+ geo_widget = vc->vte.terminal;
#endif
+ }
+
+ geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
+ gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
+}
+
+static void gd_update_windowsize(VirtualConsole *vc)
+{
+ GtkDisplayState *s = vc->s;
+
+ gd_update_geometry_hints(vc);
- gtk_window_resize(GTK_WINDOW(s->window),
- req.width * sx, req.height * sy);
+ if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
+ gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
+ VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
}
}
-static void gd_update_full_redraw(GtkDisplayState *s)
+static void gd_update_full_redraw(VirtualConsole *vc)
{
+ GtkWidget *area = vc->gfx.drawing_area;
int ww, wh;
- gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
- gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh);
+ gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
+ gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
}
static void gtk_release_modifiers(GtkDisplayState *s)
{
+ VirtualConsole *vc = gd_vc_find_current(s);
int i, keycode;
- if (!gd_on_vga(s)) {
+ if (vc->type != GD_VC_GFX) {
return;
}
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
@@ -289,7 +423,7 @@ static void gtk_release_modifiers(GtkDisplayState *s)
if (!s->modifier_pressed[i]) {
continue;
}
- qemu_input_event_send_key_number(s->dcl.con, keycode, false);
+ qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
s->modifier_pressed[i] = false;
}
}
@@ -299,29 +433,31 @@ static void gtk_release_modifiers(GtkDisplayState *s)
static void gd_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
- GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
int x1, x2, y1, y2;
int mx, my;
int fbw, fbh;
int ww, wh;
- trace_gd_update(x, y, w, h);
+ trace_gd_update(vc->label, x, y, w, h);
- if (s->convert) {
- pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert,
+ if (vc->gfx.convert) {
+ pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
+ NULL, vc->gfx.convert,
x, y, 0, 0, x, y, w, h);
}
- x1 = floor(x * s->scale_x);
- y1 = floor(y * s->scale_y);
+ x1 = floor(x * vc->gfx.scale_x);
+ y1 = floor(y * vc->gfx.scale_y);
- x2 = ceil(x * s->scale_x + w * s->scale_x);
- y2 = ceil(y * s->scale_y + h * s->scale_y);
+ x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
+ y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
- fbw = surface_width(s->ds) * s->scale_x;
- fbh = surface_height(s->ds) * s->scale_y;
+ fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
+ fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
- gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
+ gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
+ &ww, &wh);
mx = my = 0;
if (ww > fbw) {
@@ -331,7 +467,8 @@ static void gd_update(DisplayChangeListener *dcl,
my = (wh - fbh) / 2;
}
- gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1));
+ gtk_widget_queue_draw_area(vc->gfx.drawing_area,
+ mx + x1, my + y1, (x2 - x1), (y2 - y1));
}
static void gd_refresh(DisplayChangeListener *dcl)
@@ -343,7 +480,7 @@ static void gd_refresh(DisplayChangeListener *dcl)
static void gd_mouse_set(DisplayChangeListener *dcl,
int x, int y, int visible)
{
- GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
GdkDisplay *dpy;
GdkDeviceManager *mgr;
gint x_root, y_root;
@@ -352,29 +489,29 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
return;
}
- dpy = gtk_widget_get_display(s->drawing_area);
+ dpy = gtk_widget_get_display(vc->gfx.drawing_area);
mgr = gdk_display_get_device_manager(dpy);
- gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
+ gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
x, y, &x_root, &y_root);
gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
- gtk_widget_get_screen(s->drawing_area),
+ gtk_widget_get_screen(vc->gfx.drawing_area),
x_root, y_root);
}
#else
static void gd_mouse_set(DisplayChangeListener *dcl,
int x, int y, int visible)
{
- GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
gint x_root, y_root;
if (qemu_input_is_absolute()) {
return;
}
- gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
+ gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
x, y, &x_root, &y_root);
- gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area),
- gtk_widget_get_screen(s->drawing_area),
+ gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
+ gtk_widget_get_screen(vc->gfx.drawing_area),
x_root, y_root);
}
#endif
@@ -382,7 +519,7 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
static void gd_cursor_define(DisplayChangeListener *dcl,
QEMUCursor *c)
{
- GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
GdkPixbuf *pixbuf;
GdkCursor *cursor;
@@ -390,9 +527,10 @@ static void gd_cursor_define(DisplayChangeListener *dcl,
GDK_COLORSPACE_RGB, true, 8,
c->width, c->height, c->width * 4,
NULL, NULL);
- cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(s->drawing_area),
- pixbuf, c->hot_x, c->hot_y);
- gdk_window_set_cursor(gtk_widget_get_window(s->drawing_area), cursor);
+ cursor = gdk_cursor_new_from_pixbuf
+ (gtk_widget_get_display(vc->gfx.drawing_area),
+ pixbuf, c->hot_x, c->hot_y);
+ gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
g_object_unref(pixbuf);
#if !GTK_CHECK_VERSION(3, 0, 0)
gdk_cursor_unref(cursor);
@@ -404,25 +542,25 @@ static void gd_cursor_define(DisplayChangeListener *dcl,
static void gd_switch(DisplayChangeListener *dcl,
DisplaySurface *surface)
{
- GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
bool resized = true;
- trace_gd_switch(surface_width(surface), surface_height(surface));
+ trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
- if (s->surface) {
- cairo_surface_destroy(s->surface);
+ if (vc->gfx.surface) {
+ cairo_surface_destroy(vc->gfx.surface);
}
- if (s->ds &&
- surface_width(s->ds) == surface_width(surface) &&
- surface_height(s->ds) == surface_height(surface)) {
+ if (vc->gfx.ds &&
+ surface_width(vc->gfx.ds) == surface_width(surface) &&
+ surface_height(vc->gfx.ds) == surface_height(surface)) {
resized = false;
}
- s->ds = surface;
+ vc->gfx.ds = surface;
- if (s->convert) {
- pixman_image_unref(s->convert);
- s->convert = NULL;
+ if (vc->gfx.convert) {
+ pixman_image_unref(vc->gfx.convert);
+ vc->gfx.convert = NULL;
}
if (surface->format == PIXMAN_x8r8g8b8) {
@@ -432,7 +570,7 @@ static void gd_switch(DisplayChangeListener *dcl,
* No need to convert, use surface directly. Should be the
* common case as this is qemu_default_pixelformat(32) too.
*/
- s->surface = cairo_image_surface_create_for_data
+ vc->gfx.surface = cairo_image_surface_create_for_data
(surface_data(surface),
CAIRO_FORMAT_RGB24,
surface_width(surface),
@@ -440,26 +578,27 @@ static void gd_switch(DisplayChangeListener *dcl,
surface_stride(surface));
} else {
/* Must convert surface, use pixman to do it. */
- s->convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
- surface_width(surface),
- surface_height(surface),
- NULL, 0);
- s->surface = cairo_image_surface_create_for_data
- ((void *)pixman_image_get_data(s->convert),
+ vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+ surface_width(surface),
+ surface_height(surface),
+ NULL, 0);
+ vc->gfx.surface = cairo_image_surface_create_for_data
+ ((void *)pixman_image_get_data(vc->gfx.convert),
CAIRO_FORMAT_RGB24,
- pixman_image_get_width(s->convert),
- pixman_image_get_height(s->convert),
- pixman_image_get_stride(s->convert));
- pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert,
+ pixman_image_get_width(vc->gfx.convert),
+ pixman_image_get_height(vc->gfx.convert),
+ pixman_image_get_stride(vc->gfx.convert));
+ pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
+ NULL, vc->gfx.convert,
0, 0, 0, 0, 0, 0,
- pixman_image_get_width(s->convert),
- pixman_image_get_height(s->convert));
+ pixman_image_get_width(vc->gfx.convert),
+ pixman_image_get_height(vc->gfx.convert));
}
if (resized) {
- gd_update_windowsize(s);
+ gd_update_windowsize(vc);
} else {
- gd_update_full_redraw(s);
+ gd_update_full_redraw(vc);
}
}
@@ -475,6 +614,7 @@ static void gd_change_runstate(void *opaque, int running, RunState state)
static void gd_mouse_mode_change(Notifier *notify, void *data)
{
GtkDisplayState *s;
+ int i;
s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
/* release the grab at switching to absolute mode */
@@ -482,7 +622,10 @@ static void gd_mouse_mode_change(Notifier *notify, void *data)
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
FALSE);
}
- gd_update_cursor(s, FALSE);
+ for (i = 0; i < s->nb_vcs; i++) {
+ VirtualConsole *vc = &s->vc[i];
+ gd_update_cursor(vc);
+ }
}
/** GTK Events **/
@@ -491,9 +634,15 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
void *opaque)
{
GtkDisplayState *s = opaque;
+ int i;
if (!no_quit) {
- unregister_displaychangelistener(&s->dcl);
+ for (i = 0; i < s->nb_vcs; i++) {
+ if (s->vc[i].type != GD_VC_GFX) {
+ continue;
+ }
+ unregister_displaychangelistener(&s->vc[i].gfx.dcl);
+ }
qmp_quit(NULL);
return FALSE;
}
@@ -503,7 +652,8 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
{
- GtkDisplayState *s = opaque;
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
int mx, my;
int ww, wh;
int fbw, fbh;
@@ -512,25 +662,25 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
return FALSE;
}
- fbw = surface_width(s->ds);
- fbh = surface_height(s->ds);
+ fbw = surface_width(vc->gfx.ds);
+ fbh = surface_height(vc->gfx.ds);
gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
if (s->full_screen) {
- s->scale_x = (double)ww / fbw;
- s->scale_y = (double)wh / fbh;
+ vc->gfx.scale_x = (double)ww / fbw;
+ vc->gfx.scale_y = (double)wh / fbh;
} else if (s->free_scale) {
double sx, sy;
sx = (double)ww / fbw;
sy = (double)wh / fbh;
- s->scale_x = s->scale_y = MIN(sx, sy);
+ vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
}
- fbw *= s->scale_x;
- fbh *= s->scale_y;
+ fbw *= vc->gfx.scale_x;
+ fbh *= vc->gfx.scale_y;
mx = my = 0;
if (ww > fbw) {
@@ -551,8 +701,9 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
-1 * fbw, fbh);
cairo_fill(cr);
- cairo_scale(cr, s->scale_x, s->scale_y);
- cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y);
+ cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
+ cairo_set_source_surface(cr, vc->gfx.surface,
+ mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
cairo_paint(cr);
return TRUE;
@@ -584,16 +735,18 @@ static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
void *opaque)
{
- GtkDisplayState *s = opaque;
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
int x, y;
int mx, my;
int fbh, fbw;
int ww, wh;
- fbw = surface_width(s->ds) * s->scale_x;
- fbh = surface_height(s->ds) * s->scale_y;
+ fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
+ fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
- gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
+ gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
+ &ww, &wh);
mx = my = 0;
if (ww > fbw) {
@@ -603,31 +756,31 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
my = (wh - fbh) / 2;
}
- x = (motion->x - mx) / s->scale_x;
- y = (motion->y - my) / s->scale_y;
+ x = (motion->x - mx) / vc->gfx.scale_x;
+ y = (motion->y - my) / vc->gfx.scale_y;
if (qemu_input_is_absolute()) {
if (x < 0 || y < 0 ||
- x >= surface_width(s->ds) ||
- y >= surface_height(s->ds)) {
+ x >= surface_width(vc->gfx.ds) ||
+ y >= surface_height(vc->gfx.ds)) {
return TRUE;
}
- qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_X, x,
- surface_width(s->ds));
- qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_Y, y,
- surface_height(s->ds));
+ qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
+ surface_width(vc->gfx.ds));
+ qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
+ surface_height(vc->gfx.ds));
qemu_input_event_sync();
- } else if (s->last_set && gd_is_grab_active(s)) {
- qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_X, x - s->last_x);
- qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_Y, y - s->last_y);
+ } else if (s->last_set && s->ptr_owner == vc) {
+ qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
+ qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
qemu_input_event_sync();
}
s->last_x = x;
s->last_y = y;
s->last_set = TRUE;
- if (!qemu_input_is_absolute() && gd_is_grab_active(s)) {
- GdkScreen *screen = gtk_widget_get_screen(s->drawing_area);
+ if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
+ GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
int x = (int)motion->x_root;
int y = (int)motion->y_root;
@@ -669,14 +822,21 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
void *opaque)
{
- GtkDisplayState *s = opaque;
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
InputButton btn;
/* implicitly grab the input at the first click in the relative mode */
if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
- !qemu_input_is_absolute() && !gd_is_grab_active(s)) {
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
- TRUE);
+ !qemu_input_is_absolute() && s->ptr_owner != vc) {
+ gd_ungrab_pointer(s);
+ if (!vc->window) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ TRUE);
+ } else {
+ gd_grab_pointer(vc);
+ gd_update_caption(s);
+ }
return TRUE;
}
@@ -690,7 +850,8 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
return TRUE;
}
- qemu_input_queue_btn(s->dcl.con, btn, button->type == GDK_BUTTON_PRESS);
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn,
+ button->type == GDK_BUTTON_PRESS);
qemu_input_event_sync();
return TRUE;
}
@@ -698,7 +859,7 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
void *opaque)
{
- GtkDisplayState *s = opaque;
+ VirtualConsole *vc = opaque;
InputButton btn;
if (scroll->direction == GDK_SCROLL_UP) {
@@ -709,16 +870,17 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
return TRUE;
}
- qemu_input_queue_btn(s->dcl.con, btn, true);
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
qemu_input_event_sync();
- qemu_input_queue_btn(s->dcl.con, btn, false);
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
qemu_input_event_sync();
return TRUE;
}
static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
{
- GtkDisplayState *s = opaque;
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
int gdk_keycode = key->hardware_keycode;
int i;
@@ -737,7 +899,11 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
} else if (gdk_keycode < 97) {
qemu_keycode = gdk_keycode - 8;
} else if (gdk_keycode < 158) {
- qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
+ if (s->has_evdev) {
+ qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
+ } else {
+ qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
+ }
} else if (gdk_keycode == 208) { /* Hiragana_Katakana */
qemu_keycode = 0x70;
} else if (gdk_keycode == 211) { /* backslash */
@@ -747,7 +913,7 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
}
#endif
- trace_gd_key_event(gdk_keycode, qemu_keycode,
+ trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
(key->type == GDK_KEY_PRESS) ? "down" : "up");
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
@@ -756,7 +922,7 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
}
}
- qemu_input_event_send_key_number(s->dcl.con, qemu_keycode,
+ qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
key->type == GDK_KEY_PRESS);
return TRUE;
@@ -804,19 +970,14 @@ static void gd_menu_quit(GtkMenuItem *item, void *opaque)
static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
{
GtkDisplayState *s = opaque;
+ VirtualConsole *vc = gd_vc_find_by_menu(s);
+ gint page;
- if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) {
- gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0);
- } else {
- int i;
-
- gtk_release_modifiers(s);
- for (i = 0; i < s->nb_vcs; i++) {
- if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) {
- gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1);
- break;
- }
- }
+ gtk_release_modifiers(s);
+ if (vc) {
+ page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook),
+ vc->tab_item);
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page);
}
}
@@ -831,94 +992,159 @@ static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
}
}
+static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
+ void *opaque)
+{
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
+
+ gtk_widget_set_sensitive(vc->menu_item, true);
+ gtk_widget_reparent(vc->tab_item, s->notebook);
+ gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
+ vc->tab_item, vc->label);
+ gtk_widget_destroy(vc->window);
+ vc->window = NULL;
+ return TRUE;
+}
+
+static gboolean gd_win_grab(void *opaque)
+{
+ VirtualConsole *vc = opaque;
+
+ fprintf(stderr, "%s: %s\n", __func__, vc->label);
+ if (vc->s->ptr_owner) {
+ gd_ungrab_pointer(vc->s);
+ } else {
+ gd_grab_pointer(vc);
+ }
+ gd_update_caption(vc->s);
+ return TRUE;
+}
+
+static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+ VirtualConsole *vc = gd_vc_find_current(s);
+
+ if (vc->type == GD_VC_GFX) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ FALSE);
+ }
+ if (!vc->window) {
+ gtk_widget_set_sensitive(vc->menu_item, false);
+ vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_reparent(vc->tab_item, vc->window);
+
+ g_signal_connect(vc->window, "delete-event",
+ G_CALLBACK(gd_tab_window_close), vc);
+ gtk_widget_show_all(vc->window);
+
+ GtkAccelGroup *ag = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
+
+ GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), vc, NULL);
+ gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
+
+ gd_update_geometry_hints(vc);
+ gd_update_caption(s);
+ }
+}
+
static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
{
GtkDisplayState *s = opaque;
+ VirtualConsole *vc = gd_vc_find_current(s);
if (!s->full_screen) {
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
gtk_widget_set_size_request(s->menu_bar, 0, 0);
- gtk_widget_set_size_request(s->drawing_area, -1, -1);
- gtk_window_fullscreen(GTK_WINDOW(s->window));
- if (gd_on_vga(s)) {
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE);
+ if (vc->type == GD_VC_GFX) {
+ gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ TRUE);
}
+ gtk_window_fullscreen(GTK_WINDOW(s->window));
s->full_screen = TRUE;
} else {
gtk_window_unfullscreen(GTK_WINDOW(s->window));
gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
gtk_widget_set_size_request(s->menu_bar, -1, -1);
- gtk_widget_set_size_request(s->drawing_area,
- surface_width(s->ds),
- surface_height(s->ds));
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE);
s->full_screen = FALSE;
- s->scale_x = 1.0;
- s->scale_y = 1.0;
+ if (vc->type == GD_VC_GFX) {
+ vc->gfx.scale_x = 1.0;
+ vc->gfx.scale_y = 1.0;
+ gd_update_windowsize(vc);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ FALSE);
+ }
}
- gd_update_cursor(s, FALSE);
+ gd_update_cursor(vc);
}
static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
{
GtkDisplayState *s = opaque;
+ VirtualConsole *vc = gd_vc_find_current(s);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
FALSE);
- s->scale_x += .25;
- s->scale_y += .25;
+ vc->gfx.scale_x += VC_SCALE_STEP;
+ vc->gfx.scale_y += VC_SCALE_STEP;
- gd_update_windowsize(s);
+ gd_update_windowsize(vc);
}
static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
{
GtkDisplayState *s = opaque;
+ VirtualConsole *vc = gd_vc_find_current(s);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
FALSE);
- s->scale_x -= .25;
- s->scale_y -= .25;
+ vc->gfx.scale_x -= VC_SCALE_STEP;
+ vc->gfx.scale_y -= VC_SCALE_STEP;
- s->scale_x = MAX(s->scale_x, .25);
- s->scale_y = MAX(s->scale_y, .25);
+ vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
+ vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
- gd_update_windowsize(s);
+ gd_update_windowsize(vc);
}
static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
{
GtkDisplayState *s = opaque;
+ VirtualConsole *vc = gd_vc_find_current(s);
- s->scale_x = 1.0;
- s->scale_y = 1.0;
+ vc->gfx.scale_x = 1.0;
+ vc->gfx.scale_y = 1.0;
- gd_update_windowsize(s);
+ gd_update_windowsize(vc);
}
static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
{
GtkDisplayState *s = opaque;
+ VirtualConsole *vc = gd_vc_find_current(s);
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
s->free_scale = TRUE;
} else {
s->free_scale = FALSE;
- s->scale_x = 1.0;
- s->scale_y = 1.0;
- gd_update_windowsize(s);
+ vc->gfx.scale_x = 1.0;
+ vc->gfx.scale_y = 1.0;
}
- gd_update_full_redraw(s);
+ gd_update_windowsize(vc);
+ gd_update_full_redraw(vc);
}
-static void gd_grab_keyboard(GtkDisplayState *s)
+static void gd_grab_keyboard(VirtualConsole *vc)
{
#if GTK_CHECK_VERSION(3, 0, 0)
- GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
+ GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
GList *devices = gdk_device_manager_list_devices(mgr,
GDK_DEVICE_TYPE_MASTER);
@@ -927,7 +1153,7 @@ static void gd_grab_keyboard(GtkDisplayState *s)
GdkDevice *dev = tmp->data;
if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) {
gdk_device_grab(dev,
- gtk_widget_get_window(s->drawing_area),
+ gtk_widget_get_window(vc->gfx.drawing_area),
GDK_OWNERSHIP_NONE,
FALSE,
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
@@ -938,16 +1164,25 @@ static void gd_grab_keyboard(GtkDisplayState *s)
}
g_list_free(devices);
#else
- gdk_keyboard_grab(gtk_widget_get_window(s->drawing_area),
+ gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
FALSE,
GDK_CURRENT_TIME);
#endif
+ vc->s->kbd_owner = vc;
+ trace_gd_grab(vc->label, "kbd", true);
}
static void gd_ungrab_keyboard(GtkDisplayState *s)
{
+ VirtualConsole *vc = s->kbd_owner;
+
+ if (vc == NULL) {
+ return;
+ }
+ s->kbd_owner = NULL;
+
#if GTK_CHECK_VERSION(3, 0, 0)
- GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
+ GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
GList *devices = gdk_device_manager_list_devices(mgr,
GDK_DEVICE_TYPE_MASTER);
@@ -964,11 +1199,12 @@ static void gd_ungrab_keyboard(GtkDisplayState *s)
#else
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
#endif
+ trace_gd_grab(vc->label, "kbd", false);
}
-static void gd_grab_pointer(GtkDisplayState *s)
+static void gd_grab_pointer(VirtualConsole *vc)
{
- GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
+ GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
#if GTK_CHECK_VERSION(3, 0, 0)
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
GList *devices = gdk_device_manager_list_devices(mgr,
@@ -978,7 +1214,7 @@ static void gd_grab_pointer(GtkDisplayState *s)
GdkDevice *dev = tmp->data;
if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) {
gdk_device_grab(dev,
- gtk_widget_get_window(s->drawing_area),
+ gtk_widget_get_window(vc->gfx.drawing_area),
GDK_OWNERSHIP_NONE,
FALSE, /* All events to come to our
window directly */
@@ -987,16 +1223,16 @@ static void gd_grab_pointer(GtkDisplayState *s)
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_SCROLL_MASK,
- s->null_cursor,
+ vc->s->null_cursor,
GDK_CURRENT_TIME);
}
tmp = tmp->next;
}
g_list_free(devices);
gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
- NULL, &s->grab_x_root, &s->grab_y_root);
+ NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
#else
- gdk_pointer_grab(gtk_widget_get_window(s->drawing_area),
+ gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
FALSE, /* All events to come to our window directly */
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK |
@@ -1004,16 +1240,25 @@ static void gd_grab_pointer(GtkDisplayState *s)
GDK_BUTTON_MOTION_MASK |
GDK_SCROLL_MASK,
NULL, /* Allow cursor to move over entire desktop */
- s->null_cursor,
+ vc->s->null_cursor,
GDK_CURRENT_TIME);
gdk_display_get_pointer(display, NULL,
- &s->grab_x_root, &s->grab_y_root, NULL);
+ &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
#endif
+ vc->s->ptr_owner = vc;
+ trace_gd_grab(vc->label, "ptr", true);
}
static void gd_ungrab_pointer(GtkDisplayState *s)
{
- GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
+ VirtualConsole *vc = s->ptr_owner;
+
+ if (vc == NULL) {
+ return;
+ }
+ s->ptr_owner = NULL;
+
+ GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
#if GTK_CHECK_VERSION(3, 0, 0)
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
GList *devices = gdk_device_manager_list_devices(mgr,
@@ -1029,51 +1274,65 @@ static void gd_ungrab_pointer(GtkDisplayState *s)
}
g_list_free(devices);
gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
- gtk_widget_get_screen(s->drawing_area),
- s->grab_x_root, s->grab_y_root);
+ gtk_widget_get_screen(vc->gfx.drawing_area),
+ vc->s->grab_x_root, vc->s->grab_y_root);
#else
gdk_pointer_ungrab(GDK_CURRENT_TIME);
gdk_display_warp_pointer(display,
- gtk_widget_get_screen(s->drawing_area),
- s->grab_x_root, s->grab_y_root);
+ gtk_widget_get_screen(vc->gfx.drawing_area),
+ vc->s->grab_x_root, vc->s->grab_y_root);
#endif
+ trace_gd_grab(vc->label, "ptr", false);
}
static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
{
GtkDisplayState *s = opaque;
+ VirtualConsole *vc = gd_vc_find_current(s);
if (gd_is_grab_active(s)) {
- gd_grab_keyboard(s);
- gd_grab_pointer(s);
+ if (!gd_grab_on_hover(s)) {
+ gd_grab_keyboard(vc);
+ }
+ gd_grab_pointer(vc);
} else {
gd_ungrab_keyboard(s);
gd_ungrab_pointer(s);
}
gd_update_caption(s);
- gd_update_cursor(s, FALSE);
+ gd_update_cursor(vc);
}
static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
gpointer data)
{
GtkDisplayState *s = data;
- guint last_page;
+ VirtualConsole *vc;
gboolean on_vga;
if (!gtk_widget_get_realized(s->notebook)) {
return;
}
- last_page = gtk_notebook_get_current_page(nb);
-
- if (last_page) {
- gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1);
+#ifdef VTE_RESIZE_HACK
+ vc = gd_vc_find_current(s);
+ if (vc && vc->type == GD_VC_VTE) {
+ gtk_widget_hide(vc->vte.terminal);
}
-
- on_vga = arg2 == 0;
-
+#endif
+ vc = gd_vc_find_by_page(s, arg2);
+ if (!vc) {
+ return;
+ }
+#ifdef VTE_RESIZE_HACK
+ if (vc->type == GD_VC_VTE) {
+ gtk_widget_show(vc->vte.terminal);
+ }
+#endif
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
+ TRUE);
+ on_vga = (vc->type == GD_VC_GFX);
if (!on_vga) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
FALSE);
@@ -1081,71 +1340,88 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
TRUE);
}
-
- if (arg2 == 0) {
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
- } else {
-#if defined(CONFIG_VTE)
- VirtualConsole *vc = &s->vc[arg2 - 1];
- VteTerminal *term = VTE_TERMINAL(vc->terminal);
- int width, height;
-
- width = 80 * vte_terminal_get_char_width(term);
- height = 25 * vte_terminal_get_char_height(term);
-
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
- gtk_widget_set_size_request(vc->terminal, width, height);
-#else
- g_assert_not_reached();
-#endif
- }
-
gtk_widget_set_sensitive(s->grab_item, on_vga);
- gd_update_cursor(s, TRUE);
+ gd_update_windowsize(vc);
+ gd_update_cursor(vc);
}
-static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
+static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
+ gpointer opaque)
{
- GtkDisplayState *s = data;
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
- if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
- gd_grab_keyboard(s);
+ if (gd_grab_on_hover(s)) {
+ gd_ungrab_keyboard(s);
+ gd_grab_keyboard(vc);
+ gd_update_caption(s);
}
-
return TRUE;
}
-static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
+static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
+ gpointer opaque)
{
- GtkDisplayState *s = data;
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
- if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
+ if (gd_grab_on_hover(s)) {
gd_ungrab_keyboard(s);
+ gd_update_caption(s);
}
-
return TRUE;
}
static gboolean gd_focus_out_event(GtkWidget *widget,
- GdkEventCrossing *crossing, gpointer data)
+ GdkEventCrossing *crossing, gpointer opaque)
{
- GtkDisplayState *s = data;
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
gtk_release_modifiers(s);
-
return TRUE;
}
/** Virtual Console Callbacks **/
-static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
+ int idx, GSList *group, GtkWidget *view_menu)
{
+ char path[32];
+
+ snprintf(path, sizeof(path), "<QEMU>/View/VC%d", idx);
+
+ vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
+ gtk_accel_map_add_entry(path, GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
+
+ g_signal_connect(vc->menu_item, "activate",
+ G_CALLBACK(gd_menu_switch_vc), s);
+ gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
+
+ return group;
+}
+
#if defined(CONFIG_VTE)
+static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
+{
+ VirtualConsole *vc = opaque;
+
+ if (gtk_adjustment_get_upper(adjustment) >
+ gtk_adjustment_get_page_size(adjustment)) {
+ gtk_widget_show(vc->vte.scrollbar);
+ } else {
+ gtk_widget_hide(vc->vte.scrollbar);
+ }
+}
+
+static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
VirtualConsole *vc = chr->opaque;
- vte_terminal_feed(VTE_TERMINAL(vc->terminal), (const char *)buf, len);
-#endif
+ vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
return len;
}
@@ -1166,115 +1442,132 @@ static CharDriverState *gd_vc_handler(ChardevVC *unused)
return chr;
}
-void early_gtk_display_init(void)
-{
- register_vc_handler(gd_vc_handler);
-}
-
-#if defined(CONFIG_VTE)
static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
gpointer user_data)
{
VirtualConsole *vc = user_data;
- qemu_chr_be_write(vc->chr, (uint8_t *)text, (unsigned int)size);
+ qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size);
return TRUE;
}
-#endif
-static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group,
- GtkWidget *view_menu)
+static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
+ CharDriverState *chr, int idx,
+ GSList *group, GtkWidget *view_menu)
{
-#if defined(CONFIG_VTE)
- const char *label;
char buffer[32];
- char path[32];
- GtkWidget *scrolled_window;
+ GtkWidget *box;
+ GtkWidget *scrollbar;
GtkAdjustment *vadjustment;
- snprintf(buffer, sizeof(buffer), "vc%d", index);
- snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index);
-
- vc->chr = vcs[index];
-
- if (vc->chr->label) {
- label = vc->chr->label;
- } else {
- label = buffer;
- }
+ vc->s = s;
+ vc->vte.chr = chr;
- vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label);
- group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
- gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
- gtk_accel_map_add_entry(path, GDK_KEY_2 + index, HOTKEY_MODIFIERS);
+ snprintf(buffer, sizeof(buffer), "vc%d", idx);
+ vc->label = g_strdup_printf("%s", vc->vte.chr->label
+ ? vc->vte.chr->label : buffer);
+ group = gd_vc_menu_init(s, vc, idx, group, view_menu);
- vc->terminal = vte_terminal_new();
- g_signal_connect(vc->terminal, "commit", G_CALLBACK(gd_vc_in), vc);
+ vc->vte.terminal = vte_terminal_new();
+ g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
- vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1);
+ vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
+ vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
+ VC_TERM_X_MIN, VC_TERM_Y_MIN);
#if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
- vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(vc->terminal));
+ vadjustment = gtk_scrollable_get_vadjustment
+ (GTK_SCROLLABLE(vc->vte.terminal));
#else
- vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
+ vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
#endif
- scrolled_window = gtk_scrolled_window_new(NULL, vadjustment);
- gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
-
- vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25);
+#if GTK_CHECK_VERSION(3, 0, 0)
+ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
+ scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
+#else
+ box = gtk_hbox_new(false, 2);
+ scrollbar = gtk_vscrollbar_new(vadjustment);
+#endif
- vc->chr->opaque = vc;
- vc->scrolled_window = scrolled_window;
+ gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window),
- GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ vc->vte.chr->opaque = vc;
+ vc->vte.box = box;
+ vc->vte.scrollbar = scrollbar;
- gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label));
- g_signal_connect(vc->menu_item, "activate",
- G_CALLBACK(gd_menu_switch_vc), s);
+ g_signal_connect(vadjustment, "changed",
+ G_CALLBACK(gd_vc_adjustment_changed), vc);
- gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
+ vc->type = GD_VC_VTE;
+ vc->tab_item = box;
+ gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
+ gtk_label_new(vc->label));
- qemu_chr_be_generic_open(vc->chr);
- if (vc->chr->init) {
- vc->chr->init(vc->chr);
+ qemu_chr_be_generic_open(vc->vte.chr);
+ if (vc->vte.chr->init) {
+ vc->vte.chr->init(vc->vte.chr);
}
-#endif /* CONFIG_VTE */
return group;
}
+static void gd_vcs_init(GtkDisplayState *s, GSList *group,
+ GtkWidget *view_menu)
+{
+ int i;
+
+ for (i = 0; i < nb_vcs; i++) {
+ VirtualConsole *vc = &s->vc[s->nb_vcs];
+ group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
+ s->nb_vcs++;
+ }
+}
+#endif /* CONFIG_VTE */
+
/** Window Creation **/
+static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
+{
+#if GTK_CHECK_VERSION(3, 0, 0)
+ g_signal_connect(vc->gfx.drawing_area, "draw",
+ G_CALLBACK(gd_draw_event), vc);
+#else
+ g_signal_connect(vc->gfx.drawing_area, "expose-event",
+ G_CALLBACK(gd_expose_event), vc);
+#endif
+ g_signal_connect(vc->gfx.drawing_area, "event",
+ G_CALLBACK(gd_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "button-press-event",
+ G_CALLBACK(gd_button_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "button-release-event",
+ G_CALLBACK(gd_button_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "scroll-event",
+ G_CALLBACK(gd_scroll_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "key-press-event",
+ G_CALLBACK(gd_key_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "key-release-event",
+ G_CALLBACK(gd_key_event), vc);
+
+ g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
+ G_CALLBACK(gd_enter_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
+ G_CALLBACK(gd_leave_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
+ G_CALLBACK(gd_focus_out_event), vc);
+}
+
static void gd_connect_signals(GtkDisplayState *s)
{
g_signal_connect(s->show_tabs_item, "activate",
G_CALLBACK(gd_menu_show_tabs), s);
+ g_signal_connect(s->untabify_item, "activate",
+ G_CALLBACK(gd_menu_untabify), s);
g_signal_connect(s->window, "delete-event",
G_CALLBACK(gd_window_close), s);
-#if GTK_CHECK_VERSION(3, 0, 0)
- g_signal_connect(s->drawing_area, "draw",
- G_CALLBACK(gd_draw_event), s);
-#else
- g_signal_connect(s->drawing_area, "expose-event",
- G_CALLBACK(gd_expose_event), s);
-#endif
- g_signal_connect(s->drawing_area, "event",
- G_CALLBACK(gd_event), s);
- g_signal_connect(s->drawing_area, "button-press-event",
- G_CALLBACK(gd_button_event), s);
- g_signal_connect(s->drawing_area, "button-release-event",
- G_CALLBACK(gd_button_event), s);
- g_signal_connect(s->drawing_area, "scroll-event",
- G_CALLBACK(gd_scroll_event), s);
- g_signal_connect(s->drawing_area, "key-press-event",
- G_CALLBACK(gd_key_event), s);
- g_signal_connect(s->drawing_area, "key-release-event",
- G_CALLBACK(gd_key_event), s);
-
g_signal_connect(s->pause_item, "activate",
G_CALLBACK(gd_menu_pause), s);
g_signal_connect(s->reset_item, "activate",
@@ -1293,18 +1586,10 @@ static void gd_connect_signals(GtkDisplayState *s)
G_CALLBACK(gd_menu_zoom_fixed), s);
g_signal_connect(s->zoom_fit_item, "activate",
G_CALLBACK(gd_menu_zoom_fit), s);
- g_signal_connect(s->vga_item, "activate",
- G_CALLBACK(gd_menu_switch_vc), s);
g_signal_connect(s->grab_item, "activate",
G_CALLBACK(gd_menu_grab_input), s);
g_signal_connect(s->notebook, "switch-page",
G_CALLBACK(gd_change_page), s);
- g_signal_connect(s->drawing_area, "enter-notify-event",
- G_CALLBACK(gd_enter_event), s);
- g_signal_connect(s->drawing_area, "leave-notify-event",
- G_CALLBACK(gd_leave_event), s);
- g_signal_connect(s->drawing_area, "focus-out-event",
- G_CALLBACK(gd_focus_out_event), s);
}
static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *accel_group)
@@ -1340,12 +1625,68 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *acce
return machine_menu;
}
+static const DisplayChangeListenerOps dcl_ops = {
+ .dpy_name = "gtk",
+ .dpy_gfx_update = gd_update,
+ .dpy_gfx_switch = gd_switch,
+ .dpy_refresh = gd_refresh,
+ .dpy_mouse_set = gd_mouse_set,
+ .dpy_cursor_define = gd_cursor_define,
+};
+
+static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
+ QemuConsole *con, int idx,
+ GSList *group, GtkWidget *view_menu)
+{
+ Error *local_err = NULL;
+ Object *obj;
+
+ obj = object_property_get_link(OBJECT(con), "device", &local_err);
+ if (obj) {
+ vc->label = g_strdup_printf("%s", object_get_typename(obj));
+ } else {
+ vc->label = g_strdup_printf("VGA");
+ }
+
+ vc->s = s;
+ vc->gfx.scale_x = 1.0;
+ vc->gfx.scale_y = 1.0;
+
+ vc->gfx.drawing_area = gtk_drawing_area_new();
+ gtk_widget_add_events(vc->gfx.drawing_area,
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK);
+ gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
+ gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
+
+ vc->type = GD_VC_GFX;
+ vc->tab_item = vc->gfx.drawing_area;
+ gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
+ vc->tab_item, gtk_label_new(vc->label));
+ gd_connect_vc_gfx_signals(vc);
+
+ group = gd_vc_menu_init(s, vc, idx, group, view_menu);
+
+ vc->gfx.dcl.ops = &dcl_ops;
+ vc->gfx.dcl.con = con;
+ register_displaychangelistener(&vc->gfx.dcl);
+
+ return group;
+}
+
static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_group)
{
GSList *group = NULL;
GtkWidget *view_menu;
GtkWidget *separator;
- int i;
+ QemuConsole *con;
+ int vc;
view_menu = gtk_menu_new();
gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group);
@@ -1400,26 +1741,31 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g
separator = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
- s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA");
- group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item));
- gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item),
- "<QEMU>/View/VGA");
- gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, HOTKEY_MODIFIERS);
- gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->vga_item);
-
- for (i = 0; i < nb_vcs; i++) {
- VirtualConsole *vc = &s->vc[i];
-
- group = gd_vc_init(s, vc, i, group, view_menu);
+ /* gfx */
+ for (vc = 0;; vc++) {
+ con = qemu_console_lookup_by_index(vc);
+ if (!con || !qemu_console_is_graphic(con)) {
+ break;
+ }
+ group = gd_vc_gfx_init(s, &s->vc[vc], con,
+ vc, group, view_menu);
s->nb_vcs++;
}
+#if defined(CONFIG_VTE)
+ /* vte */
+ gd_vcs_init(s, group, view_menu);
+#endif
+
separator = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
+ s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
+ gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
+
return view_menu;
}
@@ -1445,14 +1791,28 @@ static void gd_create_menus(GtkDisplayState *s)
s->accel_group = accel_group;
}
-static const DisplayChangeListenerOps dcl_ops = {
- .dpy_name = "gtk",
- .dpy_gfx_update = gd_update,
- .dpy_gfx_switch = gd_switch,
- .dpy_refresh = gd_refresh,
- .dpy_mouse_set = gd_mouse_set,
- .dpy_cursor_define = gd_cursor_define,
-};
+static void gd_set_keycode_type(GtkDisplayState *s)
+{
+#ifndef _WIN32
+ char *keycodes = NULL;
+ GdkDisplay *display = gtk_widget_get_display(s->window);
+ Display *x11_display = gdk_x11_display_get_xdisplay(display);
+ XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
+ XkbUseCoreKbd);
+
+ if (desc && desc->names) {
+ keycodes = XGetAtomName(x11_display, desc->names->keycodes);
+ }
+ if (keycodes == NULL) {
+ fprintf(stderr, "could not lookup keycode name\n");
+ } else if (strstart(keycodes, "evdev", NULL)) {
+ s->has_evdev = true;
+ } else if (!strstart(keycodes, "xfree86", NULL)) {
+ fprintf(stderr, "unknown keycodes `%s', please report to "
+ "qemu-devel@nongnu.org\n", keycodes);
+ }
+#endif
+}
void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
{
@@ -1461,9 +1821,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
gtk_init(NULL, NULL);
- s->dcl.ops = &dcl_ops;
- s->dcl.con = qemu_console_lookup_by_index(0);
-
s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
#if GTK_CHECK_VERSION(3, 2, 0)
s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
@@ -1471,11 +1828,8 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
s->vbox = gtk_vbox_new(FALSE, 0);
#endif
s->notebook = gtk_notebook_new();
- s->drawing_area = gtk_drawing_area_new();
s->menu_bar = gtk_menu_bar_new();
- s->scale_x = 1.0;
- s->scale_y = 1.0;
s->free_scale = FALSE;
setlocale(LC_ALL, "");
@@ -1488,8 +1842,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
qemu_add_vm_change_state_handler(gd_change_runstate, s);
- gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA"));
-
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
if (filename) {
GError *error = NULL;
@@ -1506,18 +1858,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
gd_connect_signals(s);
- gtk_widget_add_events(s->drawing_area,
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |
- GDK_BUTTON_MOTION_MASK |
- GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK |
- GDK_SCROLL_MASK |
- GDK_KEY_PRESS_MASK);
- gtk_widget_set_double_buffered(s->drawing_area, FALSE);
- gtk_widget_set_can_focus(s->drawing_area, TRUE);
-
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
@@ -1530,6 +1870,21 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
gtk_widget_show_all(s->window);
+#ifdef VTE_RESIZE_HACK
+ {
+ VirtualConsole *cur = gd_vc_find_current(s);
+ int i;
+
+ for (i = 0; i < s->nb_vcs; i++) {
+ VirtualConsole *vc = &s->vc[i];
+ if (vc && vc->type == GD_VC_VTE && vc != cur) {
+ gtk_widget_hide(vc->vte.terminal);
+ }
+ }
+ gd_update_windowsize(cur);
+ }
+#endif
+
if (full_screen) {
gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
}
@@ -1537,7 +1892,12 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
}
- register_displaychangelistener(&s->dcl);
+ gd_set_keycode_type(s);
+}
- global_state = s;
+void early_gtk_display_init(void)
+{
+#if defined(CONFIG_VTE)
+ register_vc_handler(gd_vc_handler);
+#endif
}
diff --git a/ui/input-keymap.c b/ui/input-keymap.c
index 6da4495103..5d299353a8 100644
--- a/ui/input-keymap.c
+++ b/ui/input-keymap.c
@@ -13,6 +13,8 @@ static const int qcode_to_number[] = {
[Q_KEY_CODE_CTRL] = 0x1d,
[Q_KEY_CODE_CTRL_R] = 0x9d,
+ [Q_KEY_CODE_META_L] = 0xdb,
+ [Q_KEY_CODE_META_R] = 0xdc,
[Q_KEY_CODE_MENU] = 0xdd,
[Q_KEY_CODE_ESC] = 0x01,
@@ -129,7 +131,7 @@ static const int qcode_to_number[] = {
[Q_KEY_CODE_MAX] = 0,
};
-static int number_to_qcode[0xff];
+static int number_to_qcode[0x100];
int qemu_input_key_value_to_number(const KeyValue *value)
{
@@ -141,7 +143,7 @@ int qemu_input_key_value_to_number(const KeyValue *value)
}
}
-int qemu_input_key_value_to_qcode(const KeyValue *value)
+int qemu_input_key_number_to_qcode(uint8_t nr)
{
static int first = true;
@@ -155,11 +157,16 @@ int qemu_input_key_value_to_qcode(const KeyValue *value)
}
}
+ return number_to_qcode[nr];
+}
+
+int qemu_input_key_value_to_qcode(const KeyValue *value)
+{
if (value->kind == KEY_VALUE_KIND_QCODE) {
return value->qcode;
} else {
assert(value->kind == KEY_VALUE_KIND_NUMBER);
- return number_to_qcode[value->number];
+ return qemu_input_key_number_to_qcode(value->number);
}
}
diff --git a/ui/input.c b/ui/input.c
index fc91fba83c..14c9434f5e 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -1,3 +1,4 @@
+#include "hw/qdev.h"
#include "sysemu/sysemu.h"
#include "qapi-types.h"
#include "qmp-commands.h"
@@ -10,6 +11,7 @@ struct QemuInputHandlerState {
QemuInputHandler *handler;
int id;
int events;
+ QemuConsole *con;
QTAILQ_ENTRY(QemuInputHandlerState) node;
};
static QTAILQ_HEAD(, QemuInputHandlerState) handlers =
@@ -53,12 +55,46 @@ void qemu_input_handler_unregister(QemuInputHandlerState *s)
qemu_input_check_mode_change();
}
+void qemu_input_handler_bind(QemuInputHandlerState *s,
+ const char *device_id, int head,
+ Error **errp)
+{
+ DeviceState *dev;
+ QemuConsole *con;
+
+ dev = qdev_find_recursive(sysbus_get_default(), device_id);
+ if (dev == NULL) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device_id);
+ return;
+ }
+
+ con = qemu_console_lookup_by_device(dev, head);
+ if (con == NULL) {
+ error_setg(errp, "Device %s is not bound to a QemuConsole", device_id);
+ return;
+ }
+
+ s->con = con;
+}
+
static QemuInputHandlerState*
-qemu_input_find_handler(uint32_t mask)
+qemu_input_find_handler(uint32_t mask, QemuConsole *con)
{
QemuInputHandlerState *s;
QTAILQ_FOREACH(s, &handlers, node) {
+ if (s->con == NULL || s->con != con) {
+ continue;
+ }
+ if (mask & s->handler->mask) {
+ return s;
+ }
+ }
+
+ QTAILQ_FOREACH(s, &handlers, node) {
+ if (s->con != NULL) {
+ continue;
+ }
if (mask & s->handler->mask) {
return s;
}
@@ -94,7 +130,7 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt)
static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
{
const char *name;
- int idx = -1;
+ int qcode, idx = -1;
if (src) {
idx = qemu_console_get_index(src);
@@ -103,8 +139,10 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
case INPUT_EVENT_KIND_KEY:
switch (evt->key->key->kind) {
case KEY_VALUE_KIND_NUMBER:
+ qcode = qemu_input_key_number_to_qcode(evt->key->key->number);
+ name = QKeyCode_lookup[qcode];
trace_input_event_key_number(idx, evt->key->key->number,
- evt->key->down);
+ name, evt->key->down);
break;
case KEY_VALUE_KIND_QCODE:
name = QKeyCode_lookup[evt->key->key->qcode];
@@ -149,7 +187,7 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
}
/* send event */
- s = qemu_input_find_handler(1 << evt->kind);
+ s = qemu_input_find_handler(1 << evt->kind, src);
if (!s) {
return;
}
@@ -250,7 +288,8 @@ bool qemu_input_is_absolute(void)
{
QemuInputHandlerState *s;
- s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS);
+ s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS,
+ NULL);
return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
}
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 361de619fa..0e884f96fd 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -190,30 +190,33 @@ static void sdl_switch(DisplayChangeListener *dcl,
}
}
-static void reset_keys(void)
+static void reset_keys(struct sdl2_state *scon)
{
+ QemuConsole *con = scon ? scon->dcl.con : NULL;
int i;
for (i = 0; i < 256; i++) {
if (modifiers_state[i]) {
int qcode = sdl2_scancode_to_qcode[i];
- qemu_input_event_send_key_qcode(NULL, qcode, false);
+ qemu_input_event_send_key_qcode(con, qcode, false);
modifiers_state[i] = 0;
}
}
}
-static void sdl_process_key(SDL_KeyboardEvent *ev)
+static void sdl_process_key(struct sdl2_state *scon,
+ SDL_KeyboardEvent *ev)
{
int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
+ QemuConsole *con = scon ? scon->dcl.con : NULL;
switch (ev->keysym.scancode) {
#if 0
case SDL_SCANCODE_NUMLOCKCLEAR:
case SDL_SCANCODE_CAPSLOCK:
/* SDL does not send the key up event, so we generate it */
- qemu_input_event_send_key_qcode(NULL, qcode, true);
- qemu_input_event_send_key_qcode(NULL, qcode, false);
+ qemu_input_event_send_key_qcode(con, qcode, true);
+ qemu_input_event_send_key_qcode(con, qcode, false);
return;
#endif
case SDL_SCANCODE_LCTRL:
@@ -231,7 +234,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev)
}
/* fall though */
default:
- qemu_input_event_send_key_qcode(NULL, qcode,
+ qemu_input_event_send_key_qcode(con, qcode,
ev->type == SDL_KEYDOWN);
}
}
@@ -506,7 +509,7 @@ static void handle_keydown(SDL_Event *ev)
}
}
if (!gui_keysym) {
- sdl_process_key(&ev->key);
+ sdl_process_key(scon, &ev->key);
}
}
@@ -531,13 +534,13 @@ static void handle_keyup(SDL_Event *ev)
}
/* SDL does not send back all the modifiers key, so we must
* correct it. */
- reset_keys();
+ reset_keys(scon);
return;
}
gui_keysym = 0;
}
if (!gui_keysym) {
- sdl_process_key(&ev->key);
+ sdl_process_key(scon, &ev->key);
}
}
diff --git a/vl.c b/vl.c
index 709d8cda8d..99b6fc0050 100644
--- a/vl.c
+++ b/vl.c
@@ -965,7 +965,7 @@ static int parse_sandbox(QemuOpts *opts, void *opaque)
return 0;
}
-static void parse_name(QemuOpts *opts)
+static int parse_name(QemuOpts *opts, void *opaque)
{
const char *proc_name;
@@ -978,6 +978,8 @@ static void parse_name(QemuOpts *opts)
if (proc_name) {
os_set_proc_name(proc_name);
}
+
+ return 0;
}
bool usb_enabled(bool default_usb)
@@ -3796,7 +3798,6 @@ int main(int argc, char **argv, char **envp)
if (!opts) {
exit(1);
}
- parse_name(opts);
break;
case QEMU_OPTION_prom_env:
if (nb_prom_envs >= MAX_PROM_ENVS) {
@@ -3971,6 +3972,10 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
+ if (qemu_opts_foreach(qemu_find_opts("name"), parse_name, NULL, 1)) {
+ exit(1);
+ }
+
#ifndef _WIN32
if (qemu_opts_foreach(qemu_find_opts("add-fd"), parse_add_fd, NULL, 1)) {
exit(1);