summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile16
-rw-r--r--Makefile.objs9
-rw-r--r--QMP/qmp-events.txt18
-rw-r--r--QMP/qmp.py21
-rw-r--r--block.c326
-rw-r--r--block.h9
-rw-r--r--block/Makefile.objs1
-rw-r--r--block/commit.c11
-rw-r--r--block/mirror.c322
-rw-r--r--block/stream.c4
-rw-r--r--block_int.h26
-rw-r--r--blockdev-nbd.c119
-rw-r--r--blockdev.c182
-rw-r--r--blockjob.c36
-rw-r--r--blockjob.h41
-rwxr-xr-xconfigure26
-rw-r--r--console.h2
-rw-r--r--default-configs/sparc64-softmmu.mak1
-rw-r--r--error.c28
-rw-r--r--error.h9
-rw-r--r--hmp-commands.hx38
-rw-r--r--hmp.c50
-rw-r--r--hmp.h2
-rw-r--r--hw/ac97.c109
-rw-r--r--hw/es1370.c46
-rw-r--r--hw/i8254.c20
-rw-r--r--hw/m48t59.c24
-rw-r--r--hw/mc146818rtc.c19
-rw-r--r--hw/nseries.c3
-rw-r--r--hw/pc.c19
-rw-r--r--hw/pc_piix.c2
-rw-r--r--hw/pckbd.c48
-rw-r--r--hw/ppc/Makefile.objs1
-rw-r--r--hw/ppc/e500.c5
-rw-r--r--hw/ppc440_bamboo.c2
-rw-r--r--hw/ppc_newworld.c19
-rw-r--r--hw/ppc_oldworld.c2
-rw-r--r--hw/ppc_prep.c2
-rw-r--r--hw/ppce500_pci.c9
-rw-r--r--hw/pxa2xx.c4
-rw-r--r--hw/realview.c2
-rw-r--r--hw/rtl8139.c78
-rw-r--r--hw/serial.c30
-rw-r--r--hw/spapr.c16
-rw-r--r--hw/spapr.h8
-rw-r--r--hw/spapr_events.c321
-rw-r--r--hw/spapr_hcall.c29
-rw-r--r--hw/spapr_pci.c44
-rw-r--r--hw/spapr_pci.h2
-rw-r--r--hw/spapr_rtas.c9
-rw-r--r--hw/versatilepb.c2
-rw-r--r--hw/virtio-pci.c126
-rw-r--r--hw/vmport.c21
-rw-r--r--hw/xen_platform.c48
-rw-r--r--hw/xtensa_lx60.c30
-rw-r--r--hw/xtensa_sim.c27
-rw-r--r--migration-exec.c26
-rw-r--r--migration-fd.c27
-rw-r--r--migration-tcp.c19
-rw-r--r--migration-unix.c95
-rw-r--r--migration.c36
-rw-r--r--migration.h19
-rw-r--r--monitor.c143
-rw-r--r--monitor.h4
-rw-r--r--nbd.c39
-rw-r--r--osdep.c12
-rw-r--r--osdep.h2
-rw-r--r--qapi-schema.json225
-rw-r--r--qemu-char.c24
-rw-r--r--qemu-config.c26
-rw-r--r--qemu-img-cmds.hx4
-rw-r--r--qemu-img.c219
-rw-r--r--qemu-img.texi25
-rw-r--r--qemu-options.hx36
-rw-r--r--qemu-sockets.c424
-rw-r--r--qemu-timer.c4
-rw-r--r--qemu-tool.c6
-rw-r--r--qemu_socket.h19
-rw-r--r--qerror.h9
-rw-r--r--qga/channel-posix.c8
-rw-r--r--qmp-commands.hx69
-rw-r--r--qmp.c23
-rw-r--r--sysemu.h4
-rw-r--r--target-alpha/helper.h176
-rw-r--r--target-arm/arm-semi.c167
-rw-r--r--target-arm/helper.c5
-rw-r--r--target-arm/helper.h18
-rw-r--r--target-arm/neon_helper.c6
-rw-r--r--target-arm/op_helper.c2
-rw-r--r--target-arm/translate.c15
-rw-r--r--target-cris/helper.h18
-rw-r--r--target-i386/cpu.c1
-rw-r--r--target-i386/helper.h4
-rw-r--r--target-microblaze/helper.h6
-rw-r--r--target-mips/helper.h106
-rw-r--r--target-mips/translate.c96
-rw-r--r--target-ppc/cpu.h7
-rw-r--r--target-ppc/helper.h38
-rw-r--r--target-ppc/mmu_helper.c2
-rw-r--r--target-ppc/translate_init.c9
-rw-r--r--target-s390x/helper.h76
-rw-r--r--target-sh4/helper.h6
-rw-r--r--target-sparc/helper.h50
-rw-r--r--target-xtensa/helper.h16
-rw-r--r--tcg/README22
-rw-r--r--tcg/i386/tcg-target.c218
-rw-r--r--tcg/optimize.c3
-rw-r--r--tcg/tcg-op.h18
-rw-r--r--tcg/tcg-opc.h29
-rw-r--r--tcg/tcg.c449
-rw-r--r--tcg/tcg.h29
-rw-r--r--tests/Makefile10
-rwxr-xr-xtests/qemu-iotests/040106
-rw-r--r--tests/qemu-iotests/040.out4
-rwxr-xr-xtests/qemu-iotests/041615
-rw-r--r--tests/qemu-iotests/041.out5
-rwxr-xr-xtests/qemu-iotests/04278
-rw-r--r--tests/qemu-iotests/042.out15
-rwxr-xr-xtests/qemu-iotests/04395
-rw-r--r--tests/qemu-iotests/043.out66
-rw-r--r--tests/qemu-iotests/common.rc10
-rw-r--r--tests/qemu-iotests/group3
-rw-r--r--tests/qemu-iotests/iotests.py4
-rw-r--r--tests/tcg/hello-i386.c3
-rw-r--r--tests/tcg/test-i386.c2
-rw-r--r--trace-events8
-rw-r--r--ui/vnc.c93
-rw-r--r--vl.c151
128 files changed, 5063 insertions, 1768 deletions
diff --git a/Makefile b/Makefile
index 88285a474b..b522b10195 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,17 @@ ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
all:
include config-host.mak
+
+# Check that we're not trying to do an out-of-tree build from
+# a tree that's been used for an in-tree build.
+ifneq ($(realpath $(SRC_PATH)),$(realpath .))
+ifneq ($(wildcard $(SRC_PATH)/config-host.mak),)
+$(error This is an out of tree build but your source tree ($(SRC_PATH)) \
+seems to have been used for an in-tree build. You can fix this by running \
+"make distclean && rm -rf *-linux-user *-softmmu" in your source tree)
+endif
+endif
+
include $(SRC_PATH)/rules.mak
config-host.mak: $(SRC_PATH)/configure
@echo $@ is out-of-date, running configure
@@ -161,11 +172,10 @@ qemu-img.o: qemu-img-cmds.h
tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
qemu-timer-common.o main-loop.o notify.o \
- iohandler.o cutils.o iov.o async.o
+ iohandler.o cutils.o iov.o async.o error.o
tools-obj-$(CONFIG_POSIX) += compatfd.o
-qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) $(qapi-obj-y) \
- qapi-visit.o qapi-types.o
+qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y)
qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y)
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)
diff --git a/Makefile.objs b/Makefile.objs
index 74b35422ce..9eca179903 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -43,11 +43,12 @@ coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
block-obj-y = cutils.o iov.o cache-utils.o qemu-option.o module.o async.o
block-obj-y += nbd.o block.o blockjob.o aio.o aes.o qemu-config.o
-block-obj-y += qemu-progress.o qemu-sockets.o uri.o
+block-obj-y += qemu-progress.o qemu-sockets.o uri.o notify.o
block-obj-y += $(coroutine-obj-y) $(qobject-obj-y) $(version-obj-y)
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block-obj-y += block/
+block-obj-y += $(qapi-obj-y) qapi-types.o qapi-visit.o
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
@@ -60,7 +61,7 @@ endif
# suppress *all* target specific code in case of system emulation, i.e. a
# single QEMU executable should support all CPUs and machines.
-common-obj-y = $(block-obj-y) blockdev.o block/
+common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
common-obj-y += net.o net/
common-obj-y += qom/
common-obj-y += readline.o console.o cursor.o
@@ -93,7 +94,7 @@ common-obj-y += bt-host.o bt-vhci.o
common-obj-y += dma-helpers.o
common-obj-y += iov.o acl.o
common-obj-$(CONFIG_POSIX) += compatfd.o
-common-obj-y += notify.o event_notifier.o
+common-obj-y += event_notifier.o
common-obj-y += qemu-timer.o qemu-timer-common.o
common-obj-y += qtest.o
common-obj-y += vl.o
@@ -239,9 +240,9 @@ QEMU_CFLAGS+=$(GLIB_CFLAGS)
nested-vars += \
qga-obj-y \
- block-obj-y \
qom-obj-y \
qapi-obj-y \
+ block-obj-y \
user-obj-y \
common-obj-y \
extra-obj-y
diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt
index 987c5756b3..b2698e4153 100644
--- a/QMP/qmp-events.txt
+++ b/QMP/qmp-events.txt
@@ -118,6 +118,24 @@ Example:
"action": "stop" },
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+BLOCK_JOB_READY
+---------------
+
+Emitted when a block job is ready to complete.
+
+Data:
+
+- "device": device name (json-string)
+
+Example:
+
+{ "event": "BLOCK_JOB_READY",
+ "data": { "device": "ide0-hd1" },
+ "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+
+Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR
+event.
+
DEVICE_TRAY_MOVED
-----------------
diff --git a/QMP/qmp.py b/QMP/qmp.py
index 33c7d36d9b..c551df1ed7 100644
--- a/QMP/qmp.py
+++ b/QMP/qmp.py
@@ -96,6 +96,7 @@ class QEMUMonitorProtocol:
@raise QMPCapabilitiesError if fails to negotiate capabilities
"""
self.__sock, _ = self.__sock.accept()
+ self.__sockfile = self.__sock.makefile()
return self.__negotiate_capabilities()
def cmd_obj(self, qmp_cmd):
@@ -135,6 +136,26 @@ class QEMUMonitorProtocol:
raise Exception(ret['error']['desc'])
return ret['return']
+ def pull_event(self, wait=False):
+ """
+ Get and delete the first available QMP event.
+
+ @param wait: block until an event is available (bool)
+ """
+ self.__sock.setblocking(0)
+ try:
+ self.__json_read()
+ except socket.error, err:
+ if err[0] == errno.EAGAIN:
+ # No data available
+ pass
+ self.__sock.setblocking(1)
+ if not self.__events and wait:
+ self.__json_read(only_event=True)
+ event = self.__events[0]
+ del self.__events[0]
+ return event
+
def get_events(self, wait=False):
"""
Get a list of available QMP events.
diff --git a/block.c b/block.c
index e95f613aa4..da1fdca0e0 100644
--- a/block.c
+++ b/block.c
@@ -30,6 +30,7 @@
#include "module.h"
#include "qjson.h"
#include "sysemu.h"
+#include "notify.h"
#include "qemu-coroutine.h"
#include "qmp-commands.h"
#include "qemu-timer.h"
@@ -312,9 +313,16 @@ BlockDriverState *bdrv_new(const char *device_name)
QTAILQ_INSERT_TAIL(&bdrv_states, bs, list);
}
bdrv_iostatus_disable(bs);
+ notifier_list_init(&bs->close_notifiers);
+
return bs;
}
+void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify)
+{
+ notifier_list_add(&bs->close_notifiers, notify);
+}
+
BlockDriver *bdrv_find_format(const char *format_name)
{
BlockDriver *drv1;
@@ -379,7 +387,8 @@ int bdrv_create(BlockDriver *drv, const char* filename,
};
if (!drv->bdrv_create) {
- return -ENOTSUP;
+ ret = -ENOTSUP;
+ goto out;
}
if (qemu_in_coroutine()) {
@@ -394,8 +403,9 @@ int bdrv_create(BlockDriver *drv, const char* filename,
}
ret = cco.ret;
- g_free(cco.filename);
+out:
+ g_free(cco.filename);
return ret;
}
@@ -734,6 +744,42 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
return 0;
}
+int bdrv_open_backing_file(BlockDriverState *bs)
+{
+ char backing_filename[PATH_MAX];
+ int back_flags, ret;
+ BlockDriver *back_drv = NULL;
+
+ if (bs->backing_hd != NULL) {
+ return 0;
+ }
+
+ bs->open_flags &= ~BDRV_O_NO_BACKING;
+ if (bs->backing_file[0] == '\0') {
+ return 0;
+ }
+
+ bs->backing_hd = bdrv_new("");
+ bdrv_get_full_backing_filename(bs, backing_filename,
+ sizeof(backing_filename));
+
+ if (bs->backing_format[0] != '\0') {
+ back_drv = bdrv_find_format(bs->backing_format);
+ }
+
+ /* backing files always opened read-only */
+ back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT);
+
+ ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv);
+ if (ret < 0) {
+ bdrv_delete(bs->backing_hd);
+ bs->backing_hd = NULL;
+ bs->open_flags |= BDRV_O_NO_BACKING;
+ return ret;
+ }
+ return 0;
+}
+
/*
* Opens a disk image (raw, qcow2, vmdk, ...)
*/
@@ -821,24 +867,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
}
/* If there is a backing file, use it */
- if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') {
- char backing_filename[PATH_MAX];
- int back_flags;
- BlockDriver *back_drv = NULL;
-
- bs->backing_hd = bdrv_new("");
- bdrv_get_full_backing_filename(bs, backing_filename,
- sizeof(backing_filename));
-
- if (bs->backing_format[0] != '\0') {
- back_drv = bdrv_find_format(bs->backing_format);
- }
-
- /* backing files always opened read-only */
- back_flags =
- flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
-
- ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv);
+ if ((flags & BDRV_O_NO_BACKING) == 0) {
+ ret = bdrv_open_backing_file(bs);
if (ret < 0) {
bdrv_close(bs);
return ret;
@@ -1098,12 +1128,13 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
void bdrv_close(BlockDriverState *bs)
{
bdrv_flush(bs);
- if (bs->drv) {
- if (bs->job) {
- block_job_cancel_sync(bs->job);
- }
- bdrv_drain_all();
+ if (bs->job) {
+ block_job_cancel_sync(bs->job);
+ }
+ bdrv_drain_all();
+ notifier_list_notify(&bs->close_notifiers, bs);
+ if (bs->drv) {
if (bs == bs_snapshots) {
bs_snapshots = NULL;
}
@@ -2369,7 +2400,7 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
}
if (bs->dirty_bitmap) {
- set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
+ bdrv_set_dirty(bs, sector_num, nb_sectors);
}
if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
@@ -2797,76 +2828,82 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
return 0;
}
-BlockInfoList *qmp_query_block(Error **errp)
+BlockInfo *bdrv_query_info(BlockDriverState *bs)
{
- BlockInfoList *head = NULL, *cur_item = NULL;
- BlockDriverState *bs;
+ BlockInfo *info = g_malloc0(sizeof(*info));
+ info->device = g_strdup(bs->device_name);
+ info->type = g_strdup("unknown");
+ info->locked = bdrv_dev_is_medium_locked(bs);
+ info->removable = bdrv_dev_has_removable_media(bs);
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
- BlockInfoList *info = g_malloc0(sizeof(*info));
+ if (bdrv_dev_has_removable_media(bs)) {
+ info->has_tray_open = true;
+ info->tray_open = bdrv_dev_is_tray_open(bs);
+ }
+
+ if (bdrv_iostatus_is_enabled(bs)) {
+ info->has_io_status = true;
+ info->io_status = bs->iostatus;
+ }
- info->value = g_malloc0(sizeof(*info->value));
- info->value->device = g_strdup(bs->device_name);
- info->value->type = g_strdup("unknown");
- info->value->locked = bdrv_dev_is_medium_locked(bs);
- info->value->removable = bdrv_dev_has_removable_media(bs);
+ if (bs->dirty_bitmap) {
+ info->has_dirty = true;
+ info->dirty = g_malloc0(sizeof(*info->dirty));
+ info->dirty->count = bdrv_get_dirty_count(bs) *
+ BDRV_SECTORS_PER_DIRTY_CHUNK * BDRV_SECTOR_SIZE;
+ }
- if (bdrv_dev_has_removable_media(bs)) {
- info->value->has_tray_open = true;
- info->value->tray_open = bdrv_dev_is_tray_open(bs);
+ if (bs->drv) {
+ info->has_inserted = true;
+ info->inserted = g_malloc0(sizeof(*info->inserted));
+ info->inserted->file = g_strdup(bs->filename);
+ info->inserted->ro = bs->read_only;
+ info->inserted->drv = g_strdup(bs->drv->format_name);
+ info->inserted->encrypted = bs->encrypted;
+ info->inserted->encryption_key_missing = bdrv_key_required(bs);
+
+ if (bs->backing_file[0]) {
+ info->inserted->has_backing_file = true;
+ info->inserted->backing_file = g_strdup(bs->backing_file);
}
- if (bdrv_iostatus_is_enabled(bs)) {
- info->value->has_io_status = true;
- info->value->io_status = bs->iostatus;
+ info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs);
+
+ if (bs->io_limits_enabled) {
+ info->inserted->bps =
+ bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL];
+ info->inserted->bps_rd =
+ bs->io_limits.bps[BLOCK_IO_LIMIT_READ];
+ info->inserted->bps_wr =
+ bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE];
+ info->inserted->iops =
+ bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL];
+ info->inserted->iops_rd =
+ bs->io_limits.iops[BLOCK_IO_LIMIT_READ];
+ info->inserted->iops_wr =
+ bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE];
}
+ }
+ return info;
+}
- if (bs->drv) {
- info->value->has_inserted = true;
- info->value->inserted = g_malloc0(sizeof(*info->value->inserted));
- info->value->inserted->file = g_strdup(bs->filename);
- info->value->inserted->ro = bs->read_only;
- info->value->inserted->drv = g_strdup(bs->drv->format_name);
- info->value->inserted->encrypted = bs->encrypted;
- info->value->inserted->encryption_key_missing = bdrv_key_required(bs);
- if (bs->backing_file[0]) {
- info->value->inserted->has_backing_file = true;
- info->value->inserted->backing_file = g_strdup(bs->backing_file);
- }
+BlockInfoList *qmp_query_block(Error **errp)
+{
+ BlockInfoList *head = NULL, **p_next = &head;
+ BlockDriverState *bs;
- info->value->inserted->backing_file_depth =
- bdrv_get_backing_file_depth(bs);
-
- if (bs->io_limits_enabled) {
- info->value->inserted->bps =
- bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL];
- info->value->inserted->bps_rd =
- bs->io_limits.bps[BLOCK_IO_LIMIT_READ];
- info->value->inserted->bps_wr =
- bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE];
- info->value->inserted->iops =
- bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL];
- info->value->inserted->iops_rd =
- bs->io_limits.iops[BLOCK_IO_LIMIT_READ];
- info->value->inserted->iops_wr =
- bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE];
- }
- }
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ BlockInfoList *info = g_malloc0(sizeof(*info));
+ info->value = bdrv_query_info(bs);
- /* XXX: waiting for the qapi to support GSList */
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
+ *p_next = info;
+ p_next = &info->next;
}
return head;
}
-/* Consider exposing this as a full fledged QMP command */
-static BlockStats *qmp_query_blockstat(const BlockDriverState *bs, Error **errp)
+BlockStats *bdrv_query_stats(const BlockDriverState *bs)
{
BlockStats *s;
@@ -2890,7 +2927,7 @@ static BlockStats *qmp_query_blockstat(const BlockDriverState *bs, Error **errp)
if (bs->file) {
s->has_parent = true;
- s->parent = qmp_query_blockstat(bs->file, NULL);
+ s->parent = bdrv_query_stats(bs->file);
}
return s;
@@ -2898,20 +2935,15 @@ static BlockStats *qmp_query_blockstat(const BlockDriverState *bs, Error **errp)
BlockStatsList *qmp_query_blockstats(Error **errp)
{
- BlockStatsList *head = NULL, *cur_item = NULL;
+ BlockStatsList *head = NULL, **p_next = &head;
BlockDriverState *bs;
QTAILQ_FOREACH(bs, &bdrv_states, list) {
BlockStatsList *info = g_malloc0(sizeof(*info));
- info->value = qmp_query_blockstat(bs, NULL);
+ info->value = bdrv_query_stats(bs);
- /* XXX: waiting for the qapi to support GSList */
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
+ *p_next = info;
+ p_next = &info->next;
}
return head;
@@ -2944,9 +2976,7 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
if (bdrv_check_request(bs, sector_num, nb_sectors))
return -EIO;
- if (bs->dirty_bitmap) {
- set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
- }
+ assert(!bs->dirty_bitmap);
return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
}
@@ -3123,22 +3153,70 @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs,
return -ENOTSUP;
}
+/* backing_file can either be relative, or absolute, or a protocol. If it is
+ * relative, it must be relative to the chain. So, passing in bs->filename
+ * from a BDS as backing_file should not be done, as that may be relative to
+ * the CWD rather than the chain. */
BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
const char *backing_file)
{
- if (!bs->drv) {
+ char *filename_full = NULL;
+ char *backing_file_full = NULL;
+ char *filename_tmp = NULL;
+ int is_protocol = 0;
+ BlockDriverState *curr_bs = NULL;
+ BlockDriverState *retval = NULL;
+
+ if (!bs || !bs->drv || !backing_file) {
return NULL;
}
- if (bs->backing_hd) {
- if (strcmp(bs->backing_file, backing_file) == 0) {
- return bs->backing_hd;
+ filename_full = g_malloc(PATH_MAX);
+ backing_file_full = g_malloc(PATH_MAX);
+ filename_tmp = g_malloc(PATH_MAX);
+
+ is_protocol = path_has_protocol(backing_file);
+
+ for (curr_bs = bs; curr_bs->backing_hd; curr_bs = curr_bs->backing_hd) {
+
+ /* If either of the filename paths is actually a protocol, then
+ * compare unmodified paths; otherwise make paths relative */
+ if (is_protocol || path_has_protocol(curr_bs->backing_file)) {
+ if (strcmp(backing_file, curr_bs->backing_file) == 0) {
+ retval = curr_bs->backing_hd;
+ break;
+ }
} else {
- return bdrv_find_backing_image(bs->backing_hd, backing_file);
+ /* If not an absolute filename path, make it relative to the current
+ * image's filename path */
+ path_combine(filename_tmp, PATH_MAX, curr_bs->filename,
+ backing_file);
+
+ /* We are going to compare absolute pathnames */
+ if (!realpath(filename_tmp, filename_full)) {
+ continue;
+ }
+
+ /* We need to make sure the backing filename we are comparing against
+ * is relative to the current image filename (or absolute) */
+ path_combine(filename_tmp, PATH_MAX, curr_bs->filename,
+ curr_bs->backing_file);
+
+ if (!realpath(filename_tmp, backing_file_full)) {
+ continue;
+ }
+
+ if (strcmp(backing_file_full, filename_full) == 0) {
+ retval = curr_bs->backing_hd;
+ break;
+ }
}
}
- return NULL;
+ g_free(filename_full);
+ g_free(backing_file_full);
+ g_free(filename_tmp);
+ return retval;
}
int bdrv_get_backing_file_depth(BlockDriverState *bs)
@@ -4205,13 +4283,54 @@ int bdrv_get_dirty(BlockDriverState *bs, int64_t sector)
if (bs->dirty_bitmap &&
(sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) {
- return !!(bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
- (1UL << (chunk % (sizeof(unsigned long) * 8))));
+ return !!(bs->dirty_bitmap[chunk / BITS_PER_LONG] &
+ (1UL << (chunk % BITS_PER_LONG)));
} else {
return 0;
}
}
+int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector)
+{
+ int64_t chunk;
+ int bit, elem;
+
+ /* Avoid an infinite loop. */
+ assert(bs->dirty_count > 0);
+
+ sector = (sector | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1;
+ chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
+
+ QEMU_BUILD_BUG_ON(sizeof(bs->dirty_bitmap[0]) * 8 != BITS_PER_LONG);
+ elem = chunk / BITS_PER_LONG;
+ bit = chunk % BITS_PER_LONG;
+ for (;;) {
+ if (sector >= bs->total_sectors) {
+ sector = 0;
+ bit = elem = 0;
+ }
+ if (bit == 0 && bs->dirty_bitmap[elem] == 0) {
+ sector += BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG;
+ elem++;
+ } else {
+ if (bs->dirty_bitmap[elem] & (1UL << bit)) {
+ return sector;
+ }
+ sector += BDRV_SECTORS_PER_DIRTY_CHUNK;
+ if (++bit == BITS_PER_LONG) {
+ bit = 0;
+ elem++;
+ }
+ }
+ }
+}
+
+void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
+ int nr_sectors)
+{
+ set_dirty_bitmap(bs, cur_sector, nr_sectors, 1);
+}
+
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
int nr_sectors)
{
@@ -4259,6 +4378,9 @@ void bdrv_iostatus_reset(BlockDriverState *bs)
{
if (bdrv_iostatus_is_enabled(bs)) {
bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
+ if (bs->job) {
+ block_job_iostatus_reset(bs->job);
+ }
}
}
diff --git a/block.h b/block.h
index e2d89d7bc1..722c620590 100644
--- a/block.h
+++ b/block.h
@@ -133,6 +133,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
void bdrv_delete(BlockDriverState *bs);
int bdrv_parse_cache_flags(const char *mode, int *flags);
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
+int bdrv_open_backing_file(BlockDriverState *bs);
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
BlockDriver *drv);
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
@@ -144,6 +145,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
void bdrv_reopen_commit(BDRVReopenState *reopen_state);
void bdrv_reopen_abort(BDRVReopenState *reopen_state);
void bdrv_close(BlockDriverState *bs);
+void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify);
int bdrv_attach_dev(BlockDriverState *bs, void *dev);
void bdrv_attach_dev_nofail(BlockDriverState *bs, void *dev);
void bdrv_detach_dev(BlockDriverState *bs, void *dev);
@@ -313,6 +315,8 @@ void bdrv_get_backing_filename(BlockDriverState *bs,
char *filename, int filename_size);
void bdrv_get_full_backing_filename(BlockDriverState *bs,
char *dest, size_t sz);
+BlockInfo *bdrv_query_info(BlockDriverState *s);
+BlockStats *bdrv_query_stats(const BlockDriverState *bs);
int bdrv_can_snapshot(BlockDriverState *bs);
int bdrv_is_snapshot(BlockDriverState *bs);
BlockDriverState *bdrv_snapshots(void);
@@ -350,8 +354,9 @@ void *qemu_blockalign(BlockDriverState *bs, size_t size);
void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable);
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector);
-void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
- int nr_sectors);
+void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
+void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
+int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector);
int64_t bdrv_get_dirty_count(BlockDriverState *bs);
void bdrv_enable_copy_on_read(BlockDriverState *bs);
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 554f429d05..806e526eb4 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -12,3 +12,4 @@ block-obj-$(CONFIG_GLUSTERFS) += gluster.o
common-obj-y += stream.o
common-obj-y += commit.o
+common-obj-y += mirror.o
diff --git a/block/commit.c b/block/commit.c
index 733c91403c..fae79582d4 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -160,7 +160,7 @@ exit_restore_reopen:
bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
}
- block_job_complete(&s->common, ret);
+ block_job_completed(&s->common, ret);
}
static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
@@ -211,15 +211,6 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
return;
}
- /* top and base may be valid, but let's make sure that base is reachable
- * from top */
- if (bdrv_find_backing_image(top, base->filename) != base) {
- error_setg(errp,
- "Base (%s) is not reachable from top (%s)",
- base->filename, top->filename);
- return;
- }
-
overlay_bs = bdrv_find_overlay(bs, top);
if (overlay_bs == NULL) {
diff --git a/block/mirror.c b/block/mirror.c
new file mode 100644
index 0000000000..d6618a4b34
--- /dev/null
+++ b/block/mirror.c
@@ -0,0 +1,322 @@
+/*
+ * Image mirroring
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ * Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "trace.h"
+#include "blockjob.h"
+#include "block_int.h"
+#include "qemu/ratelimit.h"
+
+enum {
+ /*
+ * Size of data buffer for populating the image file. This should be large
+ * enough to process multiple clusters in a single call, so that populating
+ * contiguous regions of the image is efficient.
+ */
+ BLOCK_SIZE = 512 * BDRV_SECTORS_PER_DIRTY_CHUNK, /* in bytes */
+};
+
+#define SLICE_TIME 100000000ULL /* ns */
+
+typedef struct MirrorBlockJob {
+ BlockJob common;
+ RateLimit limit;
+ BlockDriverState *target;
+ MirrorSyncMode mode;
+ BlockdevOnError on_source_error, on_target_error;
+ bool synced;
+ bool should_complete;
+ int64_t sector_num;
+ uint8_t *buf;
+} MirrorBlockJob;
+
+static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
+ int error)
+{
+ s->synced = false;
+ if (read) {
+ return block_job_error_action(&s->common, s->common.bs,
+ s->on_source_error, true, error);
+ } else {
+ return block_job_error_action(&s->common, s->target,
+ s->on_target_error, false, error);
+ }
+}
+
+static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
+ BlockErrorAction *p_action)
+{
+ BlockDriverState *source = s->common.bs;
+ BlockDriverState *target = s->target;
+ QEMUIOVector qiov;
+ int ret, nb_sectors;
+ int64_t end;
+ struct iovec iov;
+
+ end = s->common.len >> BDRV_SECTOR_BITS;
+ s->sector_num = bdrv_get_next_dirty(source, s->sector_num);
+ nb_sectors = MIN(BDRV_SECTORS_PER_DIRTY_CHUNK, end - s->sector_num);
+ bdrv_reset_dirty(source, s->sector_num, nb_sectors);
+
+ /* Copy the dirty cluster. */
+ iov.iov_base = s->buf;
+ iov.iov_len = nb_sectors * 512;
+ qemu_iovec_init_external(&qiov, &iov, 1);
+
+ trace_mirror_one_iteration(s, s->sector_num, nb_sectors);
+ ret = bdrv_co_readv(source, s->sector_num, nb_sectors, &qiov);
+ if (ret < 0) {
+ *p_action = mirror_error_action(s, true, -ret);
+ goto fail;
+ }
+ ret = bdrv_co_writev(target, s->sector_num, nb_sectors, &qiov);
+ if (ret < 0) {
+ *p_action = mirror_error_action(s, false, -ret);
+ s->synced = false;
+ goto fail;
+ }
+ return 0;
+
+fail:
+ /* Try again later. */
+ bdrv_set_dirty(source, s->sector_num, nb_sectors);
+ return ret;
+}
+
+static void coroutine_fn mirror_run(void *opaque)
+{
+ MirrorBlockJob *s = opaque;
+ BlockDriverState *bs = s->common.bs;
+ int64_t sector_num, end;
+ int ret = 0;
+ int n;
+
+ if (block_job_is_cancelled(&s->common)) {
+ goto immediate_exit;
+ }
+
+ s->common.len = bdrv_getlength(bs);
+ if (s->common.len < 0) {
+ block_job_completed(&s->common, s->common.len);
+ return;
+ }
+
+ end = s->common.len >> BDRV_SECTOR_BITS;
+ s->buf = qemu_blockalign(bs, BLOCK_SIZE);
+
+ if (s->mode != MIRROR_SYNC_MODE_NONE) {
+ /* First part, loop on the sectors and initialize the dirty bitmap. */
+ BlockDriverState *base;
+ base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd;
+ for (sector_num = 0; sector_num < end; ) {
+ int64_t next = (sector_num | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1;
+ ret = bdrv_co_is_allocated_above(bs, base,
+ sector_num, next - sector_num, &n);
+
+ if (ret < 0) {
+ goto immediate_exit;
+ }
+
+ assert(n > 0);
+ if (ret == 1) {
+ bdrv_set_dirty(bs, sector_num, n);
+ sector_num = next;
+ } else {
+ sector_num += n;
+ }
+ }
+ }
+
+ s->sector_num = -1;
+ for (;;) {
+ uint64_t delay_ns;
+ int64_t cnt;
+ bool should_complete;
+
+ cnt = bdrv_get_dirty_count(bs);
+ if (cnt != 0) {
+ BlockErrorAction action = BDRV_ACTION_REPORT;
+ ret = mirror_iteration(s, &action);
+ if (ret < 0 && action == BDRV_ACTION_REPORT) {
+ goto immediate_exit;
+ }
+ cnt = bdrv_get_dirty_count(bs);
+ }
+
+ should_complete = false;
+ if (cnt == 0) {
+ trace_mirror_before_flush(s);
+ ret = bdrv_flush(s->target);
+ if (ret < 0) {
+ if (mirror_error_action(s, false, -ret) == BDRV_ACTION_REPORT) {
+ goto immediate_exit;
+ }
+ } else {
+ /* We're out of the streaming phase. From now on, if the job
+ * is cancelled we will actually complete all pending I/O and
+ * report completion. This way, block-job-cancel will leave
+ * the target in a consistent state.
+ */
+ s->common.offset = end * BDRV_SECTOR_SIZE;
+ if (!s->synced) {
+ block_job_ready(&s->common);
+ s->synced = true;
+ }
+
+ should_complete = s->should_complete ||
+ block_job_is_cancelled(&s->common);
+ cnt = bdrv_get_dirty_count(bs);
+ }
+ }
+
+ if (cnt == 0 && should_complete) {
+ /* The dirty bitmap is not updated while operations are pending.
+ * If we're about to exit, wait for pending operations before
+ * calling bdrv_get_dirty_count(bs), or we may exit while the
+ * source has dirty data to copy!
+ *
+ * Note that I/O can be submitted by the guest while
+ * mirror_populate runs.
+ */
+ trace_mirror_before_drain(s, cnt);
+ bdrv_drain_all();
+ cnt = bdrv_get_dirty_count(bs);
+ }
+
+ ret = 0;
+ trace_mirror_before_sleep(s, cnt, s->synced);
+ if (!s->synced) {
+ /* Publish progress */
+ s->common.offset = end * BDRV_SECTOR_SIZE - cnt * BLOCK_SIZE;
+
+ if (s->common.speed) {
+ delay_ns = ratelimit_calculate_delay(&s->limit, BDRV_SECTORS_PER_DIRTY_CHUNK);
+ } else {
+ delay_ns = 0;
+ }
+
+ /* Note that even when no rate limit is applied we need to yield
+ * with no pending I/O here so that qemu_aio_flush() returns.
+ */
+ block_job_sleep_ns(&s->common, rt_clock, delay_ns);
+ if (block_job_is_cancelled(&s->common)) {
+ break;
+ }
+ } else if (!should_complete) {
+ delay_ns = (cnt == 0 ? SLICE_TIME : 0);
+ block_job_sleep_ns(&s->common, rt_clock, delay_ns);
+ } else if (cnt == 0) {
+ /* The two disks are in sync. Exit and report successful
+ * completion.
+ */
+ assert(QLIST_EMPTY(&bs->tracked_requests));
+ s->common.cancelled = false;
+ break;
+ }
+ }
+
+immediate_exit:
+ g_free(s->buf);
+ bdrv_set_dirty_tracking(bs, false);
+ bdrv_iostatus_disable(s->target);
+ if (s->should_complete && ret == 0) {
+ if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) {
+ bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL);
+ }
+ bdrv_swap(s->target, s->common.bs);
+ }
+ bdrv_close(s->target);
+ bdrv_delete(s->target);
+ block_job_completed(&s->common, ret);
+}
+
+static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
+{
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
+
+ if (speed < 0) {
+ error_set(errp, QERR_INVALID_PARAMETER, "speed");
+ return;
+ }
+ ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
+}
+
+static void mirror_iostatus_reset(BlockJob *job)
+{
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
+
+ bdrv_iostatus_reset(s->target);
+}
+
+static void mirror_complete(BlockJob *job, Error **errp)
+{
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
+ int ret;
+
+ ret = bdrv_open_backing_file(s->target);
+ if (ret < 0) {
+ char backing_filename[PATH_MAX];
+ bdrv_get_full_backing_filename(s->target, backing_filename,
+ sizeof(backing_filename));
+ error_set(errp, QERR_OPEN_FILE_FAILED, backing_filename);
+ return;
+ }
+ if (!s->synced) {
+ error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
+ return;
+ }
+
+ s->should_complete = true;
+ block_job_resume(job);
+}
+
+static BlockJobType mirror_job_type = {
+ .instance_size = sizeof(MirrorBlockJob),
+ .job_type = "mirror",
+ .set_speed = mirror_set_speed,
+ .iostatus_reset= mirror_iostatus_reset,
+ .complete = mirror_complete,
+};
+
+void mirror_start(BlockDriverState *bs, BlockDriverState *target,
+ int64_t speed, MirrorSyncMode mode,
+ BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, Error **errp)
+{
+ MirrorBlockJob *s;
+
+ if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
+ on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
+ !bdrv_iostatus_is_enabled(bs)) {
+ error_set(errp, QERR_INVALID_PARAMETER, "on-source-error");
+ return;
+ }
+
+ s = block_job_create(&mirror_job_type, bs, speed, cb, opaque, errp);
+ if (!s) {
+ return;
+ }
+
+ s->on_source_error = on_source_error;
+ s->on_target_error = on_target_error;
+ s->target = target;
+ s->mode = mode;
+ bdrv_set_dirty_tracking(bs, true);
+ bdrv_set_enable_write_cache(s->target, true);
+ bdrv_set_on_error(s->target, on_target_error, on_target_error);
+ bdrv_iostatus_enable(s->target);
+ s->common.co = qemu_coroutine_create(mirror_run);
+ trace_mirror_start(bs, s, s->common.co, opaque);
+ qemu_coroutine_enter(s->common.co, s);
+}
diff --git a/block/stream.c b/block/stream.c
index 792665276e..0c0fc7a13b 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -86,7 +86,7 @@ static void coroutine_fn stream_run(void *opaque)
s->common.len = bdrv_getlength(bs);
if (s->common.len < 0) {
- block_job_complete(&s->common, s->common.len);
+ block_job_completed(&s->common, s->common.len);
return;
}
@@ -184,7 +184,7 @@ wait:
}
qemu_vfree(buf);
- block_job_complete(&s->common, ret);
+ block_job_completed(&s->common, ret);
}
static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
diff --git a/block_int.h b/block_int.h
index f4bae04401..9deedb811a 100644
--- a/block_int.h
+++ b/block_int.h
@@ -233,6 +233,8 @@ struct BlockDriverState {
BlockDriverState *backing_hd;
BlockDriverState *file;
+ NotifierList close_notifiers;
+
/* number of in-flight copy-on-read requests */
unsigned int copy_on_read_in_flight;
@@ -331,4 +333,28 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
void *opaque, Error **errp);
+/*
+ * mirror_start:
+ * @bs: Block device to operate on.
+ * @target: Block device to write to.
+ * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
+ * @mode: Whether to collapse all images in the chain to the target.
+ * @on_source_error: The action to take upon error reading from the source.
+ * @on_target_error: The action to take upon error writing to the target.
+ * @cb: Completion function for the job.
+ * @opaque: Opaque pointer value passed to @cb.
+ * @errp: Error object.
+ *
+ * Start a mirroring operation on @bs. Clusters that are allocated
+ * in @bs will be written to @bs until the job is cancelled or
+ * manually completed. At the end of a successful mirroring job,
+ * @bs will be switched to read from @target.
+ */
+void mirror_start(BlockDriverState *bs, BlockDriverState *target,
+ int64_t speed, MirrorSyncMode mode,
+ BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, Error **errp);
+
#endif /* BLOCK_INT_H */
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
new file mode 100644
index 0000000000..8031813071
--- /dev/null
+++ b/blockdev-nbd.c
@@ -0,0 +1,119 @@
+/*
+ * Serving QEMU block devices via NBD
+ *
+ * Copyright (c) 2012 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * 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 "blockdev.h"
+#include "hw/block-common.h"
+#include "monitor.h"
+#include "qerror.h"
+#include "sysemu.h"
+#include "qmp-commands.h"
+#include "trace.h"
+#include "nbd.h"
+#include "qemu_socket.h"
+
+static int server_fd = -1;
+
+static void nbd_accept(void *opaque)
+{
+ struct sockaddr_in addr;
+ 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);
+ }
+}
+
+void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
+{
+ if (server_fd != -1) {
+ error_setg(errp, "NBD server already running");
+ return;
+ }
+
+ server_fd = socket_listen(addr, errp);
+ if (server_fd != -1) {
+ qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL);
+ }
+}
+
+/* Hook into the BlockDriverState notifiers to close the export when
+ * the file is closed.
+ */
+typedef struct NBDCloseNotifier {
+ Notifier n;
+ NBDExport *exp;
+ QTAILQ_ENTRY(NBDCloseNotifier) next;
+} NBDCloseNotifier;
+
+static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers =
+ QTAILQ_HEAD_INITIALIZER(close_notifiers);
+
+static void nbd_close_notifier(Notifier *n, void *data)
+{
+ NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n);
+
+ notifier_remove(&cn->n);
+ QTAILQ_REMOVE(&close_notifiers, cn, next);
+
+ nbd_export_close(cn->exp);
+ nbd_export_put(cn->exp);
+ g_free(cn);
+}
+
+static void nbd_server_put_ref(NBDExport *exp)
+{
+ BlockDriverState *bs = nbd_export_get_blockdev(exp);
+ drive_put_ref(drive_get_by_blockdev(bs));
+}
+
+void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
+ Error **errp)
+{
+ BlockDriverState *bs;
+ NBDExport *exp;
+ NBDCloseNotifier *n;
+
+ if (nbd_export_find(device)) {
+ error_setg(errp, "NBD server already exporting device '%s'", device);
+ return;
+ }
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
+ }
+
+ exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
+ nbd_server_put_ref);
+
+ nbd_export_set_name(exp, device);
+ drive_get_ref(drive_get_by_blockdev(bs));
+
+ n = g_malloc0(sizeof(NBDCloseNotifier));
+ n->n.notify = nbd_close_notifier;
+ n->exp = exp;
+ bdrv_add_close_notifier(bs, &n->n);
+ QTAILQ_INSERT_TAIL(&close_notifiers, n, next);
+}
+
+void qmp_nbd_server_stop(Error **errp)
+{
+ while (!QTAILQ_EMPTY(&close_notifiers)) {
+ NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers);
+ nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
+ }
+
+ qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
+ close(server_fd);
+ server_fd = -1;
+}
diff --git a/blockdev.c b/blockdev.c
index 99828ad2bd..a068a4b669 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1056,20 +1056,6 @@ void qmp_block_resize(const char *device, int64_t size, Error **errp)
}
}
-static QObject *qobject_from_block_job(BlockJob *job)
-{
- return qobject_from_jsonf("{ 'type': %s,"
- "'device': %s,"
- "'len': %" PRId64 ","
- "'offset': %" PRId64 ","
- "'speed': %" PRId64 " }",
- job->job_type->job_type,
- bdrv_get_device_name(job->bs),
- job->len,
- job->offset,
- job->speed);
-}
-
static void block_job_cb(void *opaque, int ret)
{
BlockDriverState *bs = opaque;
@@ -1157,16 +1143,6 @@ void qmp_block_commit(const char *device,
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
- if (base && has_base) {
- base_bs = bdrv_find_backing_image(bs, base);
- } else {
- base_bs = bdrv_find_base(bs);
- }
-
- if (base_bs == NULL) {
- error_set(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL");
- return;
- }
/* default top_bs is the active layer */
top_bs = bs;
@@ -1182,6 +1158,17 @@ void qmp_block_commit(const char *device,
return;
}
+ if (has_base && base) {
+ base_bs = bdrv_find_backing_image(top_bs, base);
+ } else {
+ base_bs = bdrv_find_base(top_bs);
+ }
+
+ if (base_bs == NULL) {
+ error_set(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL");
+ return;
+ }
+
commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
&local_err);
if (local_err != NULL) {
@@ -1194,6 +1181,140 @@ void qmp_block_commit(const char *device,
drive_get_ref(drive_get_by_blockdev(bs));
}
+void qmp_drive_mirror(const char *device, const char *target,
+ bool has_format, const char *format,
+ enum MirrorSyncMode sync,
+ bool has_mode, enum NewImageMode mode,
+ bool has_speed, int64_t speed,
+ bool has_on_source_error, BlockdevOnError on_source_error,
+ bool has_on_target_error, BlockdevOnError on_target_error,
+ Error **errp)
+{
+ BlockDriverInfo bdi;
+ BlockDriverState *bs;
+ BlockDriverState *source, *target_bs;
+ BlockDriver *proto_drv;
+ BlockDriver *drv = NULL;
+ Error *local_err = NULL;
+ int flags;
+ uint64_t size;
+ int ret;
+
+ if (!has_speed) {
+ speed = 0;
+ }
+ if (!has_on_source_error) {
+ on_source_error = BLOCKDEV_ON_ERROR_REPORT;
+ }
+ if (!has_on_target_error) {
+ on_target_error = BLOCKDEV_ON_ERROR_REPORT;
+ }
+ if (!has_mode) {
+ mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ }
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
+ }
+
+ if (!bdrv_is_inserted(bs)) {
+ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+ return;
+ }
+
+ if (!has_format) {
+ format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
+ }
+ if (format) {
+ drv = bdrv_find_format(format);
+ if (!drv) {
+ error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
+ return;
+ }
+ }
+
+ if (bdrv_in_use(bs)) {
+ error_set(errp, QERR_DEVICE_IN_USE, device);
+ return;
+ }
+
+ flags = bs->open_flags | BDRV_O_RDWR;
+ source = bs->backing_hd;
+ if (!source && sync == MIRROR_SYNC_MODE_TOP) {
+ sync = MIRROR_SYNC_MODE_FULL;
+ }
+
+ proto_drv = bdrv_find_protocol(target);
+ if (!proto_drv) {
+ error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
+ return;
+ }
+
+ if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) {
+ /* create new image w/o backing file */
+ assert(format && drv);
+ bdrv_get_geometry(bs, &size);
+ size *= 512;
+ ret = bdrv_img_create(target, format,
+ NULL, NULL, NULL, size, flags);
+ } else {
+ switch (mode) {
+ case NEW_IMAGE_MODE_EXISTING:
+ ret = 0;
+ break;
+ case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
+ /* create new image with backing file */
+ ret = bdrv_img_create(target, format,
+ source->filename,
+ source->drv->format_name,
+ NULL, -1, flags);
+ break;
+ default:
+ abort();
+ }
+ }
+
+ if (ret) {
+ error_set(errp, QERR_OPEN_FILE_FAILED, target);
+ return;
+ }
+
+ target_bs = bdrv_new("");
+ ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv);
+
+ if (ret < 0) {
+ bdrv_delete(target_bs);
+ error_set(errp, QERR_OPEN_FILE_FAILED, target);
+ return;
+ }
+
+ /* We need a backing file if we will copy parts of a cluster. */
+ if (bdrv_get_info(target_bs, &bdi) >= 0 && bdi.cluster_size != 0 &&
+ bdi.cluster_size >= BDRV_SECTORS_PER_DIRTY_CHUNK * 512) {
+ ret = bdrv_open_backing_file(target_bs);
+ if (ret < 0) {
+ bdrv_delete(target_bs);
+ error_set(errp, QERR_OPEN_FILE_FAILED, target);
+ return;
+ }
+ }
+
+ mirror_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
+ block_job_cb, bs, &local_err);
+ if (local_err != NULL) {
+ bdrv_delete(target_bs);
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ /* Grab a reference so hotplug does not delete the BlockDriverState from
+ * underneath us.
+ */
+ drive_get_ref(drive_get_by_blockdev(bs));
+}
+
static BlockJob *find_block_job(const char *device)
{
BlockDriverState *bs;
@@ -1265,6 +1386,19 @@ void qmp_block_job_resume(const char *device, Error **errp)
block_job_resume(job);
}
+void qmp_block_job_complete(const char *device, Error **errp)
+{
+ BlockJob *job = find_block_job(device);
+
+ if (!job) {
+ error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
+ return;
+ }
+
+ trace_qmp_block_job_complete(job);
+ block_job_complete(job, errp);
+}
+
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
{
BlockJobInfoList **prev = opaque;
diff --git a/blockjob.c b/blockjob.c
index f55f55a193..cda12c6933 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -71,7 +71,7 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
return job;
}
-void block_job_complete(BlockJob *job, int ret)
+void block_job_completed(BlockJob *job, int ret)
{
BlockDriverState *bs = job->bs;
@@ -99,6 +99,16 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
job->speed = speed;
}
+void block_job_complete(BlockJob *job, Error **errp)
+{
+ if (job->paused || job->cancelled || !job->job_type->complete) {
+ error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
+ return;
+ }
+
+ job->job_type->complete(job, errp);
+}
+
void block_job_pause(BlockJob *job)
{
job->paused = true;
@@ -132,6 +142,9 @@ bool block_job_is_cancelled(BlockJob *job)
void block_job_iostatus_reset(BlockJob *job)
{
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
+ if (job->job_type->iostatus_reset) {
+ job->job_type->iostatus_reset(job);
+ }
}
struct BlockCancelData {
@@ -215,6 +228,27 @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
}
+QObject *qobject_from_block_job(BlockJob *job)
+{
+ return qobject_from_jsonf("{ 'type': %s,"
+ "'device': %s,"
+ "'len': %" PRId64 ","
+ "'offset': %" PRId64 ","
+ "'speed': %" PRId64 " }",
+ job->job_type->job_type,
+ bdrv_get_device_name(job->bs),
+ job->len,
+ job->offset,
+ job->speed);
+}
+
+void block_job_ready(BlockJob *job)
+{
+ QObject *data = qobject_from_block_job(job);
+ monitor_protocol_event(QEVENT_BLOCK_JOB_READY, data);
+ qobject_decref(data);
+}
+
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
BlockdevOnError on_err,
int is_read, int error)
diff --git a/blockjob.h b/blockjob.h
index 930cc3c46a..3792b73e52 100644
--- a/blockjob.h
+++ b/blockjob.h
@@ -41,6 +41,15 @@ typedef struct BlockJobType {
/** Optional callback for job types that support setting a speed limit */
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
+
+ /** Optional callback for job types that need to forward I/O status reset */
+ void (*iostatus_reset)(BlockJob *job);
+
+ /**
+ * Optional callback for job types whose completion must be triggered
+ * manually.
+ */
+ void (*complete)(BlockJob *job, Error **errp);
} BlockJobType;
/**
@@ -135,14 +144,14 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns);
/**
- * block_job_complete:
+ * block_job_completed:
* @job: The job being completed.
* @ret: The status code.
*
* Call the completion function that was registered at creation time, and
* free @job.
*/
-void block_job_complete(BlockJob *job, int ret);
+void block_job_completed(BlockJob *job, int ret);
/**
* block_job_set_speed:
@@ -164,6 +173,15 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
void block_job_cancel(BlockJob *job);
/**
+ * block_job_complete:
+ * @job: The job to be completed.
+ * @errp: Error object.
+ *
+ * Asynchronously complete the specified job.
+ */
+void block_job_complete(BlockJob *job, Error **errp);
+
+/**
* block_job_is_cancelled:
* @job: The job being queried.
*
@@ -196,6 +214,22 @@ void block_job_pause(BlockJob *job);
void block_job_resume(BlockJob *job);
/**
+ * qobject_from_block_job:
+ * @job: The job whose information is requested.
+ *
+ * Return a QDict corresponding to @job's query-block-jobs entry.
+ */
+QObject *qobject_from_block_job(BlockJob *job);
+
+/**
+ * block_job_ready:
+ * @job: The job which is now ready to complete.
+ *
+ * Send a BLOCK_JOB_READY event for the specified job.
+ */
+void block_job_ready(BlockJob *job);
+
+/**
* block_job_is_paused:
* @job: The job being queried.
*
@@ -222,7 +256,8 @@ int block_job_cancel_sync(BlockJob *job);
* block_job_iostatus_reset:
* @job: The job whose I/O status should be reset.
*
- * Reset I/O status on @job.
+ * Reset I/O status on @job and on BlockDriverState objects it uses,
+ * other than job->bs.
*/
void block_job_iostatus_reset(BlockJob *job);
diff --git a/configure b/configure
index fa5657f6bc..3685020ffa 100755
--- a/configure
+++ b/configure
@@ -1160,6 +1160,7 @@ gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags"
gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
gcc_flags="-fstack-protector-all -Wendif-labels $gcc_flags"
+gcc_flags="-Wno-initializer-overrides $gcc_flags"
# Note that we do not add -Werror to gcc_flags here, because that would
# enable it for all configure tests. If a configure test failed due
# to -Werror this would just silently disable some features,
@@ -2398,8 +2399,7 @@ cat > $TMPC << EOF
int main(void)
{
int pipefd[2];
- pipe2(pipefd, O_CLOEXEC);
- return 0;
+ return pipe2(pipefd, O_CLOEXEC);
}
EOF
if compile_prog "" "" ; then
@@ -2813,6 +2813,24 @@ if compile_prog "" "" ; then
fi
##########################################
+# check if we have usable SIGEV_THREAD_ID
+
+sigev_thread_id=no
+cat > $TMPC << EOF
+#include <signal.h>
+int main(void) {
+ struct sigevent ev;
+ ev.sigev_notify = SIGEV_THREAD_ID;
+ ev._sigev_un._tid = 0;
+ asm volatile("" : : "g"(&ev));
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ sigev_thread_id=yes
+fi
+
+##########################################
# check if trace backend exists
$python "$source_path/scripts/tracetool.py" "--backend=$trace_backend" --check-backend > /dev/null 2> /dev/null
@@ -3159,6 +3177,7 @@ echo "preadv support $preadv"
echo "fdatasync $fdatasync"
echo "madvise $madvise"
echo "posix_madvise $posix_madvise"
+echo "sigev_thread_id $sigev_thread_id"
echo "uuid support $uuid"
echo "libcap-ng support $cap_ng"
echo "vhost-net support $vhost_net"
@@ -3443,6 +3462,9 @@ fi
if test "$posix_madvise" = "yes" ; then
echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak
fi
+if test "$sigev_thread_id" = "yes" ; then
+ echo "CONFIG_SIGEV_THREAD_ID=y" >> $config_host_mak
+fi
if test "$spice" = "yes" ; then
echo "CONFIG_SPICE=y" >> $config_host_mak
diff --git a/console.h b/console.h
index f990684ef4..6099d8d710 100644
--- a/console.h
+++ b/console.h
@@ -378,7 +378,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen);
/* vnc.c */
void vnc_display_init(DisplayState *ds);
void vnc_display_close(DisplayState *ds);
-int vnc_display_open(DisplayState *ds, const char *display);
+void vnc_display_open(DisplayState *ds, const char *display, Error **errp);
void vnc_display_add_client(DisplayState *ds, int csock, int skipauth);
int vnc_display_disable_login(DisplayState *ds);
char *vnc_display_local_addr(DisplayState *ds);
diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak
index c9a36c1e46..03e8b42712 100644
--- a/default-configs/sparc64-softmmu.mak
+++ b/default-configs/sparc64-softmmu.mak
@@ -6,7 +6,6 @@ CONFIG_M48T59=y
CONFIG_PTIMER=y
CONFIG_VGA=y
CONFIG_VGA_PCI=y
-CONFIG_VGA_CIRRUS=y
CONFIG_SERIAL=y
CONFIG_PARALLEL=y
CONFIG_PCKBD=y
diff --git a/error.c b/error.c
index 1f05fc466e..128d88cd91 100644
--- a/error.c
+++ b/error.c
@@ -43,6 +43,34 @@ void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...)
*errp = err;
}
+void error_set_errno(Error **errp, int os_errno, ErrorClass err_class,
+ const char *fmt, ...)
+{
+ Error *err;
+ char *msg1;
+ va_list ap;
+
+ if (errp == NULL) {
+ return;
+ }
+ assert(*errp == NULL);
+
+ err = g_malloc0(sizeof(*err));
+
+ va_start(ap, fmt);
+ msg1 = g_strdup_vprintf(fmt, ap);
+ if (os_errno != 0) {
+ err->msg = g_strdup_printf("%s: %s", msg1, strerror(os_errno));
+ g_free(msg1);
+ } else {
+ err->msg = msg1;
+ }
+ va_end(ap);
+ err->err_class = err_class;
+
+ *errp = err;
+}
+
Error *error_copy(const Error *err)
{
Error *err_new;
diff --git a/error.h b/error.h
index da7fed399e..4d52e7369e 100644
--- a/error.h
+++ b/error.h
@@ -30,10 +30,19 @@ typedef struct Error Error;
void error_set(Error **err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(3, 4);
/**
+ * Set an indirect pointer to an error given a ErrorClass value and a
+ * printf-style human message, followed by a strerror() string if
+ * @os_error is not zero.
+ */
+void error_set_errno(Error **err, int os_error, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(4, 5);
+
+/**
* Same as error_set(), but sets a generic error
*/
#define error_setg(err, fmt, ...) \
error_set(err, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__)
+#define error_setg_errno(err, os_error, fmt, ...) \
+ error_set_errno(err, os_error, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__)
/**
* Returns true if an indirect pointer to an error is pointing to a valid
diff --git a/hmp-commands.hx b/hmp-commands.hx
index e0b537d0cc..f916385c0a 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -109,7 +109,22 @@ ETEXI
STEXI
@item block_job_cancel
@findex block_job_cancel
-Stop an active block streaming operation.
+Stop an active background block operation (streaming, mirroring).
+ETEXI
+
+ {
+ .name = "block_job_complete",
+ .args_type = "device:B",
+ .params = "device",
+ .help = "stop an active background block operation",
+ .mhandler.cmd = hmp_block_job_complete,
+ },
+
+STEXI
+@item block_job_complete
+@findex block_job_complete
+Manually trigger completion of an active background block operation.
+For mirroring, this will switch the device to the destination path.
ETEXI
{
@@ -989,6 +1004,27 @@ Snapshot device, using snapshot file as target if provided
ETEXI
{
+ .name = "drive_mirror",
+ .args_type = "reuse:-n,full:-f,device:B,target:s,format:s?",
+ .params = "[-n] [-f] device target [format]",
+ .help = "initiates live storage\n\t\t\t"
+ "migration for a device. The device's contents are\n\t\t\t"
+ "copied to the new image file, including data that\n\t\t\t"
+ "is written after the command is started.\n\t\t\t"
+ "The -n flag requests QEMU to reuse the image found\n\t\t\t"
+ "in new-image-file, instead of recreating it from scratch.\n\t\t\t"
+ "The -f flag requests QEMU to copy the whole disk,\n\t\t\t"
+ "so that the result does not need a backing file.\n\t\t\t",
+ .mhandler.cmd = hmp_drive_mirror,
+ },
+STEXI
+@item drive_mirror
+@findex drive_mirror
+Start mirroring a block device's writes to a new destination,
+using the specified target.
+ETEXI
+
+ {
.name = "drive_add",
.args_type = "pci_addr:s,opts:s",
.params = "[[<domain>:]<bus>:]<slot>\n"
diff --git a/hmp.c b/hmp.c
index 2b979826ee..895a343dc3 100644
--- a/hmp.c
+++ b/hmp.c
@@ -245,20 +245,19 @@ void hmp_info_cpus(Monitor *mon)
active = '*';
}
- monitor_printf(mon, "%c CPU #%" PRId64 ": ", active, cpu->value->CPU);
+ monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU);
if (cpu->value->has_pc) {
- monitor_printf(mon, "pc=0x%016" PRIx64, cpu->value->pc);
+ monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->pc);
}
if (cpu->value->has_nip) {
- monitor_printf(mon, "nip=0x%016" PRIx64, cpu->value->nip);
+ monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->nip);
}
if (cpu->value->has_npc) {
- monitor_printf(mon, "pc=0x%016" PRIx64, cpu->value->pc);
- monitor_printf(mon, "npc=0x%016" PRIx64, cpu->value->npc);
+ monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->npc);
}
if (cpu->value->has_PC) {
- monitor_printf(mon, "PC=0x%016" PRIx64, cpu->value->PC);
+ monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->PC);
}
if (cpu->value->halted) {
@@ -771,6 +770,35 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &errp);
}
+void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
+{
+ const char *device = qdict_get_str(qdict, "device");
+ const char *filename = qdict_get_str(qdict, "target");
+ const char *format = qdict_get_try_str(qdict, "format");
+ int reuse = qdict_get_try_bool(qdict, "reuse", 0);
+ int full = qdict_get_try_bool(qdict, "full", 0);
+ enum NewImageMode mode;
+ Error *errp = NULL;
+
+ if (!filename) {
+ error_set(&errp, QERR_MISSING_PARAMETER, "target");
+ hmp_handle_error(mon, &errp);
+ return;
+ }
+
+ if (reuse) {
+ mode = NEW_IMAGE_MODE_EXISTING;
+ } else {
+ mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ }
+
+ qmp_drive_mirror(device, filename, !!format, format,
+ full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
+ true, mode, false, 0,
+ false, 0, false, 0, &errp);
+ hmp_handle_error(mon, &errp);
+}
+
void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
@@ -990,6 +1018,16 @@ void hmp_block_job_resume(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &error);
}
+void hmp_block_job_complete(Monitor *mon, const QDict *qdict)
+{
+ Error *error = NULL;
+ const char *device = qdict_get_str(qdict, "device");
+
+ qmp_block_job_complete(device, &error);
+
+ hmp_handle_error(mon, &error);
+}
+
typedef struct MigrationStatus
{
QEMUTimer *timer;
diff --git a/hmp.h b/hmp.h
index 71ea384523..34eb2b36f3 100644
--- a/hmp.h
+++ b/hmp.h
@@ -51,6 +51,7 @@ void hmp_block_passwd(Monitor *mon, const QDict *qdict);
void hmp_balloon(Monitor *mon, const QDict *qdict);
void hmp_block_resize(Monitor *mon, const QDict *qdict);
void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
+void hmp_drive_mirror(Monitor *mon, const QDict *qdict);
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
@@ -66,6 +67,7 @@ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
void hmp_block_job_resume(Monitor *mon, const QDict *qdict);
+void hmp_block_job_complete(Monitor *mon, const QDict *qdict);
void hmp_migrate(Monitor *mon, const QDict *qdict);
void hmp_device_del(Monitor *mon, const QDict *qdict);
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
diff --git a/hw/ac97.c b/hw/ac97.c
index 0f561fa5c1..ce6a1dc609 100644
--- a/hw/ac97.c
+++ b/hw/ac97.c
@@ -1226,32 +1226,101 @@ static const VMStateDescription vmstate_ac97 = {
}
};
-static const MemoryRegionPortio nam_portio[] = {
- { 0, 256 * 1, 1, .read = nam_readb, },
- { 0, 256 * 2, 2, .read = nam_readw, },
- { 0, 256 * 4, 4, .read = nam_readl, },
- { 0, 256 * 1, 1, .write = nam_writeb, },
- { 0, 256 * 2, 2, .write = nam_writew, },
- { 0, 256 * 4, 4, .write = nam_writel, },
- PORTIO_END_OF_LIST (),
-};
+static uint64_t nam_read(void *opaque, hwaddr addr, unsigned size)
+{
+ if ((addr / size) > 256) {
+ return -1;
+ }
+
+ switch (size) {
+ case 1:
+ return nam_readb(opaque, addr);
+ case 2:
+ return nam_readw(opaque, addr);
+ case 4:
+ return nam_readl(opaque, addr);
+ default:
+ return -1;
+ }
+}
+
+static void nam_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ if ((addr / size) > 256) {
+ return;
+ }
+
+ switch (size) {
+ case 1:
+ nam_writeb(opaque, addr, val);
+ break;
+ case 2:
+ nam_writew(opaque, addr, val);
+ break;
+ case 4:
+ nam_writel(opaque, addr, val);
+ break;
+ }
+}
static const MemoryRegionOps ac97_io_nam_ops = {
- .old_portio = nam_portio,
+ .read = nam_read,
+ .write = nam_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
-static const MemoryRegionPortio nabm_portio[] = {
- { 0, 64 * 1, 1, .read = nabm_readb, },
- { 0, 64 * 2, 2, .read = nabm_readw, },
- { 0, 64 * 4, 4, .read = nabm_readl, },
- { 0, 64 * 1, 1, .write = nabm_writeb, },
- { 0, 64 * 2, 2, .write = nabm_writew, },
- { 0, 64 * 4, 4, .write = nabm_writel, },
- PORTIO_END_OF_LIST ()
-};
+static uint64_t nabm_read(void *opaque, hwaddr addr, unsigned size)
+{
+ if ((addr / size) > 64) {
+ return -1;
+ }
+
+ switch (size) {
+ case 1:
+ return nabm_readb(opaque, addr);
+ case 2:
+ return nabm_readw(opaque, addr);
+ case 4:
+ return nabm_readl(opaque, addr);
+ default:
+ return -1;
+ }
+}
+
+static void nabm_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ if ((addr / size) > 64) {
+ return;
+ }
+
+ switch (size) {
+ case 1:
+ nabm_writeb(opaque, addr, val);
+ break;
+ case 2:
+ nabm_writew(opaque, addr, val);
+ break;
+ case 4:
+ nabm_writel(opaque, addr, val);
+ break;
+ }
+}
+
static const MemoryRegionOps ac97_io_nabm_ops = {
- .old_portio = nabm_portio,
+ .read = nabm_read,
+ .write = nabm_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static void ac97_on_reset (void *opaque)
diff --git a/hw/es1370.c b/hw/es1370.c
index e34234c350..e0c9729dc9 100644
--- a/hw/es1370.c
+++ b/hw/es1370.c
@@ -908,18 +908,44 @@ static void es1370_adc_callback (void *opaque, int avail)
es1370_run_channel (s, ADC_CHANNEL, avail);
}
-static const MemoryRegionPortio es1370_portio[] = {
- { 0, 0x40 * 4, 1, .write = es1370_writeb, },
- { 0, 0x40 * 2, 2, .write = es1370_writew, },
- { 0, 0x40, 4, .write = es1370_writel, },
- { 0, 0x40 * 4, 1, .read = es1370_readb, },
- { 0, 0x40 * 2, 2, .read = es1370_readw, },
- { 0, 0x40, 4, .read = es1370_readl, },
- PORTIO_END_OF_LIST ()
-};
+static uint64_t es1370_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (size) {
+ case 1:
+ return es1370_readb(opaque, addr);
+ case 2:
+ return es1370_readw(opaque, addr);
+ case 4:
+ return es1370_readl(opaque, addr);
+ default:
+ return -1;
+ }
+}
+
+static void es1370_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ switch (size) {
+ case 1:
+ es1370_writeb(opaque, addr, val);
+ break;
+ case 2:
+ es1370_writew(opaque, addr, val);
+ break;
+ case 4:
+ es1370_writel(opaque, addr, val);
+ break;
+ }
+}
static const MemoryRegionOps es1370_io_ops = {
- .old_portio = es1370_portio,
+ .read = es1370_read,
+ .write = es1370_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
.endianness = DEVICE_LITTLE_ENDIAN,
};
diff --git a/hw/i8254.c b/hw/i8254.c
index 77bd5e8222..bea5f92fd2 100644
--- a/hw/i8254.c
+++ b/hw/i8254.c
@@ -111,7 +111,8 @@ static void pit_latch_count(PITChannelState *s)
}
}
-static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+static void pit_ioport_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
{
PITCommonState *pit = opaque;
int channel, access;
@@ -178,7 +179,8 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
}
}
-static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
+static uint64_t pit_ioport_read(void *opaque, hwaddr addr,
+ unsigned size)
{
PITCommonState *pit = opaque;
int ret, count;
@@ -290,14 +292,14 @@ static void pit_irq_control(void *opaque, int n, int enable)
}
}
-static const MemoryRegionPortio pit_portio[] = {
- { 0, 4, 1, .write = pit_ioport_write },
- { 0, 3, 1, .read = pit_ioport_read },
- PORTIO_END_OF_LIST()
-};
-
static const MemoryRegionOps pit_ioport_ops = {
- .old_portio = pit_portio
+ .read = pit_ioport_read,
+ .write = pit_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static void pit_post_load(PITCommonState *s)
diff --git a/hw/m48t59.c b/hw/m48t59.c
index 9eb1a0968a..9e8e692669 100644
--- a/hw/m48t59.c
+++ b/hw/m48t59.c
@@ -27,6 +27,7 @@
#include "sysemu.h"
#include "sysbus.h"
#include "isa.h"
+#include "exec-memory.h"
//#define DEBUG_NVRAM
@@ -80,6 +81,7 @@ typedef struct M48t59ISAState {
typedef struct M48t59SysBusState {
SysBusDevice busdev;
M48t59State state;
+ MemoryRegion io;
} M48t59SysBusState;
/* Fake timer functions */
@@ -481,7 +483,8 @@ void m48t59_toggle_lock (void *opaque, int lock)
}
/* IO access to NVRAM */
-static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
+static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
{
M48t59State *NVRAM = opaque;
@@ -504,7 +507,7 @@ static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
}
}
-static uint32_t NVRAM_readb (void *opaque, uint32_t addr)
+static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size)
{
M48t59State *NVRAM = opaque;
uint32_t retval;
@@ -626,13 +629,14 @@ static void m48t59_reset_sysbus(DeviceState *d)
m48t59_reset_common(NVRAM);
}
-static const MemoryRegionPortio m48t59_portio[] = {
- {0, 4, 1, .read = NVRAM_readb, .write = NVRAM_writeb },
- PORTIO_END_OF_LIST(),
-};
-
static const MemoryRegionOps m48t59_io_ops = {
- .old_portio = m48t59_portio,
+ .read = NVRAM_readb,
+ .write = NVRAM_writeb,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
/* Initialisation routine */
@@ -653,9 +657,9 @@ M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base,
d = FROM_SYSBUS(M48t59SysBusState, s);
state = &d->state;
sysbus_connect_irq(s, 0, IRQ);
+ memory_region_init_io(&d->io, &m48t59_io_ops, state, "m48t59", 4);
if (io_base != 0) {
- register_ioport_read(io_base, 0x04, 1, NVRAM_readb, state);
- register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, state);
+ memory_region_add_subregion(get_system_io(), io_base, &d->io);
}
if (mem_base != 0) {
sysbus_mmio_map(s, 0, mem_base);
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 332a77d4dc..98839f278d 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -383,7 +383,8 @@ static void rtc_update_timer(void *opaque)
check_update_timer(s);
}
-static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+static void cmos_ioport_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
{
RTCState *s = opaque;
@@ -595,7 +596,8 @@ static int update_in_progress(RTCState *s)
return 0;
}
-static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
+static uint64_t cmos_ioport_read(void *opaque, hwaddr addr,
+ unsigned size)
{
RTCState *s = opaque;
int ret;
@@ -769,13 +771,14 @@ static void rtc_reset(void *opaque)
#endif
}
-static const MemoryRegionPortio cmos_portio[] = {
- {0, 2, 1, .read = cmos_ioport_read, .write = cmos_ioport_write },
- PORTIO_END_OF_LIST(),
-};
-
static const MemoryRegionOps cmos_ops = {
- .old_portio = cmos_portio
+ .read = cmos_ioport_read,
+ .write = cmos_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static void rtc_get_date(Object *obj, Visitor *v, void *opaque,
diff --git a/hw/nseries.c b/hw/nseries.c
index 7ada90d280..9306aa15a7 100644
--- a/hw/nseries.c
+++ b/hw/nseries.c
@@ -1334,8 +1334,9 @@ static void n8x0_init(ram_addr_t ram_size, const char *boot_device,
n8x0_dss_setup(s);
n8x0_cbus_setup(s);
n8x0_uart_setup(s);
- if (usb_enabled)
+ if (usb_enabled(false)) {
n8x0_usb_setup(s);
+ }
if (kernel_filename) {
/* Or at the linux loader. */
diff --git a/hw/pc.c b/hw/pc.c
index 16de04c4b7..a02b397a24 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -421,7 +421,8 @@ typedef struct Port92State {
qemu_irq *a20_out;
} Port92State;
-static void port92_write(void *opaque, uint32_t addr, uint32_t val)
+static void port92_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
{
Port92State *s = opaque;
@@ -433,7 +434,8 @@ static void port92_write(void *opaque, uint32_t addr, uint32_t val)
}
}
-static uint32_t port92_read(void *opaque, uint32_t addr)
+static uint64_t port92_read(void *opaque, hwaddr addr,
+ unsigned size)
{
Port92State *s = opaque;
uint32_t ret;
@@ -468,13 +470,14 @@ static void port92_reset(DeviceState *d)
s->outport &= ~1;
}
-static const MemoryRegionPortio port92_portio[] = {
- { 0, 1, 1, .read = port92_read, .write = port92_write },
- PORTIO_END_OF_LIST(),
-};
-
static const MemoryRegionOps port92_ops = {
- .old_portio = port92_portio
+ .read = port92_read,
+ .write = port92_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static int port92_initfn(ISADevice *dev)
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 47ebc1ac04..c7dd75b0c0 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -267,7 +267,7 @@ static void pc_init1(MemoryRegion *system_memory,
pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
floppy, idebus[0], idebus[1], rtc_state);
- if (pci_enabled && usb_enabled) {
+ if (pci_enabled && usb_enabled(false)) {
pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
}
diff --git a/hw/pckbd.c b/hw/pckbd.c
index 000c7f00fd..5bb3e0abf3 100644
--- a/hw/pckbd.c
+++ b/hw/pckbd.c
@@ -194,7 +194,8 @@ static void kbd_update_aux_irq(void *opaque, int level)
kbd_update_irq(s);
}
-static uint32_t kbd_read_status(void *opaque, uint32_t addr)
+static uint64_t kbd_read_status(void *opaque, hwaddr addr,
+ unsigned size)
{
KBDState *s = opaque;
int val;
@@ -223,7 +224,8 @@ static void outport_write(KBDState *s, uint32_t val)
}
}
-static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
+static void kbd_write_command(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
{
KBDState *s = opaque;
@@ -303,12 +305,13 @@ static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
/* ignore that */
break;
default:
- fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
+ fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val);
break;
}
}
-static uint32_t kbd_read_data(void *opaque, uint32_t addr)
+static uint64_t kbd_read_data(void *opaque, hwaddr addr,
+ unsigned size)
{
KBDState *s = opaque;
uint32_t val;
@@ -322,7 +325,8 @@ static uint32_t kbd_read_data(void *opaque, uint32_t addr)
return val;
}
-static void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
+static void kbd_write_data(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
{
KBDState *s = opaque;
@@ -385,9 +389,9 @@ static uint32_t kbd_mm_readb (void *opaque, hwaddr addr)
KBDState *s = opaque;
if (addr & s->mask)
- return kbd_read_status(s, 0) & 0xff;
+ return kbd_read_status(s, 0, 1) & 0xff;
else
- return kbd_read_data(s, 0) & 0xff;
+ return kbd_read_data(s, 0, 1) & 0xff;
}
static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value)
@@ -395,9 +399,9 @@ static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value)
KBDState *s = opaque;
if (addr & s->mask)
- kbd_write_command(s, 0, value & 0xff);
+ kbd_write_command(s, 0, value & 0xff, 1);
else
- kbd_write_data(s, 0, value & 0xff);
+ kbd_write_data(s, 0, value & 0xff, 1);
}
static const MemoryRegionOps i8042_mmio_ops = {
@@ -459,22 +463,24 @@ static const VMStateDescription vmstate_kbd_isa = {
}
};
-static const MemoryRegionPortio i8042_data_portio[] = {
- { 0, 1, 1, .read = kbd_read_data, .write = kbd_write_data },
- PORTIO_END_OF_LIST()
-};
-
-static const MemoryRegionPortio i8042_cmd_portio[] = {
- { 0, 1, 1, .read = kbd_read_status, .write = kbd_write_command },
- PORTIO_END_OF_LIST()
-};
-
static const MemoryRegionOps i8042_data_ops = {
- .old_portio = i8042_data_portio
+ .read = kbd_read_data,
+ .write = kbd_write_data,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static const MemoryRegionOps i8042_cmd_ops = {
- .old_portio = i8042_cmd_portio
+ .read = kbd_read_status,
+ .write = kbd_write_command,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static int i8042_initfn(ISADevice *dev)
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 951e407f14..8fe21235f0 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -11,6 +11,7 @@ obj-y += ppc_newworld.o
obj-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o spapr_iommu.o
+obj-$(CONFIG_PSERIES) += spapr_events.o
# PowerPC 4xx boards
obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
obj-y += ppc440_bamboo.o
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index d655e3f46f..6749ffffb3 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -52,7 +52,6 @@
#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000ULL)
#define MPC8544_PCI_REGS_SIZE 0x1000ULL
#define MPC8544_PCI_IO 0xE1000000ULL
-#define MPC8544_PCI_IOLEN 0x10000ULL
#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000ULL)
#define MPC8544_SPIN_BASE 0xEF000000ULL
@@ -496,7 +495,7 @@ void ppce500_init(PPCE500Params *params)
if (serial_hds[1]) {
serial_mm_init(address_space_mem, MPC8544_SERIAL1_REGS_BASE,
0, mpic[12+26], 399193,
- serial_hds[0], DEVICE_BIG_ENDIAN);
+ serial_hds[1], DEVICE_BIG_ENDIAN);
}
/* General Utility device */
@@ -511,7 +510,7 @@ void ppce500_init(PPCE500Params *params)
if (!pci_bus)
printf("couldn't create PCI controller!\n");
- isa_mmio_init(MPC8544_PCI_IO, MPC8544_PCI_IOLEN);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 1, MPC8544_PCI_IO);
if (pci_bus) {
/* Register network interfaces. */
diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c
index a6b1d51fae..cc85607cb7 100644
--- a/hw/ppc440_bamboo.c
+++ b/hw/ppc440_bamboo.c
@@ -59,7 +59,7 @@ static int bamboo_load_device_tree(hwaddr addr,
{
int ret = -1;
#ifdef CONFIG_FDT
- uint32_t mem_reg_property[] = { 0, 0, ramsize };
+ uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) };
char *filename;
int fdt_size;
void *fdt;
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index 15f74f95fd..664747ead3 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -348,10 +348,6 @@ static void ppc_core99_init(QEMUMachineInitArgs *args)
ide_mem[1] = pmac_ide_init(hd, pic[0x0d], dbdma, 0x16, pic[0x02]);
ide_mem[2] = pmac_ide_init(&hd[MAX_IDE_DEVS], pic[0x0e], dbdma, 0x1a, pic[0x02]);
- /* cuda also initialize ADB */
- if (machine_arch == ARCH_MAC99_U3) {
- usb_enabled = 1;
- }
cuda_init(&cuda_mem, pic[0x19]);
adb_kbd_init(&adb_bus);
@@ -360,15 +356,14 @@ static void ppc_core99_init(QEMUMachineInitArgs *args)
macio_init(pci_bus, PCI_DEVICE_ID_APPLE_UNI_N_KEYL, 0, pic_mem,
dbdma_mem, cuda_mem, NULL, 3, ide_mem, escc_bar);
- if (usb_enabled) {
+ if (usb_enabled(machine_arch == ARCH_MAC99_U3)) {
pci_create_simple(pci_bus, -1, "pci-ohci");
- }
-
- /* U3 needs to use USB for input because Linux doesn't support via-cuda
- on PPC64 */
- if (machine_arch == ARCH_MAC99_U3) {
- usbdevice_create("keyboard");
- usbdevice_create("mouse");
+ /* U3 needs to use USB for input because Linux doesn't support via-cuda
+ on PPC64 */
+ if (machine_arch == ARCH_MAC99_U3) {
+ usbdevice_create("keyboard");
+ usbdevice_create("mouse");
+ }
}
if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8)
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index a4f899d889..e8138c091e 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -286,7 +286,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args)
macio_init(pci_bus, PCI_DEVICE_ID_APPLE_343S1201, 1, pic_mem,
dbdma_mem, cuda_mem, nvr, 2, ide_mem, escc_bar);
- if (usb_enabled) {
+ if (usb_enabled(false)) {
pci_create_simple(pci_bus, -1, "pci-ohci");
}
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
index 085851a3e6..bf15730d8b 100644
--- a/hw/ppc_prep.c
+++ b/hw/ppc_prep.c
@@ -661,7 +661,7 @@ static void ppc_prep_init(QEMUMachineInitArgs *args)
memory_region_add_subregion(sysmem, 0xFEFF0000, xcsr);
#endif
- if (usb_enabled) {
+ if (usb_enabled(false)) {
pci_create_simple(pci_bus, -1, "pci-ohci");
}
diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
index 332748a6de..2ff7438d09 100644
--- a/hw/ppce500_pci.c
+++ b/hw/ppce500_pci.c
@@ -31,6 +31,8 @@
#define PCIE500_ALL_SIZE 0x1000
#define PCIE500_REG_SIZE (PCIE500_ALL_SIZE - PCIE500_REG_BASE)
+#define PCIE500_PCI_IOLEN 0x10000ULL
+
#define PPCE500_PCI_CONFIG_ADDR 0x0
#define PPCE500_PCI_CONFIG_DATA 0x4
#define PPCE500_PCI_INTACK 0x8
@@ -87,6 +89,7 @@ struct PPCE500PCIState {
/* mmio maps */
MemoryRegion container;
MemoryRegion iomem;
+ MemoryRegion pio;
};
typedef struct PPCE500PCIState PPCE500PCIState;
@@ -314,7 +317,6 @@ static int e500_pcihost_initfn(SysBusDevice *dev)
PCIBus *b;
int i;
MemoryRegion *address_space_mem = get_system_memory();
- MemoryRegion *address_space_io = get_system_io();
h = PCI_HOST_BRIDGE(dev);
s = PPC_E500_PCI_HOST_BRIDGE(dev);
@@ -323,9 +325,11 @@ static int e500_pcihost_initfn(SysBusDevice *dev)
sysbus_init_irq(dev, &s->irq[i]);
}
+ memory_region_init(&s->pio, "pci-pio", PCIE500_PCI_IOLEN);
+
b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq,
mpc85xx_pci_map_irq, s->irq, address_space_mem,
- address_space_io, PCI_DEVFN(0x11, 0), 4);
+ &s->pio, PCI_DEVFN(0x11, 0), 4);
h->bus = b;
pci_create_simple(b, 0, "e500-host-bridge");
@@ -341,6 +345,7 @@ static int e500_pcihost_initfn(SysBusDevice *dev)
memory_region_add_subregion(&s->container, PCIE500_CFGDATA, &h->data_mem);
memory_region_add_subregion(&s->container, PCIE500_REG_BASE, &s->iomem);
sysbus_init_mmio(dev, &s->container);
+ sysbus_init_mmio(dev, &s->pio);
return 0;
}
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index 0fb2179baf..e616979207 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -2108,7 +2108,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi");
}
- if (usb_enabled) {
+ if (usb_enabled(false)) {
sysbus_create_simple("sysbus-ohci", 0x4c000000,
qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1));
}
@@ -2239,7 +2239,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi");
}
- if (usb_enabled) {
+ if (usb_enabled(false)) {
sysbus_create_simple("sysbus-ohci", 0x4c000000,
qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1));
}
diff --git a/hw/realview.c b/hw/realview.c
index baa92d42fd..b5cb08cb84 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -227,7 +227,7 @@ static void realview_init(ram_addr_t ram_size,
sysbus_connect_irq(busdev, 2, pic[50]);
sysbus_connect_irq(busdev, 3, pic[51]);
pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci");
- if (usb_enabled) {
+ if (usb_enabled(false)) {
pci_create_simple(pci_bus, -1, "pci-ohci");
}
n = drive_get_max_bus(IF_SCSI);
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 10ced8ba7d..e3aa8bfb11 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3187,38 +3187,6 @@ static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
/* */
-static void rtl8139_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
- rtl8139_io_writeb(opaque, addr & 0xFF, val);
-}
-
-static void rtl8139_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
-{
- rtl8139_io_writew(opaque, addr & 0xFF, val);
-}
-
-static void rtl8139_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
-{
- rtl8139_io_writel(opaque, addr & 0xFF, val);
-}
-
-static uint32_t rtl8139_ioport_readb(void *opaque, uint32_t addr)
-{
- return rtl8139_io_readb(opaque, addr & 0xFF);
-}
-
-static uint32_t rtl8139_ioport_readw(void *opaque, uint32_t addr)
-{
- return rtl8139_io_readw(opaque, addr & 0xFF);
-}
-
-static uint32_t rtl8139_ioport_readl(void *opaque, uint32_t addr)
-{
- return rtl8139_io_readl(opaque, addr & 0xFF);
-}
-
-/* */
-
static void rtl8139_mmio_writeb(void *opaque, hwaddr addr, uint32_t val)
{
rtl8139_io_writeb(opaque, addr & 0xFF, val);
@@ -3386,18 +3354,44 @@ static const VMStateDescription vmstate_rtl8139 = {
/***********************************************************/
/* PCI RTL8139 definitions */
-static const MemoryRegionPortio rtl8139_portio[] = {
- { 0, 0x100, 1, .read = rtl8139_ioport_readb, },
- { 0, 0x100, 1, .write = rtl8139_ioport_writeb, },
- { 0, 0x100, 2, .read = rtl8139_ioport_readw, },
- { 0, 0x100, 2, .write = rtl8139_ioport_writew, },
- { 0, 0x100, 4, .read = rtl8139_ioport_readl, },
- { 0, 0x100, 4, .write = rtl8139_ioport_writel, },
- PORTIO_END_OF_LIST()
-};
+static void rtl8139_ioport_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ switch (size) {
+ case 1:
+ rtl8139_io_writeb(opaque, addr, val);
+ break;
+ case 2:
+ rtl8139_io_writew(opaque, addr, val);
+ break;
+ case 4:
+ rtl8139_io_writel(opaque, addr, val);
+ break;
+ }
+}
+
+static uint64_t rtl8139_ioport_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (size) {
+ case 1:
+ return rtl8139_io_readb(opaque, addr);
+ case 2:
+ return rtl8139_io_readw(opaque, addr);
+ case 4:
+ return rtl8139_io_readl(opaque, addr);
+ }
+
+ return -1;
+}
static const MemoryRegionOps rtl8139_io_ops = {
- .old_portio = rtl8139_portio,
+ .read = rtl8139_ioport_read,
+ .write = rtl8139_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
.endianness = DEVICE_LITTLE_ENDIAN,
};
diff --git a/hw/serial.c b/hw/serial.c
index ae84b22f35..60283eab91 100644
--- a/hw/serial.c
+++ b/hw/serial.c
@@ -26,6 +26,7 @@
#include "serial.h"
#include "qemu-char.h"
#include "qemu-timer.h"
+#include "exec-memory.h"
//#define DEBUG_SERIAL
@@ -305,7 +306,8 @@ static void serial_xmit(void *opaque)
}
-static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
{
SerialState *s = opaque;
@@ -451,7 +453,7 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
}
}
-static uint32_t serial_ioport_read(void *opaque, uint32_t addr)
+static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size)
{
SerialState *s = opaque;
uint32_t ret;
@@ -620,7 +622,7 @@ static int serial_post_load(void *opaque, int version_id)
s->fcr_vmstate = 0;
}
/* Initialize fcr via setter to perform essential side-effects */
- serial_ioport_write(s, 0x02, s->fcr_vmstate);
+ serial_ioport_write(s, 0x02, s->fcr_vmstate, 1);
serial_update_parameters(s);
return 0;
}
@@ -705,13 +707,14 @@ void serial_set_frequency(SerialState *s, uint32_t frequency)
serial_update_parameters(s);
}
-static const MemoryRegionPortio serial_portio[] = {
- { 0, 8, 1, .read = serial_ioport_read, .write = serial_ioport_write },
- PORTIO_END_OF_LIST()
-};
-
const MemoryRegionOps serial_io_ops = {
- .old_portio = serial_portio
+ .read = serial_ioport_read,
+ .write = serial_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
SerialState *serial_init(int base, qemu_irq irq, int baudbase,
@@ -728,8 +731,9 @@ SerialState *serial_init(int base, qemu_irq irq, int baudbase,
vmstate_register(NULL, base, &vmstate_serial, s);
- register_ioport_write(base, 8, 1, serial_ioport_write, s);
- register_ioport_read(base, 8, 1, serial_ioport_read, s);
+ memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
+ memory_region_add_subregion(get_system_io(), base, &s->io);
+
return s;
}
@@ -738,7 +742,7 @@ static uint64_t serial_mm_read(void *opaque, hwaddr addr,
unsigned size)
{
SerialState *s = opaque;
- return serial_ioport_read(s, addr >> s->it_shift);
+ return serial_ioport_read(s, addr >> s->it_shift, 1);
}
static void serial_mm_write(void *opaque, hwaddr addr,
@@ -746,7 +750,7 @@ static void serial_mm_write(void *opaque, hwaddr addr,
{
SerialState *s = opaque;
value &= ~0u >> (32 - (size * 8));
- serial_ioport_write(s, addr >> s->it_shift, value);
+ serial_ioport_write(s, addr >> s->it_shift, value, 1);
}
static const MemoryRegionOps serial_mm_ops[3] = {
diff --git a/hw/spapr.c b/hw/spapr.c
index 73d75e818c..8d0ad3cfe9 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -232,7 +232,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
hwaddr initrd_size,
hwaddr kernel_size,
const char *boot_device,
- const char *kernel_cmdline)
+ const char *kernel_cmdline,
+ uint32_t epow_irq)
{
void *fdt;
CPUPPCState *env;
@@ -403,6 +404,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property(fdt, "ibm,associativity-reference-points",
refpoints, sizeof(refpoints))));
+ _FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)));
+
_FDT((fdt_end_node(fdt)));
/* interrupt controller */
@@ -433,6 +436,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_end_node(fdt)));
+ /* event-sources */
+ spapr_events_fdt_skel(fdt, epow_irq);
+
_FDT((fdt_end_node(fdt))); /* close root node */
_FDT((fdt_finish(fdt)));
@@ -795,6 +801,9 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
spapr->icp = xics_system_init(XICS_IRQS);
spapr->next_irq = 16;
+ /* Set up EPOW events infrastructure */
+ spapr_events_init(spapr);
+
/* Set up IOMMU */
spapr_iommu_init();
@@ -840,7 +849,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
spapr->has_graphics = true;
}
- if (usb_enabled) {
+ if (usb_enabled(spapr->has_graphics)) {
pci_create_simple(phb->bus, -1, "pci-ohci");
if (spapr->has_graphics) {
usbdevice_create("keyboard");
@@ -903,7 +912,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
initrd_base, initrd_size,
kernel_size,
- boot_device, kernel_cmdline);
+ boot_device, kernel_cmdline,
+ spapr->epow_irq);
assert(spapr->fdt_skel != NULL);
}
diff --git a/hw/spapr.h b/hw/spapr.h
index 8ee69bdec8..51c709ea10 100644
--- a/hw/spapr.h
+++ b/hw/spapr.h
@@ -26,6 +26,9 @@ typedef struct sPAPREnvironment {
int rtc_offset;
char *cpu_model;
bool has_graphics;
+
+ uint32_t epow_irq;
+ Notifier epow_notifier;
} sPAPREnvironment;
#define H_SUCCESS 0
@@ -335,7 +338,12 @@ typedef struct sPAPRTCE {
#define SPAPR_VIO_BASE_LIOBN 0x00000000
#define SPAPR_PCI_BASE_LIOBN 0x80000000
+#define RTAS_ERROR_LOG_MAX 2048
+
+
void spapr_iommu_init(void);
+void spapr_events_init(sPAPREnvironment *spapr);
+void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq);
DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size);
void spapr_tce_free(DMAContext *dma);
void spapr_tce_reset(DMAContext *dma);
diff --git a/hw/spapr_events.c b/hw/spapr_events.c
new file mode 100644
index 0000000000..18ccd4a9e0
--- /dev/null
+++ b/hw/spapr_events.c
@@ -0,0 +1,321 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * RTAS events handling
+ *
+ * Copyright (c) 2012 David Gibson, IBM Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#include "cpu.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "hw/qdev.h"
+#include "device_tree.h"
+
+#include "hw/spapr.h"
+#include "hw/spapr_vio.h"
+
+#include <libfdt.h>
+
+struct rtas_error_log {
+ uint32_t summary;
+#define RTAS_LOG_VERSION_MASK 0xff000000
+#define RTAS_LOG_VERSION_6 0x06000000
+#define RTAS_LOG_SEVERITY_MASK 0x00e00000
+#define RTAS_LOG_SEVERITY_ALREADY_REPORTED 0x00c00000
+#define RTAS_LOG_SEVERITY_FATAL 0x00a00000
+#define RTAS_LOG_SEVERITY_ERROR 0x00800000
+#define RTAS_LOG_SEVERITY_ERROR_SYNC 0x00600000
+#define RTAS_LOG_SEVERITY_WARNING 0x00400000
+#define RTAS_LOG_SEVERITY_EVENT 0x00200000
+#define RTAS_LOG_SEVERITY_NO_ERROR 0x00000000
+#define RTAS_LOG_DISPOSITION_MASK 0x00180000
+#define RTAS_LOG_DISPOSITION_FULLY_RECOVERED 0x00000000
+#define RTAS_LOG_DISPOSITION_LIMITED_RECOVERY 0x00080000
+#define RTAS_LOG_DISPOSITION_NOT_RECOVERED 0x00100000
+#define RTAS_LOG_OPTIONAL_PART_PRESENT 0x00040000
+#define RTAS_LOG_INITIATOR_MASK 0x0000f000
+#define RTAS_LOG_INITIATOR_UNKNOWN 0x00000000
+#define RTAS_LOG_INITIATOR_CPU 0x00001000
+#define RTAS_LOG_INITIATOR_PCI 0x00002000
+#define RTAS_LOG_INITIATOR_MEMORY 0x00004000
+#define RTAS_LOG_INITIATOR_HOTPLUG 0x00006000
+#define RTAS_LOG_TARGET_MASK 0x00000f00
+#define RTAS_LOG_TARGET_UNKNOWN 0x00000000
+#define RTAS_LOG_TARGET_CPU 0x00000100
+#define RTAS_LOG_TARGET_PCI 0x00000200
+#define RTAS_LOG_TARGET_MEMORY 0x00000400
+#define RTAS_LOG_TARGET_HOTPLUG 0x00000600
+#define RTAS_LOG_TYPE_MASK 0x000000ff
+#define RTAS_LOG_TYPE_OTHER 0x00000000
+#define RTAS_LOG_TYPE_RETRY 0x00000001
+#define RTAS_LOG_TYPE_TCE_ERR 0x00000002
+#define RTAS_LOG_TYPE_INTERN_DEV_FAIL 0x00000003
+#define RTAS_LOG_TYPE_TIMEOUT 0x00000004
+#define RTAS_LOG_TYPE_DATA_PARITY 0x00000005
+#define RTAS_LOG_TYPE_ADDR_PARITY 0x00000006
+#define RTAS_LOG_TYPE_CACHE_PARITY 0x00000007
+#define RTAS_LOG_TYPE_ADDR_INVALID 0x00000008
+#define RTAS_LOG_TYPE_ECC_UNCORR 0x00000009
+#define RTAS_LOG_TYPE_ECC_CORR 0x0000000a
+#define RTAS_LOG_TYPE_EPOW 0x00000040
+ uint32_t extended_length;
+} QEMU_PACKED;
+
+struct rtas_event_log_v6 {
+ uint8_t b0;
+#define RTAS_LOG_V6_B0_VALID 0x80
+#define RTAS_LOG_V6_B0_UNRECOVERABLE_ERROR 0x40
+#define RTAS_LOG_V6_B0_RECOVERABLE_ERROR 0x20
+#define RTAS_LOG_V6_B0_DEGRADED_OPERATION 0x10
+#define RTAS_LOG_V6_B0_PREDICTIVE_ERROR 0x08
+#define RTAS_LOG_V6_B0_NEW_LOG 0x04
+#define RTAS_LOG_V6_B0_BIGENDIAN 0x02
+ uint8_t _resv1;
+ uint8_t b2;
+#define RTAS_LOG_V6_B2_POWERPC_FORMAT 0x80
+#define RTAS_LOG_V6_B2_LOG_FORMAT_MASK 0x0f
+#define RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT 0x0e
+ uint8_t _resv2[9];
+ uint32_t company;
+#define RTAS_LOG_V6_COMPANY_IBM 0x49424d00 /* IBM<null> */
+} QEMU_PACKED;
+
+struct rtas_event_log_v6_section_header {
+ uint16_t section_id;
+ uint16_t section_length;
+ uint8_t section_version;
+ uint8_t section_subtype;
+ uint16_t creator_component_id;
+} QEMU_PACKED;
+
+struct rtas_event_log_v6_maina {
+#define RTAS_LOG_V6_SECTION_ID_MAINA 0x5048 /* PH */
+ struct rtas_event_log_v6_section_header hdr;
+ uint32_t creation_date; /* BCD: YYYYMMDD */
+ uint32_t creation_time; /* BCD: HHMMSS00 */
+ uint8_t _platform1[8];
+ char creator_id;
+ uint8_t _resv1[2];
+ uint8_t section_count;
+ uint8_t _resv2[4];
+ uint8_t _platform2[8];
+ uint32_t plid;
+ uint8_t _platform3[4];
+} QEMU_PACKED;
+
+struct rtas_event_log_v6_mainb {
+#define RTAS_LOG_V6_SECTION_ID_MAINB 0x5548 /* UH */
+ struct rtas_event_log_v6_section_header hdr;
+ uint8_t subsystem_id;
+ uint8_t _platform1;
+ uint8_t event_severity;
+ uint8_t event_subtype;
+ uint8_t _platform2[4];
+ uint8_t _resv1[2];
+ uint16_t action_flags;
+ uint8_t _resv2[4];
+} QEMU_PACKED;
+
+struct rtas_event_log_v6_epow {
+#define RTAS_LOG_V6_SECTION_ID_EPOW 0x4550 /* EP */
+ struct rtas_event_log_v6_section_header hdr;
+ uint8_t sensor_value;
+#define RTAS_LOG_V6_EPOW_ACTION_RESET 0
+#define RTAS_LOG_V6_EPOW_ACTION_WARN_COOLING 1
+#define RTAS_LOG_V6_EPOW_ACTION_WARN_POWER 2
+#define RTAS_LOG_V6_EPOW_ACTION_SYSTEM_SHUTDOWN 3
+#define RTAS_LOG_V6_EPOW_ACTION_SYSTEM_HALT 4
+#define RTAS_LOG_V6_EPOW_ACTION_MAIN_ENCLOSURE 5
+#define RTAS_LOG_V6_EPOW_ACTION_POWER_OFF 7
+ uint8_t event_modifier;
+#define RTAS_LOG_V6_EPOW_MODIFIER_NORMAL 1
+#define RTAS_LOG_V6_EPOW_MODIFIER_ON_UPS 2
+#define RTAS_LOG_V6_EPOW_MODIFIER_CRITICAL 3
+#define RTAS_LOG_V6_EPOW_MODIFIER_TEMPERATURE 4
+ uint8_t extended_modifier;
+#define RTAS_LOG_V6_EPOW_XMODIFIER_SYSTEM_WIDE 0
+#define RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC 1
+ uint8_t _resv;
+ uint64_t reason_code;
+} QEMU_PACKED;
+
+struct epow_log_full {
+ struct rtas_error_log hdr;
+ struct rtas_event_log_v6 v6hdr;
+ struct rtas_event_log_v6_maina maina;
+ struct rtas_event_log_v6_mainb mainb;
+ struct rtas_event_log_v6_epow epow;
+} QEMU_PACKED;
+
+#define EVENT_MASK_INTERNAL_ERRORS 0x80000000
+#define EVENT_MASK_EPOW 0x40000000
+#define EVENT_MASK_HOTPLUG 0x10000000
+#define EVENT_MASK_IO 0x08000000
+
+#define _FDT(exp) \
+ do { \
+ int ret = (exp); \
+ if (ret < 0) { \
+ fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
+ #exp, fdt_strerror(ret)); \
+ exit(1); \
+ } \
+ } while (0)
+
+void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq)
+{
+ uint32_t epow_irq_ranges[] = {cpu_to_be32(epow_irq), cpu_to_be32(1)};
+ uint32_t epow_interrupts[] = {cpu_to_be32(epow_irq), 0};
+
+ _FDT((fdt_begin_node(fdt, "event-sources")));
+
+ _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
+ _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
+ _FDT((fdt_property(fdt, "interrupt-ranges",
+ epow_irq_ranges, sizeof(epow_irq_ranges))));
+
+ _FDT((fdt_begin_node(fdt, "epow-events")));
+ _FDT((fdt_property(fdt, "interrupts",
+ epow_interrupts, sizeof(epow_interrupts))));
+ _FDT((fdt_end_node(fdt)));
+
+ _FDT((fdt_end_node(fdt)));
+}
+
+static struct epow_log_full *pending_epow;
+static uint32_t next_plid;
+
+static void spapr_powerdown_req(Notifier *n, void *opaque)
+{
+ sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
+ struct rtas_error_log *hdr;
+ struct rtas_event_log_v6 *v6hdr;
+ struct rtas_event_log_v6_maina *maina;
+ struct rtas_event_log_v6_mainb *mainb;
+ struct rtas_event_log_v6_epow *epow;
+ struct tm tm;
+ int year;
+
+ if (pending_epow) {
+ /* For now, we just throw away earlier events if two come
+ * along before any are consumed. This is sufficient for our
+ * powerdown messages, but we'll need more if we do more
+ * general error/event logging */
+ g_free(pending_epow);
+ }
+ pending_epow = g_malloc0(sizeof(*pending_epow));
+ hdr = &pending_epow->hdr;
+ v6hdr = &pending_epow->v6hdr;
+ maina = &pending_epow->maina;
+ mainb = &pending_epow->mainb;
+ epow = &pending_epow->epow;
+
+ hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
+ | RTAS_LOG_SEVERITY_EVENT
+ | RTAS_LOG_DISPOSITION_NOT_RECOVERED
+ | RTAS_LOG_OPTIONAL_PART_PRESENT
+ | RTAS_LOG_TYPE_EPOW);
+ hdr->extended_length = cpu_to_be32(sizeof(*pending_epow)
+ - sizeof(pending_epow->hdr));
+
+ v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
+ | RTAS_LOG_V6_B0_BIGENDIAN;
+ v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
+ | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
+ v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
+
+ maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
+ maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
+ /* FIXME: section version, subtype and creator id? */
+ qemu_get_timedate(&tm, spapr->rtc_offset);
+ year = tm.tm_year + 1900;
+ maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
+ | (to_bcd(year % 100) << 16)
+ | (to_bcd(tm.tm_mon + 1) << 8)
+ | to_bcd(tm.tm_mday));
+ maina->creation_time = cpu_to_be32((to_bcd(tm.tm_hour) << 24)
+ | (to_bcd(tm.tm_min) << 16)
+ | (to_bcd(tm.tm_sec) << 8));
+ maina->creator_id = 'H'; /* Hypervisor */
+ maina->section_count = 3; /* Main-A, Main-B and EPOW */
+ maina->plid = next_plid++;
+
+ mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
+ mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
+ /* FIXME: section version, subtype and creator id? */
+ mainb->subsystem_id = 0xa0; /* External environment */
+ mainb->event_severity = 0x00; /* Informational / non-error */
+ mainb->event_subtype = 0xd0; /* Normal shutdown */
+
+ epow->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_EPOW);
+ epow->hdr.section_length = cpu_to_be16(sizeof(*epow));
+ epow->hdr.section_version = 2; /* includes extended modifier */
+ /* FIXME: section subtype and creator id? */
+ epow->sensor_value = RTAS_LOG_V6_EPOW_ACTION_SYSTEM_SHUTDOWN;
+ epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL;
+ epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC;
+
+ qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->epow_irq));
+}
+
+static void check_exception(sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ uint32_t mask, buf, len;
+ uint64_t xinfo;
+
+ if ((nargs < 6) || (nargs > 7) || nret != 1) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ xinfo = rtas_ld(args, 1);
+ mask = rtas_ld(args, 2);
+ buf = rtas_ld(args, 4);
+ len = rtas_ld(args, 5);
+ if (nargs == 7) {
+ xinfo |= (uint64_t)rtas_ld(args, 6) << 32;
+ }
+
+ if ((mask & EVENT_MASK_EPOW) && pending_epow) {
+ if (sizeof(*pending_epow) < len) {
+ len = sizeof(*pending_epow);
+ }
+
+ cpu_physical_memory_write(buf, pending_epow, len);
+ g_free(pending_epow);
+ pending_epow = NULL;
+ rtas_st(rets, 0, 0);
+ } else {
+ rtas_st(rets, 0, 1);
+ }
+}
+
+void spapr_events_init(sPAPREnvironment *spapr)
+{
+ spapr->epow_irq = spapr_allocate_msi(0);
+ spapr->epow_notifier.notify = spapr_powerdown_req;
+ qemu_register_powerdown_notifier(&spapr->epow_notifier);
+ spapr_rtas_register("check-exception", check_exception);
+}
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index 194d9c24b5..621dabdfb1 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -366,26 +366,26 @@ static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa)
return H_PARAMETER;
}
- env->vpa = vpa;
+ env->vpa_addr = vpa;
- tmp = ldub_phys(env->vpa + VPA_SHARED_PROC_OFFSET);
+ tmp = ldub_phys(env->vpa_addr + VPA_SHARED_PROC_OFFSET);
tmp |= VPA_SHARED_PROC_VAL;
- stb_phys(env->vpa + VPA_SHARED_PROC_OFFSET, tmp);
+ stb_phys(env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp);
return H_SUCCESS;
}
static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa)
{
- if (env->slb_shadow) {
+ if (env->slb_shadow_addr) {
return H_RESOURCE;
}
- if (env->dispatch_trace_log) {
+ if (env->dtl_addr) {
return H_RESOURCE;
}
- env->vpa = 0;
+ env->vpa_addr = 0;
return H_SUCCESS;
}
@@ -407,18 +407,20 @@ static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr)
return H_PARAMETER;
}
- if (!env->vpa) {
+ if (!env->vpa_addr) {
return H_RESOURCE;
}
- env->slb_shadow = addr;
+ env->slb_shadow_addr = addr;
+ env->slb_shadow_size = size;
return H_SUCCESS;
}
static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr)
{
- env->slb_shadow = 0;
+ env->slb_shadow_addr = 0;
+ env->slb_shadow_size = 0;
return H_SUCCESS;
}
@@ -437,11 +439,11 @@ static target_ulong register_dtl(CPUPPCState *env, target_ulong addr)
return H_PARAMETER;
}
- if (!env->vpa) {
+ if (!env->vpa_addr) {
return H_RESOURCE;
}
- env->dispatch_trace_log = addr;
+ env->dtl_addr = addr;
env->dtl_size = size;
return H_SUCCESS;
@@ -449,7 +451,7 @@ static target_ulong register_dtl(CPUPPCState *env, target_ulong addr)
static target_ulong deregister_dtl(CPUPPCState *env, target_ulong addr)
{
- env->dispatch_trace_log = 0;
+ env->dtl_addr = 0;
env->dtl_size = 0;
return H_SUCCESS;
@@ -670,11 +672,10 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
} else {
assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX));
-
slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
}
- assert(!(*slot) || (fn == *slot));
+ assert(!(*slot));
*slot = fn;
}
diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c
index a08ed11166..c2c3079d21 100644
--- a/hw/spapr_pci.c
+++ b/hw/spapr_pci.c
@@ -439,43 +439,6 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level)
qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level);
}
-static uint64_t spapr_io_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- switch (size) {
- case 1:
- return cpu_inb(addr);
- case 2:
- return cpu_inw(addr);
- case 4:
- return cpu_inl(addr);
- }
- assert(0);
-}
-
-static void spapr_io_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- switch (size) {
- case 1:
- cpu_outb(addr, data);
- return;
- case 2:
- cpu_outw(addr, data);
- return;
- case 4:
- cpu_outl(addr, data);
- return;
- }
- assert(0);
-}
-
-static const MemoryRegionOps spapr_io_ops = {
- .endianness = DEVICE_LITTLE_ENDIAN,
- .read = spapr_io_read,
- .write = spapr_io_write
-};
-
/*
* MSI/MSIX memory region implementation.
* The handler handles both MSI and MSIX.
@@ -545,14 +508,9 @@ static int spapr_phb_init(SysBusDevice *s)
* old_portion are updated */
sprintf(namebuf, "%s.io", sphb->dtbusname);
memory_region_init(&sphb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE);
- /* FIXME: fix to support multiple PHBs */
- memory_region_add_subregion(get_system_io(), 0, &sphb->iospace);
- sprintf(namebuf, "%s.io-alias", sphb->dtbusname);
- memory_region_init_io(&sphb->iowindow, &spapr_io_ops, sphb,
- namebuf, SPAPR_PCI_IO_WIN_SIZE);
memory_region_add_subregion(get_system_memory(), sphb->io_win_addr,
- &sphb->iowindow);
+ &sphb->iospace);
/* As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors,
* we need to allocate some memory to catch those writes coming
diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h
index e307ac8035..a77d7d5448 100644
--- a/hw/spapr_pci.h
+++ b/hw/spapr_pci.h
@@ -44,7 +44,7 @@ typedef struct sPAPRPHBState {
MemoryRegion memspace, iospace;
hwaddr mem_win_addr, mem_win_size, io_win_addr, io_win_size;
hwaddr msi_win_addr;
- MemoryRegion memwindow, iowindow, msiwindow;
+ MemoryRegion memwindow, msiwindow;
uint32_t dma_liobn;
uint64_t dma_window_start;
diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
index 67da27ba51..ce76c5856a 100644
--- a/hw/spapr_rtas.c
+++ b/hw/spapr_rtas.c
@@ -241,6 +241,15 @@ target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
void spapr_rtas_register(const char *name, spapr_rtas_fn fn)
{
+ int i;
+
+ for (i = 0; i < (rtas_next - rtas_table); i++) {
+ if (strcmp(name, rtas_table[i].name) == 0) {
+ fprintf(stderr, "RTAS call \"%s\" registered twice\n", name);
+ exit(1);
+ }
+ }
+
assert(rtas_next < (rtas_table + TOKEN_MAX));
rtas_next->name = name;
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index f55bd0ca1d..e85f982897 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -248,7 +248,7 @@ static void versatile_init(ram_addr_t ram_size,
pci_nic_init_nofail(nd, "rtl8139", NULL);
}
}
- if (usb_enabled) {
+ if (usb_enabled(false)) {
pci_create_simple(pci_bus, -1, "pci-ohci");
}
n = drive_get_max_bus(IF_SCSI);
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index c7f20c36a2..96031500ee 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -374,79 +374,39 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
return ret;
}
-static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr)
-{
- VirtIOPCIProxy *proxy = opaque;
- uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
- if (addr < config)
- return virtio_ioport_read(proxy, addr);
- addr -= config;
- return virtio_config_readb(proxy->vdev, addr);
-}
-
-static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr)
-{
- VirtIOPCIProxy *proxy = opaque;
- uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
- uint16_t val;
- if (addr < config)
- return virtio_ioport_read(proxy, addr);
- addr -= config;
- val = virtio_config_readw(proxy->vdev, addr);
- if (virtio_is_big_endian()) {
- /*
- * virtio is odd, ioports are LE but config space is target native
- * endian. However, in qemu, all PIO is LE, so we need to re-swap
- * on BE targets
- */
- val = bswap16(val);
- }
- return val;
-}
-
-static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr)
-{
- VirtIOPCIProxy *proxy = opaque;
- uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
- uint32_t val;
- if (addr < config)
- return virtio_ioport_read(proxy, addr);
- addr -= config;
- val = virtio_config_readl(proxy->vdev, addr);
- if (virtio_is_big_endian()) {
- val = bswap32(val);
- }
- return val;
-}
-
-static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val)
+static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
+ unsigned size)
{
VirtIOPCIProxy *proxy = opaque;
uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ uint64_t val = 0;
if (addr < config) {
- virtio_ioport_write(proxy, addr, val);
- return;
+ return virtio_ioport_read(proxy, addr);
}
addr -= config;
- virtio_config_writeb(proxy->vdev, addr, val);
-}
-static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val)
-{
- VirtIOPCIProxy *proxy = opaque;
- uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
- if (addr < config) {
- virtio_ioport_write(proxy, addr, val);
- return;
- }
- addr -= config;
- if (virtio_is_big_endian()) {
- val = bswap16(val);
+ switch (size) {
+ case 1:
+ val = virtio_config_readb(proxy->vdev, addr);
+ break;
+ case 2:
+ val = virtio_config_readw(proxy->vdev, addr);
+ if (virtio_is_big_endian()) {
+ val = bswap16(val);
+ }
+ break;
+ case 4:
+ val = virtio_config_readl(proxy->vdev, addr);
+ if (virtio_is_big_endian()) {
+ val = bswap32(val);
+ }
+ break;
}
- virtio_config_writew(proxy->vdev, addr, val);
+ return val;
}
-static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val)
+static void virtio_pci_config_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
{
VirtIOPCIProxy *proxy = opaque;
uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
@@ -455,24 +415,36 @@ static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val)
return;
}
addr -= config;
- if (virtio_is_big_endian()) {
- val = bswap32(val);
+ /*
+ * Virtio-PCI is odd. Ioports are LE but config space is target native
+ * endian.
+ */
+ switch (size) {
+ case 1:
+ virtio_config_writeb(proxy->vdev, addr, val);
+ break;
+ case 2:
+ if (virtio_is_big_endian()) {
+ val = bswap16(val);
+ }
+ virtio_config_writew(proxy->vdev, addr, val);
+ break;
+ case 4:
+ if (virtio_is_big_endian()) {
+ val = bswap32(val);
+ }
+ virtio_config_writel(proxy->vdev, addr, val);
+ break;
}
- virtio_config_writel(proxy->vdev, addr, val);
}
-static const MemoryRegionPortio virtio_portio[] = {
- { 0, 0x10000, 1, .write = virtio_pci_config_writeb, },
- { 0, 0x10000, 2, .write = virtio_pci_config_writew, },
- { 0, 0x10000, 4, .write = virtio_pci_config_writel, },
- { 0, 0x10000, 1, .read = virtio_pci_config_readb, },
- { 0, 0x10000, 2, .read = virtio_pci_config_readw, },
- { 0, 0x10000, 4, .read = virtio_pci_config_readl, },
- PORTIO_END_OF_LIST()
-};
-
static const MemoryRegionOps virtio_pci_config_ops = {
- .old_portio = virtio_portio,
+ .read = virtio_pci_config_read,
+ .write = virtio_pci_config_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
.endianness = DEVICE_LITTLE_ENDIAN,
};
diff --git a/hw/vmport.c b/hw/vmport.c
index a4f52ee5bb..3ab3a1405c 100644
--- a/hw/vmport.c
+++ b/hw/vmport.c
@@ -54,7 +54,8 @@ void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque)
port_state->opaque[command] = opaque;
}
-static uint32_t vmport_ioport_read(void *opaque, uint32_t addr)
+static uint64_t vmport_ioport_read(void *opaque, hwaddr addr,
+ unsigned size)
{
VMPortState *s = opaque;
CPUX86State *env = cpu_single_env;
@@ -81,11 +82,12 @@ static uint32_t vmport_ioport_read(void *opaque, uint32_t addr)
return s->func[command](s->opaque[command], addr);
}
-static void vmport_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+static void vmport_ioport_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
{
CPUX86State *env = cpu_single_env;
- env->regs[R_EAX] = vmport_ioport_read(opaque, addr);
+ env->regs[R_EAX] = vmport_ioport_read(opaque, addr, 4);
}
static uint32_t vmport_cmd_get_version(void *opaque, uint32_t addr)
@@ -121,13 +123,14 @@ void vmmouse_set_data(const uint32_t *data)
env->regs[R_ESI] = data[4]; env->regs[R_EDI] = data[5];
}
-static const MemoryRegionPortio vmport_portio[] = {
- {0, 1, 4, .read = vmport_ioport_read, .write = vmport_ioport_write },
- PORTIO_END_OF_LIST(),
-};
-
static const MemoryRegionOps vmport_ops = {
- .old_portio = vmport_portio
+ .read = vmport_ioport_read,
+ .write = vmport_ioport_write,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static int vmport_initfn(ISADevice *dev)
diff --git a/hw/xen_platform.c b/hw/xen_platform.c
index 890eb72f20..a54e7a2cdb 100644
--- a/hw/xen_platform.c
+++ b/hw/xen_platform.c
@@ -228,18 +228,46 @@ static void platform_fixed_ioport_reset(void *opaque)
platform_fixed_ioport_writeb(s, 0, 0);
}
-const MemoryRegionPortio xen_platform_ioport[] = {
- { 0, 16, 4, .write = platform_fixed_ioport_writel, },
- { 0, 16, 2, .write = platform_fixed_ioport_writew, },
- { 0, 16, 1, .write = platform_fixed_ioport_writeb, },
- { 0, 16, 2, .read = platform_fixed_ioport_readw, },
- { 0, 16, 1, .read = platform_fixed_ioport_readb, },
- PORTIO_END_OF_LIST()
-};
+static uint64_t platform_fixed_ioport_read(void *opaque,
+ hwaddr addr,
+ unsigned size)
+{
+ switch (size) {
+ case 1:
+ return platform_fixed_ioport_readb(opaque, addr);
+ case 2:
+ return platform_fixed_ioport_readw(opaque, addr);
+ default:
+ return -1;
+ }
+}
+
+static void platform_fixed_ioport_write(void *opaque, hwaddr addr,
+
+ uint64_t val, unsigned size)
+{
+ switch (size) {
+ case 1:
+ platform_fixed_ioport_writeb(opaque, addr, val);
+ break;
+ case 2:
+ platform_fixed_ioport_writew(opaque, addr, val);
+ break;
+ case 4:
+ platform_fixed_ioport_writel(opaque, addr, val);
+ break;
+ }
+}
+
static const MemoryRegionOps platform_fixed_io_ops = {
- .old_portio = xen_platform_ioport,
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .read = platform_fixed_ioport_read,
+ .write = platform_fixed_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static void platform_fixed_ioport_init(PCIXenPlatformState* s)
diff --git a/hw/xtensa_lx60.c b/hw/xtensa_lx60.c
index 5dd2e08332..4c42edc4ea 100644
--- a/hw/xtensa_lx60.c
+++ b/hw/xtensa_lx60.c
@@ -155,10 +155,7 @@ static void lx60_reset(void *opaque)
cpu_reset(CPU(cpu));
}
-static void lx_init(const LxBoardDesc *board,
- ram_addr_t ram_size, const char *boot_device,
- const char *kernel_filename, const char *kernel_cmdline,
- const char *initrd_filename, const char *cpu_model)
+static void lx_init(const LxBoardDesc *board, QEMUMachineInitArgs *args)
{
#ifdef TARGET_WORDS_BIGENDIAN
int be = 1;
@@ -171,6 +168,9 @@ static void lx_init(const LxBoardDesc *board,
MemoryRegion *ram, *rom, *system_io;
DriveInfo *dinfo;
pflash_t *flash = NULL;
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
int n;
if (!cpu_model) {
@@ -194,7 +194,7 @@ static void lx_init(const LxBoardDesc *board,
}
ram = g_malloc(sizeof(*ram));
- memory_region_init_ram(ram, "lx60.dram", ram_size);
+ memory_region_init_ram(ram, "lx60.dram", args->ram_size);
vmstate_register_ram_global(ram);
memory_region_add_subregion(system_memory, 0, ram);
@@ -271,38 +271,22 @@ static void lx_init(const LxBoardDesc *board,
static void xtensa_lx60_init(QEMUMachineInitArgs *args)
{
- ram_addr_t ram_size = args->ram_size;
- const char *cpu_model = args->cpu_model;
- const char *kernel_filename = args->kernel_filename;
- const char *kernel_cmdline = args->kernel_cmdline;
- const char *initrd_filename = args->initrd_filename;
- const char *boot_device = args->boot_device;
static const LxBoardDesc lx60_board = {
.flash_size = 0x400000,
.flash_sector_size = 0x10000,
.sram_size = 0x20000,
};
- lx_init(&lx60_board, ram_size, boot_device,
- kernel_filename, kernel_cmdline,
- initrd_filename, cpu_model);
+ lx_init(&lx60_board, args);
}
static void xtensa_lx200_init(QEMUMachineInitArgs *args)
{
- ram_addr_t ram_size = args->ram_size;
- const char *cpu_model = args->cpu_model;
- const char *kernel_filename = args->kernel_filename;
- const char *kernel_cmdline = args->kernel_cmdline;
- const char *initrd_filename = args->initrd_filename;
- const char *boot_device = args->boot_device;
static const LxBoardDesc lx200_board = {
.flash_size = 0x1000000,
.flash_sector_size = 0x20000,
.sram_size = 0x2000000,
};
- lx_init(&lx200_board, ram_size, boot_device,
- kernel_filename, kernel_cmdline,
- initrd_filename, cpu_model);
+ lx_init(&lx200_board, args);
}
static QEMUMachine xtensa_lx60_machine = {
diff --git a/hw/xtensa_sim.c b/hw/xtensa_sim.c
index 2e846d8aa6..0d633e4724 100644
--- a/hw/xtensa_sim.c
+++ b/hw/xtensa_sim.c
@@ -44,16 +44,20 @@ static void sim_reset(void *opaque)
cpu_reset(CPU(cpu));
}
-static void sim_init(ram_addr_t ram_size,
- const char *boot_device,
- const char *kernel_filename, const char *kernel_cmdline,
- const char *initrd_filename, const char *cpu_model)
+static void xtensa_sim_init(QEMUMachineInitArgs *args)
{
XtensaCPU *cpu = NULL;
CPUXtensaState *env = NULL;
MemoryRegion *ram, *rom;
+ ram_addr_t ram_size = args->ram_size;
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
int n;
+ if (!cpu_model) {
+ cpu_model = XTENSA_DEFAULT_CPU_MODEL;
+ }
+
for (n = 0; n < smp_cpus; n++) {
cpu = cpu_xtensa_init(cpu_model);
if (cpu == NULL) {
@@ -96,21 +100,6 @@ static void sim_init(ram_addr_t ram_size,
}
}
-static void xtensa_sim_init(QEMUMachineInitArgs *args)
-{
- ram_addr_t ram_size = args->ram_size;
- const char *cpu_model = args->cpu_model;
- const char *kernel_filename = args->kernel_filename;
- const char *kernel_cmdline = args->kernel_cmdline;
- const char *initrd_filename = args->initrd_filename;
- const char *boot_device = args->boot_device;
- if (!cpu_model) {
- cpu_model = XTENSA_DEFAULT_CPU_MODEL;
- }
- sim_init(ram_size, boot_device, kernel_filename, kernel_cmdline,
- initrd_filename, cpu_model);
-}
-
static QEMUMachine xtensa_sim_machine = {
.name = "sim",
.desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")",
diff --git a/migration-exec.c b/migration-exec.c
index 6c97db973c..519af57ac7 100644
--- a/migration-exec.c
+++ b/migration-exec.c
@@ -60,22 +60,18 @@ static int exec_close(MigrationState *s)
return ret;
}
-int exec_start_outgoing_migration(MigrationState *s, const char *command)
+void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp)
{
FILE *f;
f = popen(command, "w");
if (f == NULL) {
- DPRINTF("Unable to popen exec target\n");
- goto err_after_popen;
+ error_setg_errno(errp, errno, "failed to popen the migration target");
+ return;
}
s->fd = fileno(f);
- if (s->fd == -1) {
- DPRINTF("Unable to retrieve file descriptor for popen'd handle\n");
- goto err_after_open;
- }
-
+ assert(s->fd != -1);
socket_set_nonblock(s->fd);
s->opaque = qemu_popen(f, "w");
@@ -85,12 +81,6 @@ int exec_start_outgoing_migration(MigrationState *s, const char *command)
s->write = file_write;
migrate_fd_connect(s);
- return 0;
-
-err_after_open:
- pclose(f);
-err_after_popen:
- return -1;
}
static void exec_accept_incoming_migration(void *opaque)
@@ -102,19 +92,17 @@ static void exec_accept_incoming_migration(void *opaque)
qemu_fclose(f);
}
-int exec_start_incoming_migration(const char *command)
+void exec_start_incoming_migration(const char *command, Error **errp)
{
QEMUFile *f;
DPRINTF("Attempting to start an incoming migration\n");
f = qemu_popen_cmd(command, "r");
if(f == NULL) {
- DPRINTF("Unable to apply qemu wrapper to popen file\n");
- return -errno;
+ error_setg_errno(errp, errno, "failed to popen the migration source");
+ return;
}
qemu_set_fd_handler2(qemu_stdio_fd(f), NULL,
exec_accept_incoming_migration, NULL, f);
-
- return 0;
}
diff --git a/migration-fd.c b/migration-fd.c
index 73351678e0..ce6932d7c3 100644
--- a/migration-fd.c
+++ b/migration-fd.c
@@ -73,30 +73,19 @@ static int fd_close(MigrationState *s)
return 0;
}
-int fd_start_outgoing_migration(MigrationState *s, const char *fdname)
+void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp)
{
- s->fd = monitor_get_fd(cur_mon, fdname, NULL);
+ s->fd = monitor_get_fd(cur_mon, fdname, errp);
if (s->fd == -1) {
- DPRINTF("fd_migration: invalid file descriptor identifier\n");
- goto err_after_get_fd;
- }
-
- if (fcntl(s->fd, F_SETFL, O_NONBLOCK) == -1) {
- DPRINTF("Unable to set nonblocking mode on file descriptor\n");
- goto err_after_open;
+ return;
}
+ fcntl(s->fd, F_SETFL, O_NONBLOCK);
s->get_error = fd_errno;
s->write = fd_write;
s->close = fd_close;
migrate_fd_connect(s);
- return 0;
-
-err_after_open:
- close(s->fd);
-err_after_get_fd:
- return -1;
}
static void fd_accept_incoming_migration(void *opaque)
@@ -108,7 +97,7 @@ static void fd_accept_incoming_migration(void *opaque)
qemu_fclose(f);
}
-int fd_start_incoming_migration(const char *infd)
+void fd_start_incoming_migration(const char *infd, Error **errp)
{
int fd;
QEMUFile *f;
@@ -118,11 +107,9 @@ int fd_start_incoming_migration(const char *infd)
fd = strtol(infd, NULL, 0);
f = qemu_fdopen(fd, "rb");
if(f == NULL) {
- DPRINTF("Unable to apply qemu wrapper to file descriptor\n");
- return -errno;
+ error_setg_errno(errp, errno, "failed to open the source descriptor");
+ return;
}
qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, f);
-
- return 0;
}
diff --git a/migration-tcp.c b/migration-tcp.c
index a15c2b87a1..46f6ac545c 100644
--- a/migration-tcp.c
+++ b/migration-tcp.c
@@ -68,21 +68,13 @@ static void tcp_wait_for_connect(int fd, void *opaque)
}
}
-int tcp_start_outgoing_migration(MigrationState *s, const char *host_port,
- Error **errp)
+void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp)
{
s->get_error = socket_errno;
s->write = socket_write;
s->close = tcp_close;
- s->fd = inet_nonblocking_connect(host_port, tcp_wait_for_connect, s,
- errp);
- if (error_is_set(errp)) {
- migrate_fd_error(s);
- return -1;
- }
-
- return 0;
+ s->fd = inet_nonblocking_connect(host_port, tcp_wait_for_connect, s, errp);
}
static void tcp_accept_incoming_migration(void *opaque)
@@ -119,18 +111,15 @@ out2:
close(s);
}
-int tcp_start_incoming_migration(const char *host_port, Error **errp)
+void tcp_start_incoming_migration(const char *host_port, Error **errp)
{
int s;
s = inet_listen(host_port, NULL, 256, SOCK_STREAM, 0, errp);
-
if (s < 0) {
- return -1;
+ return;
}
qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL,
(void *)(intptr_t)s);
-
- return 0;
}
diff --git a/migration-unix.c b/migration-unix.c
index 169de88677..ed3db3a39a 100644
--- a/migration-unix.c
+++ b/migration-unix.c
@@ -53,69 +53,28 @@ static int unix_close(MigrationState *s)
return r;
}
-static void unix_wait_for_connect(void *opaque)
+static void unix_wait_for_connect(int fd, void *opaque)
{
MigrationState *s = opaque;
- int val, ret;
- socklen_t valsize = sizeof(val);
- DPRINTF("connect completed\n");
- do {
- ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
- } while (ret == -1 && errno == EINTR);
-
- if (ret < 0) {
+ if (fd < 0) {
+ DPRINTF("migrate connect error\n");
+ s->fd = -1;
migrate_fd_error(s);
- return;
- }
-
- qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
-
- if (val == 0)
+ } else {
+ DPRINTF("migrate connect success\n");
+ s->fd = fd;
migrate_fd_connect(s);
- else {
- DPRINTF("error connecting %d\n", val);
- migrate_fd_error(s);
}
}
-int unix_start_outgoing_migration(MigrationState *s, const char *path)
+void unix_start_outgoing_migration(MigrationState *s, const char *path, Error **errp)
{
- struct sockaddr_un addr;
- int ret;
-
- addr.sun_family = AF_UNIX;
- snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
s->get_error = unix_errno;
s->write = unix_write;
s->close = unix_close;
- s->fd = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
- if (s->fd == -1) {
- DPRINTF("Unable to open socket");
- return -errno;
- }
-
- socket_set_nonblock(s->fd);
-
- do {
- ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr));
- if (ret == -1) {
- ret = -errno;
- }
- if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) {
- qemu_set_fd_handler2(s->fd, NULL, NULL, unix_wait_for_connect, s);
- return 0;
- }
- } while (ret == -EINTR);
-
- if (ret < 0) {
- DPRINTF("connect failed\n");
- migrate_fd_error(s);
- return ret;
- }
- migrate_fd_connect(s);
- return 0;
+ s->fd = unix_nonblocking_connect(path, unix_wait_for_connect, s, errp);
}
static void unix_accept_incoming_migration(void *opaque)
@@ -152,43 +111,15 @@ out2:
close(s);
}
-int unix_start_incoming_migration(const char *path)
+void unix_start_incoming_migration(const char *path, Error **errp)
{
- struct sockaddr_un addr;
int s;
- int ret;
-
- DPRINTF("Attempting to start an incoming migration\n");
-
- s = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
- if (s == -1) {
- fprintf(stderr, "Could not open unix socket: %s\n", strerror(errno));
- return -errno;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
- unlink(addr.sun_path);
- if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- ret = -errno;
- fprintf(stderr, "bind(unix:%s): %s\n", addr.sun_path, strerror(errno));
- goto err;
- }
- if (listen(s, 1) == -1) {
- fprintf(stderr, "listen(unix:%s): %s\n", addr.sun_path,
- strerror(errno));
- ret = -errno;
- goto err;
+ s = unix_listen(path, NULL, 0, errp);
+ if (s < 0) {
+ return;
}
qemu_set_fd_handler2(s, NULL, unix_accept_incoming_migration, NULL,
(void *)(intptr_t)s);
-
- return 0;
-
-err:
- close(s);
- return ret;
}
diff --git a/migration.c b/migration.c
index 62e030487d..300ab75aaf 100644
--- a/migration.c
+++ b/migration.c
@@ -64,26 +64,23 @@ MigrationState *migrate_get_current(void)
return &current_migration;
}
-int qemu_start_incoming_migration(const char *uri, Error **errp)
+void qemu_start_incoming_migration(const char *uri, Error **errp)
{
const char *p;
- int ret;
if (strstart(uri, "tcp:", &p))
- ret = tcp_start_incoming_migration(p, errp);
+ tcp_start_incoming_migration(p, errp);
#if !defined(WIN32)
else if (strstart(uri, "exec:", &p))
- ret = exec_start_incoming_migration(p);
+ exec_start_incoming_migration(p, errp);
else if (strstart(uri, "unix:", &p))
- ret = unix_start_incoming_migration(p);
+ unix_start_incoming_migration(p, errp);
else if (strstart(uri, "fd:", &p))
- ret = fd_start_incoming_migration(p);
+ fd_start_incoming_migration(p, errp);
#endif
else {
- fprintf(stderr, "unknown migration protocol: %s\n", uri);
- ret = -EPROTONOSUPPORT;
+ error_setg(errp, "unknown migration protocol: %s\n", uri);
}
- return ret;
}
void process_incoming_migration(QEMUFile *f)
@@ -102,7 +99,7 @@ void process_incoming_migration(QEMUFile *f)
if (autostart) {
vm_start();
} else {
- runstate_set(RUN_STATE_PRELAUNCH);
+ runstate_set(RUN_STATE_PAUSED);
}
}
@@ -483,10 +480,10 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
bool has_inc, bool inc, bool has_detach, bool detach,
Error **errp)
{
+ Error *local_err = NULL;
MigrationState *s = migrate_get_current();
MigrationParams params;
const char *p;
- int ret;
params.blk = blk;
params.shared = inc;
@@ -508,26 +505,23 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
s = migrate_init(&params);
if (strstart(uri, "tcp:", &p)) {
- ret = tcp_start_outgoing_migration(s, p, errp);
+ tcp_start_outgoing_migration(s, p, &local_err);
#if !defined(WIN32)
} else if (strstart(uri, "exec:", &p)) {
- ret = exec_start_outgoing_migration(s, p);
+ exec_start_outgoing_migration(s, p, &local_err);
} else if (strstart(uri, "unix:", &p)) {
- ret = unix_start_outgoing_migration(s, p);
+ unix_start_outgoing_migration(s, p, &local_err);
} else if (strstart(uri, "fd:", &p)) {
- ret = fd_start_outgoing_migration(s, p);
+ fd_start_outgoing_migration(s, p, &local_err);
#endif
} else {
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol");
return;
}
- if (ret < 0) {
- if (!error_is_set(errp)) {
- DPRINTF("migration failed: %s\n", strerror(-ret));
- /* FIXME: we should return meaningful errors */
- error_set(errp, QERR_UNDEFINED_ERROR);
- }
+ if (local_err) {
+ migrate_fd_error(s);
+ error_propagate(errp, local_err);
return;
}
diff --git a/migration.h b/migration.h
index 1c3e9b750e..c3a23cc6c8 100644
--- a/migration.h
+++ b/migration.h
@@ -49,7 +49,7 @@ struct MigrationState
void process_incoming_migration(QEMUFile *f);
-int qemu_start_incoming_migration(const char *uri, Error **errp);
+void qemu_start_incoming_migration(const char *uri, Error **errp);
uint64_t migrate_max_downtime(void);
@@ -57,22 +57,21 @@ void do_info_migrate_print(Monitor *mon, const QObject *data);
void do_info_migrate(Monitor *mon, QObject **ret_data);
-int exec_start_incoming_migration(const char *host_port);
+void exec_start_incoming_migration(const char *host_port, Error **errp);
-int exec_start_outgoing_migration(MigrationState *s, const char *host_port);
+void exec_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp);
-int tcp_start_incoming_migration(const char *host_port, Error **errp);
+void tcp_start_incoming_migration(const char *host_port, Error **errp);
-int tcp_start_outgoing_migration(MigrationState *s, const char *host_port,
- Error **errp);
+void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp);
-int unix_start_incoming_migration(const char *path);
+void unix_start_incoming_migration(const char *path, Error **errp);
-int unix_start_outgoing_migration(MigrationState *s, const char *path);
+void unix_start_outgoing_migration(MigrationState *s, const char *path, Error **errp);
-int fd_start_incoming_migration(const char *path);
+void fd_start_incoming_migration(const char *path, Error **errp);
-int fd_start_outgoing_migration(MigrationState *s, const char *fdname);
+void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp);
void migrate_fd_error(MigrationState *s);
diff --git a/monitor.c b/monitor.c
index d17ae2d3b1..eeef32e38b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -451,6 +451,7 @@ static const char *monitor_event_names[] = {
[QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED",
[QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED",
[QEVENT_BLOCK_JOB_ERROR] = "BLOCK_JOB_ERROR",
+ [QEVENT_BLOCK_JOB_READY] = "BLOCK_JOB_READY",
[QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED",
[QEVENT_SUSPEND] = "SUSPEND",
[QEVENT_SUSPEND_DISK] = "SUSPEND_DISK",
@@ -2105,8 +2106,9 @@ static void monitor_fdset_cleanup(MonFdset *mon_fdset)
MonFdsetFd *mon_fdset_fd_next;
QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) {
- if (mon_fdset_fd->removed ||
- (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) {
+ if ((mon_fdset_fd->removed ||
+ (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) &&
+ runstate_is_running()) {
close(mon_fdset_fd->fd);
g_free(mon_fdset_fd->opaque);
QLIST_REMOVE(mon_fdset_fd, next);
@@ -2135,8 +2137,6 @@ AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque,
{
int fd;
Monitor *mon = cur_mon;
- MonFdset *mon_fdset;
- MonFdsetFd *mon_fdset_fd;
AddfdInfo *fdinfo;
fd = qemu_chr_fe_get_msgfd(mon->chr);
@@ -2145,57 +2145,11 @@ AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque,
goto error;
}
- if (has_fdset_id) {
- QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
- if (mon_fdset->id == fdset_id) {
- break;
- }
- }
- if (mon_fdset == NULL) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
- "an existing fdset-id");
- goto error;
- }
- } else {
- int64_t fdset_id_prev = -1;
- MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets);
-
- /* Use first available fdset ID */
- QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
- mon_fdset_cur = mon_fdset;
- if (fdset_id_prev == mon_fdset_cur->id - 1) {
- fdset_id_prev = mon_fdset_cur->id;
- continue;
- }
- break;
- }
-
- mon_fdset = g_malloc0(sizeof(*mon_fdset));
- mon_fdset->id = fdset_id_prev + 1;
-
- /* The fdset list is ordered by fdset ID */
- if (mon_fdset->id == 0) {
- QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next);
- } else if (mon_fdset->id < mon_fdset_cur->id) {
- QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next);
- } else {
- QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next);
- }
- }
-
- mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd));
- mon_fdset_fd->fd = fd;
- mon_fdset_fd->removed = false;
- if (has_opaque) {
- mon_fdset_fd->opaque = g_strdup(opaque);
+ fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id,
+ has_opaque, opaque, errp);
+ if (fdinfo) {
+ return fdinfo;
}
- QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next);
-
- fdinfo = g_malloc0(sizeof(*fdinfo));
- fdinfo->fdset_id = mon_fdset->id;
- fdinfo->fd = mon_fdset_fd->fd;
-
- return fdinfo;
error:
if (fd != -1) {
@@ -2281,6 +2235,87 @@ FdsetInfoList *qmp_query_fdsets(Error **errp)
return fdset_list;
}
+AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
+ bool has_opaque, const char *opaque,
+ Error **errp)
+{
+ MonFdset *mon_fdset = NULL;
+ MonFdsetFd *mon_fdset_fd;
+ AddfdInfo *fdinfo;
+
+ if (has_fdset_id) {
+ QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
+ /* Break if match found or match impossible due to ordering by ID */
+ if (fdset_id <= mon_fdset->id) {
+ if (fdset_id < mon_fdset->id) {
+ mon_fdset = NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ if (mon_fdset == NULL) {
+ int64_t fdset_id_prev = -1;
+ MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets);
+
+ if (has_fdset_id) {
+ if (fdset_id < 0) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
+ "a non-negative value");
+ return NULL;
+ }
+ /* Use specified fdset ID */
+ QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
+ mon_fdset_cur = mon_fdset;
+ if (fdset_id < mon_fdset_cur->id) {
+ break;
+ }
+ }
+ } else {
+ /* Use first available fdset ID */
+ QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
+ mon_fdset_cur = mon_fdset;
+ if (fdset_id_prev == mon_fdset_cur->id - 1) {
+ fdset_id_prev = mon_fdset_cur->id;
+ continue;
+ }
+ break;
+ }
+ }
+
+ mon_fdset = g_malloc0(sizeof(*mon_fdset));
+ if (has_fdset_id) {
+ mon_fdset->id = fdset_id;
+ } else {
+ mon_fdset->id = fdset_id_prev + 1;
+ }
+
+ /* The fdset list is ordered by fdset ID */
+ if (!mon_fdset_cur) {
+ QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next);
+ } else if (mon_fdset->id < mon_fdset_cur->id) {
+ QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next);
+ } else {
+ QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next);
+ }
+ }
+
+ mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd));
+ mon_fdset_fd->fd = fd;
+ mon_fdset_fd->removed = false;
+ if (has_opaque) {
+ mon_fdset_fd->opaque = g_strdup(opaque);
+ }
+ QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next);
+
+ fdinfo = g_malloc0(sizeof(*fdinfo));
+ fdinfo->fdset_id = mon_fdset->id;
+ fdinfo->fd = mon_fdset_fd->fd;
+
+ return fdinfo;
+}
+
int monitor_fdset_get_fd(int64_t fdset_id, int flags)
{
#ifndef _WIN32
diff --git a/monitor.h b/monitor.h
index b6e7d95a30..b4ef9559dc 100644
--- a/monitor.h
+++ b/monitor.h
@@ -39,6 +39,7 @@ typedef enum MonitorEvent {
QEVENT_BLOCK_JOB_COMPLETED,
QEVENT_BLOCK_JOB_CANCELLED,
QEVENT_BLOCK_JOB_ERROR,
+ QEVENT_BLOCK_JOB_READY,
QEVENT_DEVICE_TRAY_MOVED,
QEVENT_SUSPEND,
QEVENT_SUSPEND_DISK,
@@ -90,6 +91,9 @@ int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret);
int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret);
+AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
+ bool has_opaque, const char *opaque,
+ Error **errp);
int monitor_fdset_get_fd(int64_t fdset_id, int flags);
int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd);
int monitor_fdset_dup_fd_remove(int dup_fd);
diff --git a/nbd.c b/nbd.c
index 6f0db62deb..cec5a9449b 100644
--- a/nbd.c
+++ b/nbd.c
@@ -208,7 +208,14 @@ int tcp_socket_outgoing(const char *address, uint16_t port)
int tcp_socket_outgoing_spec(const char *address_and_port)
{
- return inet_connect(address_and_port, NULL);
+ Error *local_err = NULL;
+ int fd = inet_connect(address_and_port, &local_err);
+
+ if (local_err != NULL) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
+ return fd;
}
int tcp_socket_incoming(const char *address, uint16_t port)
@@ -220,22 +227,38 @@ int tcp_socket_incoming(const char *address, uint16_t port)
int tcp_socket_incoming_spec(const char *address_and_port)
{
- char *ostr = NULL;
- int olen = 0;
- return inet_listen(address_and_port, ostr, olen, SOCK_STREAM, 0, NULL);
+ Error *local_err = NULL;
+ int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err);
+
+ if (local_err != NULL) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
+ return fd;
}
int unix_socket_incoming(const char *path)
{
- char *ostr = NULL;
- int olen = 0;
+ Error *local_err = NULL;
+ int fd = unix_listen(path, NULL, 0, &local_err);
- return unix_listen(path, ostr, olen);
+ if (local_err != NULL) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
+ return fd;
}
int unix_socket_outgoing(const char *path)
{
- return unix_connect(path);
+ Error *local_err = NULL;
+ int fd = unix_connect(path, &local_err);
+
+ if (local_err != NULL) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
+ return fd;
}
/* Basic flow for negotiation
diff --git a/osdep.c b/osdep.c
index 3b25297a25..c822a01c04 100644
--- a/osdep.c
+++ b/osdep.c
@@ -88,7 +88,6 @@ static int qemu_dup_flags(int fd, int flags)
int ret;
int serrno;
int dup_flags;
- int setfl_flags;
#ifdef F_DUPFD_CLOEXEC
ret = fcntl(fd, F_DUPFD_CLOEXEC, 0);
@@ -113,16 +112,7 @@ static int qemu_dup_flags(int fd, int flags)
}
/* Set/unset flags that we can with fcntl */
- setfl_flags = O_APPEND | O_ASYNC | O_NONBLOCK;
-#ifdef O_NOATIME
- setfl_flags |= O_NOATIME;
-#endif
-#ifdef O_DIRECT
- setfl_flags |= O_DIRECT;
-#endif
- dup_flags &= ~setfl_flags;
- dup_flags |= (flags & setfl_flags);
- if (fcntl(ret, F_SETFL, dup_flags) == -1) {
+ if (fcntl(ret, F_SETFL, flags) == -1) {
goto fail;
}
diff --git a/osdep.h b/osdep.h
index c5fd3d91ff..585e2c1787 100644
--- a/osdep.h
+++ b/osdep.h
@@ -121,6 +121,7 @@ void qemu_vfree(void *ptr);
#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID
#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
#define QEMU_MADV_DONTDUMP QEMU_MADV_INVALID
+#define QEMU_MADV_HUGEPAGE QEMU_MADV_INVALID
#else /* no-op */
@@ -129,6 +130,7 @@ void qemu_vfree(void *ptr);
#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID
#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
#define QEMU_MADV_DONTDUMP QEMU_MADV_INVALID
+#define QEMU_MADV_HUGEPAGE QEMU_MADV_INVALID
#endif
diff --git a/qapi-schema.json b/qapi-schema.json
index c615ee212d..542e3ac069 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -22,15 +22,11 @@
# @KVMMissingCap: the requested operation can't be fulfilled because a
# required KVM capability is missing
#
-# @MigrationExpected: the requested operation can't be fulfilled because a
-# migration process is expected
-#
# Since: 1.2
##
{ 'enum': 'ErrorClass',
'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted',
- 'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap',
- 'MigrationExpected' ] }
+ 'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
##
# @add_client
@@ -149,7 +145,11 @@
#
# @finish-migrate: guest is paused to finish the migration process
#
-# @inmigrate: guest is paused waiting for an incoming migration
+# @inmigrate: guest is paused waiting for an incoming migration. Note
+# that this state does not tell whether the machine will start at the
+# end of the migration. This depends on the command-line -S option and
+# any invocation of 'stop' or 'cont' that has happened since QEMU was
+# started.
#
# @internal-error: An internal error that prevents further guest execution
# has occurred
@@ -661,6 +661,18 @@
{ 'enum': 'BlockDeviceIoStatus', 'data': [ 'ok', 'failed', 'nospace' ] }
##
+# @BlockDirtyInfo:
+#
+# Block dirty bitmap information.
+#
+# @count: number of dirty bytes according to the dirty bitmap
+#
+# Since: 1.3
+##
+{ 'type': 'BlockDirtyInfo',
+ 'data': {'count': 'int'} }
+
+##
# @BlockInfo:
#
# Block device information. This structure describes a virtual device and
@@ -679,6 +691,9 @@
# @tray_open: #optional True if the device has a tray and it is open
# (only present if removable is true)
#
+# @dirty: #optional dirty bitmap information (only present if the dirty
+# bitmap is enabled)
+#
# @io-status: #optional @BlockDeviceIoStatus. Only present if the device
# supports it and the VM is configured to stop on errors
#
@@ -690,7 +705,8 @@
{ 'type': 'BlockInfo',
'data': {'device': 'str', 'type': 'str', 'removable': 'bool',
'locked': 'bool', '*inserted': 'BlockDeviceInfo',
- '*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus'} }
+ '*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus',
+ '*dirty': 'BlockDirtyInfo' } }
##
# @query-block:
@@ -1150,6 +1166,23 @@
'data': ['report', 'ignore', 'enospc', 'stop'] }
##
+# @MirrorSyncMode:
+#
+# An enumeration of possible behaviors for the initial synchronization
+# phase of storage mirroring.
+#
+# @top: copies data in the topmost image to the destination
+#
+# @full: copies data from all images to the destination
+#
+# @none: only copy data written from now on
+#
+# Since: 1.3
+##
+{ 'enum': 'MirrorSyncMode',
+ 'data': ['top', 'full', 'none'] }
+
+##
# @BlockJobInfo:
#
# Information about a long-running block device operation.
@@ -1210,7 +1243,9 @@
# Since: 0.14.0
#
# Notes: This function will succeed even if the guest is already in the stopped
-# state
+# state. In "inmigrate" state, it will ensure that the guest
+# remains paused once migration finishes, as if the -S option was
+# passed on the command line.
##
{ 'command': 'stop' }
@@ -1299,11 +1334,14 @@
# Since: 0.14.0
#
# Returns: If successful, nothing
-# If the QEMU is waiting for an incoming migration, MigrationExpected
# If QEMU was started with an encrypted block device and a key has
# not yet been set, DeviceEncrypted.
#
-# Notes: This command will succeed if the guest is currently running.
+# Notes: This command will succeed if the guest is currently running. It
+# will also succeed if the guest is in the "inmigrate" state; in
+# this case, the effect of the command is to make sure the guest
+# starts once migration finishes, removing the effect of the -S
+# command line option if it was passed.
##
{ 'command': 'cont' }
@@ -1573,6 +1611,49 @@
'data': { 'device': 'str', '*base': 'str', 'top': 'str',
'*speed': 'int' } }
+##
+# @drive-mirror
+#
+# Start mirroring a block device's writes to a new destination.
+#
+# @device: the name of the device whose writes should be mirrored.
+#
+# @target: the target of the new image. If the file exists, or if it
+# is a device, the existing file/device will be used as the new
+# destination. If it does not exist, a new file will be created.
+#
+# @format: #optional the format of the new destination, default is to
+# probe if @mode is 'existing', else the format of the source
+#
+# @mode: #optional whether and how QEMU should create a new image, default is
+# 'absolute-paths'.
+#
+# @speed: #optional the maximum speed, in bytes per second
+#
+# @sync: what parts of the disk image should be copied to the destination
+# (all the disk, only the sectors allocated in the topmost image, or
+# only new I/O).
+#
+# @on-source-error: #optional the action to take on an error on the source,
+# default 'report'. 'stop' and 'enospc' can only be used
+# if the block device supports io-status (see BlockInfo).
+#
+# @on-target-error: #optional the action to take on an error on the target,
+# default 'report' (no limitations, since this applies to
+# a different block device than @device).
+#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Since 1.3
+##
+{ 'command': 'drive-mirror',
+ 'data': { 'device': 'str', 'target': 'str', '*format': 'str',
+ 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
+ '*speed': 'int', '*on-source-error': 'BlockdevOnError',
+ '*on-target-error': 'BlockdevOnError' } }
+
+##
# @migrate_cancel
#
# Cancel the current executing migration process.
@@ -2017,6 +2098,32 @@
{ 'command': 'block-job-resume', 'data': { 'device': 'str' } }
##
+# @block-job-complete:
+#
+# Manually trigger completion of an active background block operation. This
+# is supported for drive mirroring, where it also switches the device to
+# write to the target path only. The ability to complete is signaled with
+# a BLOCK_JOB_READY event.
+#
+# This command completes an active background block operation synchronously.
+# The ordering of this command's return with the BLOCK_JOB_COMPLETED event
+# is not defined. Note that if an I/O error occurs during the processing of
+# this command: 1) the command itself will fail; 2) the error will be processed
+# according to the rerror/werror arguments that were specified when starting
+# the operation.
+#
+# A cancelled or paused job cannot be completed.
+#
+# @device: the device name
+#
+# Returns: Nothing on success
+# If no background operation is active on this device, DeviceNotActive
+#
+# Since: 1.3
+##
+{ 'command': 'block-job-complete', 'data': { 'device': 'str' } }
+
+##
# @ObjectTypeInfo:
#
# This structure describes a search result from @qom-list-types
@@ -2519,6 +2626,59 @@
'opts': 'NetClientOptions' } }
##
+# @InetSocketAddress
+#
+# Captures a socket address or address range in the Internet namespace.
+#
+# @host: host part of the address
+#
+# @port: port part of the address, or lowest port if @to is present
+#
+# @to: highest port to try
+#
+# @ipv4: whether to accept IPv4 addresses, default try both IPv4 and IPv6
+# #optional
+#
+# @ipv6: whether to accept IPv6 addresses, default try both IPv4 and IPv6
+# #optional
+#
+# Since 1.3
+##
+{ 'type': 'InetSocketAddress',
+ 'data': {
+ 'host': 'str',
+ 'port': 'str',
+ '*to': 'uint16',
+ '*ipv4': 'bool',
+ '*ipv6': 'bool' } }
+
+##
+# @UnixSocketAddress
+#
+# Captures a socket address in the local ("Unix socket") namespace.
+#
+# @path: filesystem path to use
+#
+# Since 1.3
+##
+{ 'type': 'UnixSocketAddress',
+ 'data': {
+ 'path': 'str' } }
+
+##
+# @SocketAddress
+#
+# Captures the address of a socket, which could also be a named file descriptor
+#
+# Since 1.3
+##
+{ 'union': 'SocketAddress',
+ 'data': {
+ 'inet': 'InetSocketAddress',
+ 'unix': 'UnixSocketAddress',
+ 'fd': 'String' } }
+
+##
# @getfd:
#
# Receive a file descriptor via SCM rights and assign it a name
@@ -2625,7 +2785,7 @@
#
# Returns: @AddfdInfo on success
# If file descriptor was not received, FdNotSupplied
-# If @fdset-id does not exist, InvalidParameterValue
+# If @fdset-id is a negative value, InvalidParameterValue
#
# Notes: The list of fd sets is shared by all monitor connections.
#
@@ -2810,3 +2970,46 @@
# Since: 0.14.0
##
{ 'command': 'screendump', 'data': {'filename': 'str'} }
+
+##
+# @nbd-server-start:
+#
+# Start an NBD server listening on the given host and port. Block
+# devices can then be exported using @nbd-server-add. The NBD
+# server will present them as named exports; for example, another
+# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
+#
+# @addr: Address on which to listen.
+#
+# Returns: error if the server is already running.
+#
+# Since: 1.3.0
+##
+{ 'command': 'nbd-server-start',
+ 'data': { 'addr': 'SocketAddress' } }
+
+##
+# @nbd-server-add:
+#
+# Export a device to QEMU's embedded NBD server.
+#
+# @device: Block device to be exported
+#
+# @writable: Whether clients should be able to write to the device via the
+# NBD connection (default false). #optional
+#
+# Returns: error if the device is already marked for export.
+#
+# Since: 1.3.0
+##
+{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool'} }
+
+##
+# @nbd-server-stop:
+#
+# Stop QEMU's embedded NBD server, and unregister all devices previously
+# added via @nbd-server-add.
+#
+# Since: 1.3.0
+##
+{ 'command': 'nbd-server-stop' }
diff --git a/qemu-char.c b/qemu-char.c
index b082bae11b..afe2bfb4dd 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2097,14 +2097,14 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
+ Error *local_err = NULL;
int fd = -1;
chr = g_malloc0(sizeof(CharDriverState));
s = g_malloc0(sizeof(NetCharDriver));
- fd = inet_dgram_opts(opts);
+ fd = inet_dgram_opts(opts, &local_err);
if (fd < 0) {
- fprintf(stderr, "inet_dgram_opts failed\n");
goto return_err;
}
@@ -2118,6 +2118,10 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
return chr;
return_err:
+ if (local_err) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
g_free(chr);
g_free(s);
if (fd >= 0) {
@@ -2428,6 +2432,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
{
CharDriverState *chr = NULL;
TCPCharDriver *s = NULL;
+ Error *local_err = NULL;
int fd = -1;
int is_listen;
int is_waitconnect;
@@ -2448,15 +2453,15 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
if (is_unix) {
if (is_listen) {
- fd = unix_listen_opts(opts);
+ fd = unix_listen_opts(opts, &local_err);
} else {
- fd = unix_connect_opts(opts);
+ fd = unix_connect_opts(opts, &local_err, NULL, NULL);
}
} else {
if (is_listen) {
- fd = inet_listen_opts(opts, 0, NULL);
+ fd = inet_listen_opts(opts, 0, &local_err);
} else {
- fd = inet_connect_opts(opts, NULL, NULL, NULL);
+ fd = inet_connect_opts(opts, &local_err, NULL, NULL);
}
}
if (fd < 0) {
@@ -2517,8 +2522,13 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
return chr;
fail:
- if (fd >= 0)
+ if (local_err) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
+ if (fd >= 0) {
closesocket(fd);
+ }
g_free(s);
g_free(chr);
return NULL;
diff --git a/qemu-config.c b/qemu-config.c
index cd1ec2165a..e854fffe19 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -619,6 +619,10 @@ static QemuOptsList qemu_machine_opts = {
.name = "mem-merge",
.type = QEMU_OPT_BOOL,
.help = "enable/disable memory merge support",
+ },{
+ .name = "usb",
+ .type = QEMU_OPT_BOOL,
+ .help = "Set on/off to enable/disable usb",
},
{ /* End of list */ }
},
@@ -653,6 +657,27 @@ QemuOptsList qemu_boot_opts = {
},
};
+static QemuOptsList qemu_add_fd_opts = {
+ .name = "add-fd",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_add_fd_opts.head),
+ .desc = {
+ {
+ .name = "fd",
+ .type = QEMU_OPT_NUMBER,
+ .help = "file descriptor of which a duplicate is added to fd set",
+ },{
+ .name = "set",
+ .type = QEMU_OPT_NUMBER,
+ .help = "ID of the fd set to add fd to",
+ },{
+ .name = "opaque",
+ .type = QEMU_OPT_STRING,
+ .help = "free-form string used to describe fd",
+ },
+ { /* end of list */ }
+ },
+};
+
static QemuOptsList *vm_config_groups[32] = {
&qemu_drive_opts,
&qemu_chardev_opts,
@@ -669,6 +694,7 @@ static QemuOptsList *vm_config_groups[32] = {
&qemu_boot_opts,
&qemu_iscsi_opts,
&qemu_sandbox_opts,
+ &qemu_add_fd_opts,
NULL,
};
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 0ef82e9ac7..a18136302d 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -34,9 +34,9 @@ STEXI
ETEXI
DEF("info", img_info,
- "info [-f fmt] [--output=ofmt] filename")
+ "info [-f fmt] [--output=ofmt] [--backing-chain] filename")
STEXI
-@item info [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
+@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
ETEXI
DEF("snapshot", img_snapshot,
diff --git a/qemu-img.c b/qemu-img.c
index f17f1872d6..b17bddd25c 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -674,7 +674,7 @@ static int img_convert(int argc, char **argv)
QEMUOptionParameter *out_baseimg_param;
char *options = NULL;
const char *snapshot_name = NULL;
- float local_progress;
+ float local_progress = 0;
int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */
fmt = NULL;
@@ -914,8 +914,10 @@ static int img_convert(int argc, char **argv)
sector_num = 0;
nb_sectors = total_sectors;
- local_progress = (float)100 /
- (nb_sectors / MIN(nb_sectors, cluster_sectors));
+ if (nb_sectors != 0) {
+ local_progress = (float)100 /
+ (nb_sectors / MIN(nb_sectors, cluster_sectors));
+ }
for(;;) {
int64_t bs_num;
@@ -986,8 +988,10 @@ static int img_convert(int argc, char **argv)
sector_num = 0; // total number of sectors converted so far
nb_sectors = total_sectors - sector_num;
- local_progress = (float)100 /
- (nb_sectors / MIN(nb_sectors, IO_BUF_SIZE / 512));
+ if (nb_sectors != 0) {
+ local_progress = (float)100 /
+ (nb_sectors / MIN(nb_sectors, IO_BUF_SIZE / 512));
+ }
for(;;) {
nb_sectors = total_sectors - sector_num;
@@ -1108,6 +1112,23 @@ static void dump_snapshots(BlockDriverState *bs)
g_free(sn_tab);
}
+static void dump_json_image_info_list(ImageInfoList *list)
+{
+ Error *errp = NULL;
+ QString *str;
+ QmpOutputVisitor *ov = qmp_output_visitor_new();
+ QObject *obj;
+ visit_type_ImageInfoList(qmp_output_get_visitor(ov),
+ &list, NULL, &errp);
+ obj = qmp_output_get_qobject(ov);
+ str = qobject_to_json_pretty(obj);
+ assert(str != NULL);
+ printf("%s\n", qstring_get_str(str));
+ qobject_decref(obj);
+ qmp_output_visitor_cleanup(ov);
+ QDECREF(str);
+}
+
static void collect_snapshots(BlockDriverState *bs , ImageInfo *info)
{
int i, sn_count;
@@ -1247,9 +1268,129 @@ static void dump_human_image_info(ImageInfo *info)
printf("backing file format: %s\n", info->backing_filename_format);
}
}
+
+ if (info->has_snapshots) {
+ SnapshotInfoList *elem;
+ char buf[256];
+
+ printf("Snapshot list:\n");
+ printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+
+ /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but
+ * we convert to the block layer's native QEMUSnapshotInfo for now.
+ */
+ for (elem = info->snapshots; elem; elem = elem->next) {
+ QEMUSnapshotInfo sn = {
+ .vm_state_size = elem->value->vm_state_size,
+ .date_sec = elem->value->date_sec,
+ .date_nsec = elem->value->date_nsec,
+ .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL +
+ elem->value->vm_clock_nsec,
+ };
+
+ pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id);
+ pstrcpy(sn.name, sizeof(sn.name), elem->value->name);
+ printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), &sn));
+ }
+ }
}
-enum {OPTION_OUTPUT = 256};
+static void dump_human_image_info_list(ImageInfoList *list)
+{
+ ImageInfoList *elem;
+ bool delim = false;
+
+ for (elem = list; elem; elem = elem->next) {
+ if (delim) {
+ printf("\n");
+ }
+ delim = true;
+
+ dump_human_image_info(elem->value);
+ }
+}
+
+static gboolean str_equal_func(gconstpointer a, gconstpointer b)
+{
+ return strcmp(a, b) == 0;
+}
+
+/**
+ * Open an image file chain and return an ImageInfoList
+ *
+ * @filename: topmost image filename
+ * @fmt: topmost image format (may be NULL to autodetect)
+ * @chain: true - enumerate entire backing file chain
+ * false - only topmost image file
+ *
+ * Returns a list of ImageInfo objects or NULL if there was an error opening an
+ * image file. If there was an error a message will have been printed to
+ * stderr.
+ */
+static ImageInfoList *collect_image_info_list(const char *filename,
+ const char *fmt,
+ bool chain)
+{
+ ImageInfoList *head = NULL;
+ ImageInfoList **last = &head;
+ GHashTable *filenames;
+
+ filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL);
+
+ while (filename) {
+ BlockDriverState *bs;
+ ImageInfo *info;
+ ImageInfoList *elem;
+
+ if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
+ error_report("Backing file '%s' creates an infinite loop.",
+ filename);
+ goto err;
+ }
+ g_hash_table_insert(filenames, (gpointer)filename, NULL);
+
+ bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_NO_BACKING,
+ false);
+ if (!bs) {
+ goto err;
+ }
+
+ info = g_new0(ImageInfo, 1);
+ collect_image_info(bs, info, filename, fmt);
+ collect_snapshots(bs, info);
+
+ elem = g_new0(ImageInfoList, 1);
+ elem->value = info;
+ *last = elem;
+ last = &elem->next;
+
+ bdrv_delete(bs);
+
+ filename = fmt = NULL;
+ if (chain) {
+ if (info->has_full_backing_filename) {
+ filename = info->full_backing_filename;
+ } else if (info->has_backing_filename) {
+ filename = info->backing_filename;
+ }
+ if (info->has_backing_filename_format) {
+ fmt = info->backing_filename_format;
+ }
+ }
+ }
+ g_hash_table_destroy(filenames);
+ return head;
+
+err:
+ qapi_free_ImageInfoList(head);
+ g_hash_table_destroy(filenames);
+ return NULL;
+}
+
+enum {
+ OPTION_OUTPUT = 256,
+ OPTION_BACKING_CHAIN = 257,
+};
typedef enum OutputFormat {
OFORMAT_JSON,
@@ -1260,9 +1401,9 @@ static int img_info(int argc, char **argv)
{
int c;
OutputFormat output_format = OFORMAT_HUMAN;
+ bool chain = false;
const char *filename, *fmt, *output;
- BlockDriverState *bs;
- ImageInfo *info;
+ ImageInfoList *list;
fmt = NULL;
output = NULL;
@@ -1272,6 +1413,7 @@ static int img_info(int argc, char **argv)
{"help", no_argument, 0, 'h'},
{"format", required_argument, 0, 'f'},
{"output", required_argument, 0, OPTION_OUTPUT},
+ {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "f:h",
@@ -1290,6 +1432,9 @@ static int img_info(int argc, char **argv)
case OPTION_OUTPUT:
output = optarg;
break;
+ case OPTION_BACKING_CHAIN:
+ chain = true;
+ break;
}
}
if (optind >= argc) {
@@ -1306,27 +1451,25 @@ static int img_info(int argc, char **argv)
return 1;
}
- bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_NO_BACKING, false);
- if (!bs) {
+ list = collect_image_info_list(filename, fmt, chain);
+ if (!list) {
return 1;
}
- info = g_new0(ImageInfo, 1);
- collect_image_info(bs, info, filename, fmt);
-
switch (output_format) {
case OFORMAT_HUMAN:
- dump_human_image_info(info);
- dump_snapshots(bs);
+ dump_human_image_info_list(list);
break;
case OFORMAT_JSON:
- collect_snapshots(bs, info);
- dump_json_image_info(info);
+ if (chain) {
+ dump_json_image_info_list(list);
+ } else {
+ dump_json_image_info(list->value);
+ }
break;
}
- qapi_free_ImageInfo(info);
- bdrv_delete(bs);
+ qapi_free_ImageInfoList(list);
return 0;
}
@@ -1558,13 +1701,15 @@ static int img_rebase(int argc, char **argv)
error_report("Could not open old backing file '%s'", backing_name);
goto out;
}
-
- bs_new_backing = bdrv_new("new_backing");
- ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS,
+ if (out_baseimg[0]) {
+ bs_new_backing = bdrv_new("new_backing");
+ ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS,
new_backing_drv);
- if (ret) {
- error_report("Could not open new backing file '%s'", out_baseimg);
- goto out;
+ if (ret) {
+ error_report("Could not open new backing file '%s'",
+ out_baseimg);
+ goto out;
+ }
}
}
@@ -1580,22 +1725,27 @@ static int img_rebase(int argc, char **argv)
if (!unsafe) {
uint64_t num_sectors;
uint64_t old_backing_num_sectors;
- uint64_t new_backing_num_sectors;
+ uint64_t new_backing_num_sectors = 0;
uint64_t sector;
int n;
uint8_t * buf_old;
uint8_t * buf_new;
- float local_progress;
+ float local_progress = 0;
buf_old = qemu_blockalign(bs, IO_BUF_SIZE);
buf_new = qemu_blockalign(bs, IO_BUF_SIZE);
bdrv_get_geometry(bs, &num_sectors);
bdrv_get_geometry(bs_old_backing, &old_backing_num_sectors);
- bdrv_get_geometry(bs_new_backing, &new_backing_num_sectors);
+ if (bs_new_backing) {
+ bdrv_get_geometry(bs_new_backing, &new_backing_num_sectors);
+ }
+
+ if (num_sectors != 0) {
+ local_progress = (float)100 /
+ (num_sectors / MIN(num_sectors, IO_BUF_SIZE / 512));
+ }
- local_progress = (float)100 /
- (num_sectors / MIN(num_sectors, IO_BUF_SIZE / 512));
for (sector = 0; sector < num_sectors; sector += n) {
/* How many sectors can we handle with the next read? */
@@ -1629,7 +1779,7 @@ static int img_rebase(int argc, char **argv)
}
}
- if (sector >= new_backing_num_sectors) {
+ if (sector >= new_backing_num_sectors || !bs_new_backing) {
memset(buf_new, 0, n * BDRV_SECTOR_SIZE);
} else {
if (sector + n > new_backing_num_sectors) {
@@ -1675,7 +1825,12 @@ static int img_rebase(int argc, char **argv)
* backing file are overwritten in the COW file now, so the visible content
* doesn't change when we switch the backing file.
*/
- ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt);
+ if (out_baseimg && *out_baseimg) {
+ ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt);
+ } else {
+ ret = bdrv_change_backing_file(bs, NULL, NULL);
+ }
+
if (ret == -ENOSPC) {
error_report("Could not change the backing file to '%s': No "
"space left in the file header", out_baseimg);
diff --git a/qemu-img.texi b/qemu-img.texi
index 8b05f2c428..60b83fc11a 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -28,6 +28,10 @@ Command parameters:
is the disk image format. It is guessed automatically in most cases. See below
for a description of the supported disk formats.
+@item --backing-chain
+will enumerate information about backing files in a disk image chain. Refer
+below for further description.
+
@item size
is the disk image size in bytes. Optional suffixes @code{k} or @code{K}
(kilobyte, 1024) @code{M} (megabyte, 1024k) and @code{G} (gigabyte, 1024M)
@@ -129,7 +133,7 @@ created as a copy on write image of the specified base image; the
@var{backing_file} should have the same content as the input's base image,
however the path, image format, etc may differ.
-@item info [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
+@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
Give information about the disk image @var{filename}. Use it in
particular to know the size reserved on disk which can be different
@@ -137,6 +141,21 @@ from the displayed size. If VM snapshots are stored in the disk image,
they are displayed too. The command can output in the format @var{ofmt}
which is either @code{human} or @code{json}.
+If a disk image has a backing file chain, information about each disk image in
+the chain can be recursively enumerated by using the option @code{--backing-chain}.
+
+For instance, if you have an image chain like:
+
+@example
+base.qcow2 <- snap1.qcow2 <- snap2.qcow2
+@end example
+
+To enumerate information about each disk image in the above chain, starting from top to base, do:
+
+@example
+qemu-img info --backing-chain snap2.qcow2
+@end example
+
@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename}
List, apply, create or delete snapshots in image @var{filename}.
@@ -148,7 +167,9 @@ Changes the backing file of an image. Only the formats @code{qcow2} and
The backing file is changed to @var{backing_file} and (if the image format of
@var{filename} supports this) the backing file format is changed to
-@var{backing_fmt}.
+@var{backing_fmt}. If @var{backing_file} is specified as ``'' (the empty
+string), then the image is rebased onto no backing file (i.e. it will exist
+independently of any backing file).
There are two different modes in which @code{rebase} can operate:
@table @option
diff --git a/qemu-options.hx b/qemu-options.hx
index 46f0539182..a67a2551e5 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -253,6 +253,14 @@ qemu-system-i386 -drive file=file,index=2,media=disk
qemu-system-i386 -drive file=file,index=3,media=disk
@end example
+You can open an image using pre-opened file descriptors from an fd set:
+@example
+qemu-system-i386
+-add-fd fd=3,set=2,opaque="rdwr:/path/to/file"
+-add-fd fd=4,set=2,opaque="rdonly:/path/to/file"
+-drive file=/dev/fdset/2,index=0,media=disk
+@end example
+
You can connect a CDROM to the slave of ide0:
@example
qemu-system-i386 -drive file=file,if=ide,index=1,media=cdrom
@@ -285,6 +293,34 @@ qemu-system-i386 -hda a -hdb b
@end example
ETEXI
+DEF("add-fd", HAS_ARG, QEMU_OPTION_add_fd,
+ "-add-fd fd=fd,set=set[,opaque=opaque]\n"
+ " Add 'fd' to fd 'set'\n", QEMU_ARCH_ALL)
+STEXI
+@item -add-fd fd=@var{fd},set=@var{set}[,opaque=@var{opaque}]
+@findex -add-fd
+
+Add a file descriptor to an fd set. Valid options are:
+
+@table @option
+@item fd=@var{fd}
+This option defines the file descriptor of which a duplicate is added to fd set.
+The file descriptor cannot be stdin, stdout, or stderr.
+@item set=@var{set}
+This option defines the ID of the fd set to add the file descriptor to.
+@item opaque=@var{opaque}
+This option defines a free-form string that can be used to describe @var{fd}.
+@end table
+
+You can open an image using pre-opened file descriptors from an fd set:
+@example
+qemu-system-i386
+-add-fd fd=3,set=2,opaque="rdwr:/path/to/file"
+-add-fd fd=4,set=2,opaque="rdonly:/path/to/file"
+-drive file=/dev/fdset/2,index=0,media=disk
+@end example
+ETEXI
+
DEF("set", HAS_ARG, QEMU_OPTION_set,
"-set group.id.arg=value\n"
" set <arg> parameter for item <id> of type <group>\n"
diff --git a/qemu-sockets.c b/qemu-sockets.c
index 2b1ed2f0e9..cfed9c5a5b 100644
--- a/qemu-sockets.c
+++ b/qemu-sockets.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <unistd.h>
+#include "monitor.h"
#include "qemu_socket.h"
#include "qemu-common.h" /* for qemu_isdigit */
#include "main-loop.h"
@@ -120,8 +121,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
if ((qemu_opt_get(opts, "host") == NULL) ||
(qemu_opt_get(opts, "port") == NULL)) {
- fprintf(stderr, "%s: host and/or port not specified\n", __FUNCTION__);
- error_set(errp, QERR_SOCKET_CREATE_FAILED);
+ error_setg(errp, "host and/or port not specified");
return -1;
}
pstrcpy(port, sizeof(port), qemu_opt_get(opts, "port"));
@@ -138,9 +138,8 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
if (rc != 0) {
- fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
- gai_strerror(rc));
- error_set(errp, QERR_SOCKET_CREATE_FAILED);
+ error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
+ gai_strerror(rc));
return -1;
}
@@ -151,10 +150,8 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
NI_NUMERICHOST | NI_NUMERICSERV);
slisten = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol);
if (slisten < 0) {
- fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
- inet_strfamily(e->ai_family), strerror(errno));
if (!e->ai_next) {
- error_set(errp, QERR_SOCKET_CREATE_FAILED);
+ error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
}
continue;
}
@@ -176,24 +173,19 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
goto listen;
}
if (p == port_max) {
- fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
- inet_strfamily(e->ai_family), uaddr, inet_getport(e),
- strerror(errno));
if (!e->ai_next) {
- error_set(errp, QERR_SOCKET_BIND_FAILED);
+ error_set_errno(errp, errno, QERR_SOCKET_BIND_FAILED);
}
}
}
closesocket(slisten);
}
- fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
freeaddrinfo(res);
return -1;
listen:
if (listen(slisten,1) != 0) {
- error_set(errp, QERR_SOCKET_LISTEN_FAILED);
- perror("listen");
+ error_set_errno(errp, errno, QERR_SOCKET_LISTEN_FAILED);
closesocket(slisten);
freeaddrinfo(res);
return -1;
@@ -225,7 +217,7 @@ typedef struct ConnectState {
} ConnectState;
static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
- ConnectState *connect_state);
+ ConnectState *connect_state, Error **errp);
static void wait_for_connect(void *opaque)
{
@@ -252,16 +244,19 @@ static void wait_for_connect(void *opaque)
}
/* try to connect to the next address on the list */
- while (s->current_addr->ai_next != NULL && s->fd < 0) {
- s->current_addr = s->current_addr->ai_next;
- s->fd = inet_connect_addr(s->current_addr, &in_progress, s);
- /* connect in progress */
- if (in_progress) {
- return;
+ if (s->current_addr) {
+ while (s->current_addr->ai_next != NULL && s->fd < 0) {
+ s->current_addr = s->current_addr->ai_next;
+ s->fd = inet_connect_addr(s->current_addr, &in_progress, s, NULL);
+ /* connect in progress */
+ if (in_progress) {
+ return;
+ }
}
+
+ freeaddrinfo(s->addr_list);
}
- freeaddrinfo(s->addr_list);
if (s->callback) {
s->callback(s->fd, s->opaque);
}
@@ -269,7 +264,7 @@ static void wait_for_connect(void *opaque)
}
static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
- ConnectState *connect_state)
+ ConnectState *connect_state, Error **errp)
{
int sock, rc;
@@ -277,8 +272,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock < 0) {
- fprintf(stderr, "%s: socket(%s): %s\n", __func__,
- inet_strfamily(addr->ai_family), strerror(errno));
+ error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
return -1;
}
qemu_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
@@ -299,6 +293,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
connect_state);
*in_progress = true;
} else if (rc < 0) {
+ error_set_errno(errp, errno, QERR_SOCKET_CONNECT_FAILED);
closesocket(sock);
return -1;
}
@@ -321,9 +316,7 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
addr = qemu_opt_get(opts, "host");
port = qemu_opt_get(opts, "port");
if (addr == NULL || port == NULL) {
- fprintf(stderr,
- "inet_parse_connect_opts: host and/or port not specified\n");
- error_set(errp, QERR_SOCKET_CREATE_FAILED);
+ error_setg(errp, "host and/or port not specified");
return NULL;
}
@@ -337,9 +330,8 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
/* lookup */
rc = getaddrinfo(addr, port, &ai, &res);
if (rc != 0) {
- fprintf(stderr, "getaddrinfo(%s,%s): %s\n", addr, port,
- gai_strerror(rc));
- error_set(errp, QERR_SOCKET_CREATE_FAILED);
+ error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
+ gai_strerror(rc));
return NULL;
}
return res;
@@ -384,7 +376,7 @@ int inet_connect_opts(QemuOpts *opts, Error **errp,
if (connect_state != NULL) {
connect_state->current_addr = e;
}
- sock = inet_connect_addr(e, &in_progress, connect_state);
+ sock = inet_connect_addr(e, &in_progress, connect_state, errp);
if (in_progress) {
return sock;
} else if (sock >= 0) {
@@ -395,21 +387,16 @@ int inet_connect_opts(QemuOpts *opts, Error **errp,
break;
}
}
- if (sock < 0) {
- error_set(errp, QERR_SOCKET_CONNECT_FAILED);
- }
g_free(connect_state);
freeaddrinfo(res);
return sock;
}
-int inet_dgram_opts(QemuOpts *opts)
+int inet_dgram_opts(QemuOpts *opts, Error **errp)
{
struct addrinfo ai, *peer = NULL, *local = NULL;
const char *addr;
const char *port;
- char uaddr[INET6_ADDRSTRLEN+1];
- char uport[33];
int sock = -1, rc;
/* lookup peer addr */
@@ -424,7 +411,7 @@ int inet_dgram_opts(QemuOpts *opts)
addr = "localhost";
}
if (port == NULL || strlen(port) == 0) {
- fprintf(stderr, "inet_dgram: port not specified\n");
+ error_setg(errp, "remote port not specified");
return -1;
}
@@ -434,8 +421,8 @@ int inet_dgram_opts(QemuOpts *opts)
ai.ai_family = PF_INET6;
if (0 != (rc = getaddrinfo(addr, port, &ai, &peer))) {
- fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
- gai_strerror(rc));
+ error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
+ gai_strerror(rc));
return -1;
}
@@ -454,44 +441,28 @@ int inet_dgram_opts(QemuOpts *opts)
port = "0";
if (0 != (rc = getaddrinfo(addr, port, &ai, &local))) {
- fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
- gai_strerror(rc));
+ error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
+ gai_strerror(rc));
goto err;
}
/* create socket */
sock = qemu_socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol);
if (sock < 0) {
- fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
- inet_strfamily(peer->ai_family), strerror(errno));
+ error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
goto err;
}
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
/* bind socket */
- if (getnameinfo((struct sockaddr*)local->ai_addr,local->ai_addrlen,
- uaddr,INET6_ADDRSTRLEN,uport,32,
- NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
- fprintf(stderr, "%s: getnameinfo: oops\n", __FUNCTION__);
- goto err;
- }
if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) {
- fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
- inet_strfamily(local->ai_family), uaddr, inet_getport(local));
+ error_set_errno(errp, errno, QERR_SOCKET_BIND_FAILED);
goto err;
}
/* connect to peer */
- if (getnameinfo((struct sockaddr*)peer->ai_addr, peer->ai_addrlen,
- uaddr, INET6_ADDRSTRLEN, uport, 32,
- NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
- fprintf(stderr, "%s: getnameinfo: oops\n", __FUNCTION__);
- goto err;
- }
if (connect(sock,peer->ai_addr,peer->ai_addrlen) < 0) {
- fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
- inet_strfamily(peer->ai_family),
- peer->ai_canonname, uaddr, uport, strerror(errno));
+ error_set_errno(errp, errno, QERR_SOCKET_CONNECT_FAILED);
goto err;
}
@@ -510,59 +481,91 @@ err:
}
/* compatibility wrapper */
-static int inet_parse(QemuOpts *opts, const char *str)
+static InetSocketAddress *inet_parse(const char *str, Error **errp)
{
+ InetSocketAddress *addr;
const char *optstr, *h;
- char addr[64];
+ char host[64];
char port[33];
+ int to;
int pos;
+ addr = g_new0(InetSocketAddress, 1);
+
/* parse address */
if (str[0] == ':') {
/* no host given */
- addr[0] = '\0';
- if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
- fprintf(stderr, "%s: portonly parse error (%s)\n",
- __FUNCTION__, str);
- return -1;
+ host[0] = '\0';
+ if (1 != sscanf(str, ":%32[^,]%n", port, &pos)) {
+ error_setg(errp, "error parsing port in address '%s'", str);
+ goto fail;
}
} else if (str[0] == '[') {
/* IPv6 addr */
- if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
- fprintf(stderr, "%s: ipv6 parse error (%s)\n",
- __FUNCTION__, str);
- return -1;
+ if (2 != sscanf(str, "[%64[^]]]:%32[^,]%n", host, port, &pos)) {
+ error_setg(errp, "error parsing IPv6 address '%s'", str);
+ goto fail;
}
- qemu_opt_set(opts, "ipv6", "on");
+ addr->ipv6 = addr->has_ipv6 = true;
} else if (qemu_isdigit(str[0])) {
/* IPv4 addr */
- if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
- fprintf(stderr, "%s: ipv4 parse error (%s)\n",
- __FUNCTION__, str);
- return -1;
+ if (2 != sscanf(str, "%64[0-9.]:%32[^,]%n", host, port, &pos)) {
+ error_setg(errp, "error parsing IPv4 address '%s'", str);
+ goto fail;
}
- qemu_opt_set(opts, "ipv4", "on");
+ addr->ipv4 = addr->has_ipv4 = true;
} else {
/* hostname */
- if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
- fprintf(stderr, "%s: hostname parse error (%s)\n",
- __FUNCTION__, str);
- return -1;
+ if (2 != sscanf(str, "%64[^:]:%32[^,]%n", host, port, &pos)) {
+ error_setg(errp, "error parsing address '%s'", str);
+ goto fail;
}
}
- qemu_opt_set(opts, "host", addr);
- qemu_opt_set(opts, "port", port);
+
+ addr->host = g_strdup(host);
+ addr->port = g_strdup(port);
/* parse options */
optstr = str + pos;
h = strstr(optstr, ",to=");
- if (h)
- qemu_opt_set(opts, "to", h+4);
- if (strstr(optstr, ",ipv4"))
- qemu_opt_set(opts, "ipv4", "on");
- if (strstr(optstr, ",ipv6"))
- qemu_opt_set(opts, "ipv6", "on");
- return 0;
+ if (h) {
+ if (1 != sscanf(str, "%d%n", &to, &pos) ||
+ (str[pos] != '\0' && str[pos] != ',')) {
+ error_setg(errp, "error parsing to= argument");
+ goto fail;
+ }
+ addr->has_to = true;
+ addr->to = to;
+ }
+ if (strstr(optstr, ",ipv4")) {
+ addr->ipv4 = addr->has_ipv4 = true;
+ }
+ if (strstr(optstr, ",ipv6")) {
+ addr->ipv6 = addr->has_ipv6 = true;
+ }
+ return addr;
+
+fail:
+ qapi_free_InetSocketAddress(addr);
+ return NULL;
+}
+
+static void inet_addr_to_opts(QemuOpts *opts, InetSocketAddress *addr)
+{
+ bool ipv4 = addr->ipv4 || !addr->has_ipv4;
+ bool ipv6 = addr->ipv6 || !addr->has_ipv6;
+
+ if (!ipv4 || !ipv6) {
+ qemu_opt_set_bool(opts, "ipv4", ipv4);
+ qemu_opt_set_bool(opts, "ipv6", ipv6);
+ }
+ if (addr->has_to) {
+ char to[20];
+ snprintf(to, sizeof(to), "%d", addr->to);
+ qemu_opt_set(opts, "to", to);
+ }
+ qemu_opt_set(opts, "host", addr->host);
+ qemu_opt_set(opts, "port", addr->port);
}
int inet_listen(const char *str, char *ostr, int olen,
@@ -571,9 +574,13 @@ int inet_listen(const char *str, char *ostr, int olen,
QemuOpts *opts;
char *optstr;
int sock = -1;
+ InetSocketAddress *addr;
- opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
- if (inet_parse(opts, str) == 0) {
+ addr = inet_parse(str, errp);
+ if (addr != NULL) {
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ inet_addr_to_opts(opts, addr);
+ qapi_free_InetSocketAddress(addr);
sock = inet_listen_opts(opts, port_offset, errp);
if (sock != -1 && ostr) {
optstr = strchr(str, ',');
@@ -589,10 +596,8 @@ int inet_listen(const char *str, char *ostr, int olen,
optstr ? optstr : "");
}
}
- } else {
- error_set(errp, QERR_SOCKET_CREATE_FAILED);
+ qemu_opts_del(opts);
}
- qemu_opts_del(opts);
return sock;
}
@@ -608,14 +613,16 @@ int inet_connect(const char *str, Error **errp)
{
QemuOpts *opts;
int sock = -1;
+ InetSocketAddress *addr;
- opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
- if (inet_parse(opts, str) == 0) {
+ addr = inet_parse(str, errp);
+ if (addr != NULL) {
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ inet_addr_to_opts(opts, addr);
+ qapi_free_InetSocketAddress(addr);
sock = inet_connect_opts(opts, errp, NULL, NULL);
- } else {
- error_set(errp, QERR_SOCKET_CREATE_FAILED);
+ qemu_opts_del(opts);
}
- qemu_opts_del(opts);
return sock;
}
@@ -638,22 +645,24 @@ int inet_nonblocking_connect(const char *str,
{
QemuOpts *opts;
int sock = -1;
+ InetSocketAddress *addr;
g_assert(callback != NULL);
- opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
- if (inet_parse(opts, str) == 0) {
+ addr = inet_parse(str, errp);
+ if (addr != NULL) {
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ inet_addr_to_opts(opts, addr);
+ qapi_free_InetSocketAddress(addr);
sock = inet_connect_opts(opts, errp, callback, opaque);
- } else {
- error_set(errp, QERR_SOCKET_CREATE_FAILED);
+ qemu_opts_del(opts);
}
- qemu_opts_del(opts);
return sock;
}
#ifndef _WIN32
-int unix_listen_opts(QemuOpts *opts)
+int unix_listen_opts(QemuOpts *opts, Error **errp)
{
struct sockaddr_un un;
const char *path = qemu_opt_get(opts, "path");
@@ -661,7 +670,7 @@ int unix_listen_opts(QemuOpts *opts)
sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
- perror("socket(unix)");
+ error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
return -1;
}
@@ -686,11 +695,11 @@ int unix_listen_opts(QemuOpts *opts)
unlink(un.sun_path);
if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
- fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
+ error_set_errno(errp, errno, QERR_SOCKET_BIND_FAILED);
goto err;
}
if (listen(sock, 1) < 0) {
- fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
+ error_set_errno(errp, errno, QERR_SOCKET_LISTEN_FAILED);
goto err;
}
@@ -701,37 +710,85 @@ err:
return -1;
}
-int unix_connect_opts(QemuOpts *opts)
+int unix_connect_opts(QemuOpts *opts, Error **errp,
+ NonBlockingConnectHandler *callback, void *opaque)
{
struct sockaddr_un un;
const char *path = qemu_opt_get(opts, "path");
- int sock;
+ ConnectState *connect_state = NULL;
+ int sock, rc;
if (NULL == path) {
- fprintf(stderr, "unix connect: no path specified\n");
+ error_setg(errp, "unix connect: no path specified\n");
return -1;
}
sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
- perror("socket(unix)");
+ error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
return -1;
}
+ if (callback != NULL) {
+ connect_state = g_malloc0(sizeof(*connect_state));
+ connect_state->callback = callback;
+ connect_state->opaque = opaque;
+ socket_set_nonblock(sock);
+ }
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
- if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
- fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
+
+ /* connect to peer */
+ do {
+ rc = 0;
+ if (connect(sock, (struct sockaddr *) &un, sizeof(un)) < 0) {
+ rc = -socket_error();
+ }
+ } while (rc == -EINTR);
+
+ if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) {
+ connect_state->fd = sock;
+ qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect,
+ connect_state);
+ return sock;
+ } else if (rc >= 0) {
+ /* non blocking socket immediate success, call callback */
+ if (callback != NULL) {
+ callback(sock, opaque);
+ }
+ }
+
+ if (rc < 0) {
+ error_set_errno(errp, -rc, QERR_SOCKET_CONNECT_FAILED);
close(sock);
- return -1;
+ sock = -1;
}
+ g_free(connect_state);
return sock;
}
+#else
+
+int unix_listen_opts(QemuOpts *opts, Error **errp)
+{
+ error_setg(errp, "unix sockets are not available on windows");
+ errno = ENOTSUP;
+ return -1;
+}
+
+int unix_connect_opts(QemuOpts *opts, Error **errp,
+ NonBlockingConnectHandler *callback, void *opaque)
+{
+ error_setg(errp, "unix sockets are not available on windows");
+ errno = ENOTSUP;
+ return -1;
+}
+#endif
+
/* compatibility wrapper */
-int unix_listen(const char *str, char *ostr, int olen)
+int unix_listen(const char *str, char *ostr, int olen, Error **errp)
{
QemuOpts *opts;
char *path, *optstr;
@@ -752,7 +809,7 @@ int unix_listen(const char *str, char *ostr, int olen)
qemu_opt_set(opts, "path", str);
}
- sock = unix_listen_opts(opts);
+ sock = unix_listen_opts(opts, errp);
if (sock != -1 && ostr)
snprintf(ostr, olen, "%s%s", qemu_opt_get(opts, "path"), optstr ? optstr : "");
@@ -760,49 +817,132 @@ int unix_listen(const char *str, char *ostr, int olen)
return sock;
}
-int unix_connect(const char *path)
+int unix_connect(const char *path, Error **errp)
{
QemuOpts *opts;
int sock;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
qemu_opt_set(opts, "path", path);
- sock = unix_connect_opts(opts);
+ sock = unix_connect_opts(opts, errp, NULL, NULL);
qemu_opts_del(opts);
return sock;
}
-#else
-int unix_listen_opts(QemuOpts *opts)
+int unix_nonblocking_connect(const char *path,
+ NonBlockingConnectHandler *callback,
+ void *opaque, Error **errp)
{
- fprintf(stderr, "unix sockets are not available on windows\n");
- errno = ENOTSUP;
- return -1;
+ QemuOpts *opts;
+ int sock = -1;
+
+ g_assert(callback != NULL);
+
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ qemu_opt_set(opts, "path", path);
+ sock = unix_connect_opts(opts, errp, callback, opaque);
+ qemu_opts_del(opts);
+ return sock;
}
-int unix_connect_opts(QemuOpts *opts)
+SocketAddress *socket_parse(const char *str, Error **errp)
{
- fprintf(stderr, "unix sockets are not available on windows\n");
- errno = ENOTSUP;
- return -1;
+ SocketAddress *addr = NULL;
+
+ addr = g_new(SocketAddress, 1);
+ if (strstart(str, "unix:", NULL)) {
+ if (str[5] == '\0') {
+ error_setg(errp, "invalid Unix socket address\n");
+ goto fail;
+ } else {
+ addr->kind = SOCKET_ADDRESS_KIND_UNIX;
+ addr->q_unix = g_new(UnixSocketAddress, 1);
+ addr->q_unix->path = g_strdup(str + 5);
+ }
+ } else if (strstart(str, "fd:", NULL)) {
+ if (str[3] == '\0') {
+ error_setg(errp, "invalid file descriptor address\n");
+ goto fail;
+ } else {
+ addr->kind = SOCKET_ADDRESS_KIND_FD;
+ addr->fd = g_new(String, 1);
+ addr->fd->str = g_strdup(str + 3);
+ }
+ } else {
+ addr->kind = SOCKET_ADDRESS_KIND_INET;
+ addr->inet = g_new(InetSocketAddress, 1);
+ addr->inet = inet_parse(str, errp);
+ if (addr->inet == NULL) {
+ goto fail;
+ }
+ }
+ return addr;
+
+fail:
+ qapi_free_SocketAddress(addr);
+ return NULL;
}
-int unix_listen(const char *path, char *ostr, int olen)
+int socket_connect(SocketAddress *addr, Error **errp,
+ NonBlockingConnectHandler *callback, void *opaque)
{
- fprintf(stderr, "unix sockets are not available on windows\n");
- errno = ENOTSUP;
- return -1;
+ QemuOpts *opts;
+ int fd;
+
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ switch (addr->kind) {
+ case SOCKET_ADDRESS_KIND_INET:
+ inet_addr_to_opts(opts, addr->inet);
+ fd = inet_connect_opts(opts, errp, callback, opaque);
+ break;
+
+ case SOCKET_ADDRESS_KIND_UNIX:
+ qemu_opt_set(opts, "path", addr->q_unix->path);
+ fd = unix_connect_opts(opts, errp, callback, opaque);
+ break;
+
+ case SOCKET_ADDRESS_KIND_FD:
+ fd = monitor_get_fd(cur_mon, addr->fd->str, errp);
+ if (callback) {
+ callback(fd, opaque);
+ }
+ break;
+
+ default:
+ abort();
+ }
+ qemu_opts_del(opts);
+ return fd;
}
-int unix_connect(const char *path)
+int socket_listen(SocketAddress *addr, Error **errp)
{
- fprintf(stderr, "unix sockets are not available on windows\n");
- errno = ENOTSUP;
- return -1;
-}
+ QemuOpts *opts;
+ int fd;
-#endif
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ switch (addr->kind) {
+ case SOCKET_ADDRESS_KIND_INET:
+ inet_addr_to_opts(opts, addr->inet);
+ fd = inet_listen_opts(opts, 0, errp);
+ break;
+
+ case SOCKET_ADDRESS_KIND_UNIX:
+ qemu_opt_set(opts, "path", addr->q_unix->path);
+ fd = unix_listen_opts(opts, errp);
+ break;
+
+ case SOCKET_ADDRESS_KIND_FD:
+ fd = monitor_get_fd(cur_mon, addr->fd->str, errp);
+ break;
+
+ default:
+ abort();
+ }
+ qemu_opts_del(opts);
+ return fd;
+}
#ifdef _WIN32
static void socket_cleanup(void)
diff --git a/qemu-timer.c b/qemu-timer.c
index 908a1030b6..ede84ffd56 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -494,12 +494,12 @@ static int dynticks_start_timer(struct qemu_alarm_timer *t)
memset(&ev, 0, sizeof(ev));
ev.sigev_value.sival_int = 0;
ev.sigev_notify = SIGEV_SIGNAL;
-#ifdef SIGEV_THREAD_ID
+#ifdef CONFIG_SIGEV_THREAD_ID
if (qemu_signalfd_available()) {
ev.sigev_notify = SIGEV_THREAD_ID;
ev._sigev_un._tid = qemu_get_thread_id();
}
-#endif /* SIGEV_THREAD_ID */
+#endif /* CONFIG_SIGEV_THREAD_ID */
ev.sigev_signo = SIGALRM;
if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) {
diff --git a/qemu-tool.c b/qemu-tool.c
index f2f98138ce..da4c05aaf7 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -38,6 +38,12 @@ const char *qemu_get_vm_name(void)
Monitor *cur_mon;
+int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
+{
+ error_setg(errp, "only QEMU supports file descriptor passing");
+ return -1;
+}
+
void vm_stop(RunState state)
{
abort();
diff --git a/qemu_socket.h b/qemu_socket.h
index 3e8aee9cad..02490ad06c 100644
--- a/qemu_socket.h
+++ b/qemu_socket.h
@@ -53,13 +53,22 @@ int inet_nonblocking_connect(const char *str,
NonBlockingConnectHandler *callback,
void *opaque, Error **errp);
-int inet_dgram_opts(QemuOpts *opts);
+int inet_dgram_opts(QemuOpts *opts, Error **errp);
const char *inet_strfamily(int family);
-int unix_listen_opts(QemuOpts *opts);
-int unix_listen(const char *path, char *ostr, int olen);
-int unix_connect_opts(QemuOpts *opts);
-int unix_connect(const char *path);
+int unix_listen_opts(QemuOpts *opts, Error **errp);
+int unix_listen(const char *path, char *ostr, int olen, Error **errp);
+int unix_connect_opts(QemuOpts *opts, Error **errp,
+ NonBlockingConnectHandler *callback, void *opaque);
+int unix_connect(const char *path, Error **errp);
+int unix_nonblocking_connect(const char *str,
+ NonBlockingConnectHandler *callback,
+ void *opaque, Error **errp);
+
+SocketAddress *socket_parse(const char *str, Error **errp);
+int socket_connect(SocketAddress *addr, Error **errp,
+ NonBlockingConnectHandler *callback, void *opaque);
+int socket_listen(SocketAddress *addr, Error **errp);
/* Old, ipv4 only bits. Don't use for new code. */
int parse_host_port(struct sockaddr_in *saddr, const char *str);
diff --git a/qerror.h b/qerror.h
index c91708cc3c..8db4309aef 100644
--- a/qerror.h
+++ b/qerror.h
@@ -54,6 +54,9 @@ void assert_no_error(Error *err);
#define QERR_BLOCK_JOB_PAUSED \
ERROR_CLASS_GENERIC_ERROR, "The block job for device '%s' is currently paused"
+#define QERR_BLOCK_JOB_NOT_READY \
+ ERROR_CLASS_GENERIC_ERROR, "The active block job for device '%s' cannot be completed"
+
#define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \
ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'"
@@ -165,9 +168,6 @@ void assert_no_error(Error *err);
#define QERR_MIGRATION_NOT_SUPPORTED \
ERROR_CLASS_GENERIC_ERROR, "State blocked by non-migratable device '%s'"
-#define QERR_MIGRATION_EXPECTED \
- ERROR_CLASS_MIGRATION_EXPECTED, "An incoming migration is expected before this command can be executed"
-
#define QERR_MISSING_PARAMETER \
ERROR_CLASS_GENERIC_ERROR, "Parameter '%s' is missing"
@@ -237,9 +237,6 @@ void assert_no_error(Error *err);
#define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \
ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'"
-#define QERR_VNC_SERVER_FAILED \
- ERROR_CLASS_GENERIC_ERROR, "Could not start VNC server on %s"
-
#define QERR_SOCKET_CONNECT_FAILED \
ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket"
diff --git a/qga/channel-posix.c b/qga/channel-posix.c
index 57eea06c47..d152827bcf 100644
--- a/qga/channel-posix.c
+++ b/qga/channel-posix.c
@@ -181,9 +181,11 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod
break;
}
case GA_CHANNEL_UNIX_LISTEN: {
- int fd = unix_listen(path, NULL, strlen(path));
- if (fd == -1) {
- g_critical("error opening path: %s", strerror(errno));
+ Error *local_err = NULL;
+ int fd = unix_listen(path, NULL, strlen(path), &local_err);
+ if (local_err != NULL) {
+ g_critical("%s", error_get_pretty(local_err));
+ error_free(local_err);
return false;
}
ga_channel_listen_add(c, fd, true);
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 5ba8c48cb4..5c692d0cb5 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -843,6 +843,11 @@ EQMP
.mhandler.cmd_new = qmp_marshal_input_block_job_resume,
},
{
+ .name = "block-job-complete",
+ .args_type = "device:B",
+ .mhandler.cmd_new = qmp_marshal_input_block_job_complete,
+ },
+ {
.name = "transaction",
.args_type = "actions:q",
.mhandler.cmd_new = qmp_marshal_input_transaction,
@@ -931,6 +936,54 @@ Example:
EQMP
{
+ .name = "drive-mirror",
+ .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
+ "on-source-error:s?,on-target-error:s?",
+ .mhandler.cmd_new = qmp_marshal_input_drive_mirror,
+ },
+
+SQMP
+drive-mirror
+------------
+
+Start mirroring a block device's writes to a new destination. target
+specifies the target of the new image. If the file exists, or if it is
+a device, it will be used as the new destination for writes. If it does not
+exist, a new file will be created. format specifies the format of the
+mirror image, default is to probe if mode='existing', else the format
+of the source.
+
+Arguments:
+
+- "device": device name to operate on (json-string)
+- "target": name of new image file (json-string)
+- "format": format of new image (json-string, optional)
+- "mode": how an image file should be created into the target
+ file/device (NewImageMode, optional, default 'absolute-paths')
+- "speed": maximum speed of the streaming job, in bytes per second
+ (json-int)
+- "sync": what parts of the disk image should be copied to the destination;
+ possibilities include "full" for all the disk, "top" for only the sectors
+ allocated in the topmost image, or "none" to only replicate new I/O
+ (MirrorSyncMode).
+- "on-source-error": the action to take on an error on the source
+ (BlockdevOnError, default 'report')
+- "on-target-error": the action to take on an error on the target
+ (BlockdevOnError, default 'report')
+
+
+
+Example:
+
+-> { "execute": "drive-mirror", "arguments": { "device": "ide-hd0",
+ "target": "/some/place/my-image",
+ "sync": "full",
+ "format": "qcow2" } }
+<- { "return": {} }
+
+EQMP
+
+ {
.name = "balloon",
.args_type = "value:M",
.mhandler.cmd_new = qmp_marshal_input_balloon,
@@ -2552,6 +2605,22 @@ EQMP
},
{
+ .name = "nbd-server-start",
+ .args_type = "addr:q",
+ .mhandler.cmd_new = qmp_marshal_input_nbd_server_start,
+ },
+ {
+ .name = "nbd-server-add",
+ .args_type = "device:B,writable:b?",
+ .mhandler.cmd_new = qmp_marshal_input_nbd_server_add,
+ },
+ {
+ .name = "nbd-server-stop",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_nbd_server_stop,
+ },
+
+ {
.name = "change-vnc-password",
.args_type = "password:s",
.mhandler.cmd_new = qmp_marshal_input_change_vnc_password,
diff --git a/qmp.c b/qmp.c
index 36c54c57cf..d8a3c00b97 100644
--- a/qmp.c
+++ b/qmp.c
@@ -85,7 +85,11 @@ void qmp_quit(Error **err)
void qmp_stop(Error **errp)
{
- vm_stop(RUN_STATE_PAUSED);
+ if (runstate_check(RUN_STATE_INMIGRATE)) {
+ autostart = 0;
+ } else {
+ vm_stop(RUN_STATE_PAUSED);
+ }
}
void qmp_system_reset(Error **errp)
@@ -144,10 +148,7 @@ void qmp_cont(Error **errp)
{
Error *local_err = NULL;
- if (runstate_check(RUN_STATE_INMIGRATE)) {
- error_set(errp, QERR_MIGRATION_EXPECTED);
- return;
- } else if (runstate_check(RUN_STATE_INTERNAL_ERROR) ||
+ if (runstate_check(RUN_STATE_INTERNAL_ERROR) ||
runstate_check(RUN_STATE_SHUTDOWN)) {
error_set(errp, QERR_RESET_REQUIRED);
return;
@@ -162,7 +163,11 @@ void qmp_cont(Error **errp)
return;
}
- vm_start();
+ if (runstate_check(RUN_STATE_INMIGRATE)) {
+ autostart = 1;
+ } else {
+ vm_start();
+ }
}
void qmp_system_wakeup(Error **errp)
@@ -349,11 +354,9 @@ void qmp_change_vnc_password(const char *password, Error **errp)
}
}
-static void qmp_change_vnc_listen(const char *target, Error **err)
+static void qmp_change_vnc_listen(const char *target, Error **errp)
{
- if (vnc_display_open(NULL, target) < 0) {
- error_set(err, QERR_VNC_SERVER_FAILED, target);
- }
+ vnc_display_open(NULL, target, errp);
}
static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
diff --git a/sysemu.h b/sysemu.h
index 0c39a3ac1a..f72b4ce125 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -116,7 +116,6 @@ extern const char *keyboard_layout;
extern int win2k_install_hack;
extern int alt_grab;
extern int ctrl_grab;
-extern int usb_enabled;
extern int smp_cpus;
extern int max_cpus;
extern int cursor_hide;
@@ -186,4 +185,7 @@ void register_devices(void);
void add_boot_device_path(int32_t bootindex, DeviceState *dev,
const char *suffix);
char *get_boot_devices_list(uint32_t *size);
+
+bool usb_enabled(bool default_usb);
+
#endif
diff --git a/target-alpha/helper.h b/target-alpha/helper.h
index a184def3e9..162816fa73 100644
--- a/target-alpha/helper.h
+++ b/target-alpha/helper.h
@@ -1,7 +1,7 @@
#include "def-helper.h"
DEF_HELPER_3(excp, noreturn, env, int, int)
-DEF_HELPER_FLAGS_1(load_pcc, TCG_CALL_CONST | TCG_CALL_PURE, i64, env)
+DEF_HELPER_FLAGS_1(load_pcc, TCG_CALL_NO_RWG_SE, i64, env)
DEF_HELPER_3(addqv, i64, env, i64, i64)
DEF_HELPER_3(addlv, i64, env, i64, i64)
@@ -9,89 +9,89 @@ DEF_HELPER_3(subqv, i64, env, i64, i64)
DEF_HELPER_3(sublv, i64, env, i64, i64)
DEF_HELPER_3(mullv, i64, env, i64, i64)
DEF_HELPER_3(mulqv, i64, env, i64, i64)
-DEF_HELPER_FLAGS_2(umulh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-
-DEF_HELPER_FLAGS_1(ctpop, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-DEF_HELPER_FLAGS_1(ctlz, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-DEF_HELPER_FLAGS_1(cttz, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-
-DEF_HELPER_FLAGS_2(zap, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(zapnot, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-
-DEF_HELPER_FLAGS_2(cmpbge, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-
-DEF_HELPER_FLAGS_2(minub8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(minsb8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(minuw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(minsw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(maxub8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(maxsb8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(maxuw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(maxsw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(perr, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_1(pklb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-DEF_HELPER_FLAGS_1(pkwb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-DEF_HELPER_FLAGS_1(unpkbl, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-DEF_HELPER_FLAGS_1(unpkbw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-
-DEF_HELPER_FLAGS_1(load_fpcr, TCG_CALL_CONST | TCG_CALL_PURE, i64, env)
-DEF_HELPER_FLAGS_2(store_fpcr, TCG_CALL_CONST, void, env, i64)
-
-DEF_HELPER_FLAGS_1(f_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64)
-DEF_HELPER_FLAGS_1(memory_to_f, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32)
-DEF_HELPER_FLAGS_3(addf, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(subf, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(mulf, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(divf, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_2(sqrtf, TCG_CALL_CONST, i64, env, i64)
-
-DEF_HELPER_FLAGS_1(g_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-DEF_HELPER_FLAGS_1(memory_to_g, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-DEF_HELPER_FLAGS_3(addg, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(subg, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(mulg, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(divg, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_2(sqrtg, TCG_CALL_CONST, i64, env, i64)
-
-DEF_HELPER_FLAGS_1(s_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64)
-DEF_HELPER_FLAGS_1(memory_to_s, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32)
-DEF_HELPER_FLAGS_3(adds, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(subs, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(muls, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(divs, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_2(sqrts, TCG_CALL_CONST, i64, env, i64)
-
-DEF_HELPER_FLAGS_3(addt, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(subt, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(mult, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(divt, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_2(sqrtt, TCG_CALL_CONST, i64, env, i64)
-
-DEF_HELPER_FLAGS_3(cmptun, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(cmpteq, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(cmptle, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(cmptlt, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(cmpgeq, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(cmpgle, TCG_CALL_CONST, i64, env, i64, i64)
-DEF_HELPER_FLAGS_3(cmpglt, TCG_CALL_CONST, i64, env, i64, i64)
-
-DEF_HELPER_FLAGS_2(cvtts, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvtst, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvtqs, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvtqt, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvtqf, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvtgf, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvtgq, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvtqg, TCG_CALL_CONST, i64, env, i64)
-
-DEF_HELPER_FLAGS_2(cvttq, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvttq_c, TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_2(cvttq_svic, TCG_CALL_CONST, i64, env, i64)
-
-DEF_HELPER_FLAGS_2(setroundmode, TCG_CALL_CONST, void, env, i32)
-DEF_HELPER_FLAGS_2(setflushzero, TCG_CALL_CONST, void, env, i32)
-DEF_HELPER_FLAGS_1(fp_exc_clear, TCG_CALL_CONST, void, env)
-DEF_HELPER_FLAGS_1(fp_exc_get, TCG_CALL_CONST | TCG_CALL_PURE, i32, env)
+DEF_HELPER_FLAGS_2(umulh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+
+DEF_HELPER_FLAGS_1(ctpop, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(ctlz, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(cttz, TCG_CALL_NO_RWG_SE, i64, i64)
+
+DEF_HELPER_FLAGS_2(zap, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(zapnot, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+
+DEF_HELPER_FLAGS_2(cmpbge, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+
+DEF_HELPER_FLAGS_2(minub8, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(minsb8, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(minuw4, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(minsw4, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(maxub8, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(maxsb8, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(maxuw4, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(maxsw4, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(perr, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_1(pklb, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(pkwb, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(unpkbl, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(unpkbw, TCG_CALL_NO_RWG_SE, i64, i64)
+
+DEF_HELPER_FLAGS_1(load_fpcr, TCG_CALL_NO_RWG_SE, i64, env)
+DEF_HELPER_FLAGS_2(store_fpcr, TCG_CALL_NO_RWG, void, env, i64)
+
+DEF_HELPER_FLAGS_1(f_to_memory, TCG_CALL_NO_RWG_SE, i32, i64)
+DEF_HELPER_FLAGS_1(memory_to_f, TCG_CALL_NO_RWG_SE, i64, i32)
+DEF_HELPER_FLAGS_3(addf, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(subf, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(mulf, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(divf, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(sqrtf, TCG_CALL_NO_RWG, i64, env, i64)
+
+DEF_HELPER_FLAGS_1(g_to_memory, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(memory_to_g, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_3(addg, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(subg, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(mulg, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(divg, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(sqrtg, TCG_CALL_NO_RWG, i64, env, i64)
+
+DEF_HELPER_FLAGS_1(s_to_memory, TCG_CALL_NO_RWG_SE, i32, i64)
+DEF_HELPER_FLAGS_1(memory_to_s, TCG_CALL_NO_RWG_SE, i64, i32)
+DEF_HELPER_FLAGS_3(adds, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(subs, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(muls, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(sqrts, TCG_CALL_NO_RWG, i64, env, i64)
+
+DEF_HELPER_FLAGS_3(addt, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(subt, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(mult, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(divt, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(sqrtt, TCG_CALL_NO_RWG, i64, env, i64)
+
+DEF_HELPER_FLAGS_3(cmptun, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(cmpteq, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(cmptle, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(cmptlt, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(cmpgeq, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(cmpgle, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(cmpglt, TCG_CALL_NO_RWG, i64, env, i64, i64)
+
+DEF_HELPER_FLAGS_2(cvtts, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvtst, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvtqs, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvtqt, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvtqf, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvtgf, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvtgq, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvtqg, TCG_CALL_NO_RWG, i64, env, i64)
+
+DEF_HELPER_FLAGS_2(cvttq, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvttq_c, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(cvttq_svic, TCG_CALL_NO_RWG, i64, env, i64)
+
+DEF_HELPER_FLAGS_2(setroundmode, TCG_CALL_NO_RWG, void, env, i32)
+DEF_HELPER_FLAGS_2(setflushzero, TCG_CALL_NO_RWG, void, env, i32)
+DEF_HELPER_FLAGS_1(fp_exc_clear, TCG_CALL_NO_RWG, void, env)
+DEF_HELPER_FLAGS_1(fp_exc_get, TCG_CALL_NO_RWG_SE, i32, env)
DEF_HELPER_3(fp_exc_raise, void, env, i32, i32)
DEF_HELPER_3(fp_exc_raise_s, void, env, i32, i32)
@@ -110,13 +110,13 @@ DEF_HELPER_2(stq_phys, void, i64, i64)
DEF_HELPER_3(stl_c_phys, i64, env, i64, i64)
DEF_HELPER_3(stq_c_phys, i64, env, i64, i64)
-DEF_HELPER_FLAGS_1(tbia, TCG_CALL_CONST, void, env)
-DEF_HELPER_FLAGS_2(tbis, TCG_CALL_CONST, void, env, i64)
+DEF_HELPER_FLAGS_1(tbia, TCG_CALL_NO_RWG, void, env)
+DEF_HELPER_FLAGS_2(tbis, TCG_CALL_NO_RWG, void, env, i64)
DEF_HELPER_1(halt, void, i64);
-DEF_HELPER_FLAGS_0(get_time, TCG_CALL_CONST, i64)
-DEF_HELPER_FLAGS_2(set_alarm, TCG_CALL_CONST, void, env, i64)
+DEF_HELPER_FLAGS_0(get_time, TCG_CALL_NO_RWG, i64)
+DEF_HELPER_FLAGS_2(set_alarm, TCG_CALL_NO_RWG, void, env, i64)
#endif
#include "def-helper.h"
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index 73bde584ab..7743d67010 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -166,17 +166,20 @@ static void arm_semi_flen_cb(CPUARMState *env, target_ulong ret, target_ulong er
#endif
}
-#define ARG(n) \
-({ \
- target_ulong __arg; \
- /* FIXME - handle get_user() failure */ \
- get_user_ual(__arg, args + (n) * 4); \
- __arg; \
-})
+/* Read the input value from the argument block; fail the semihosting
+ * call if the memory read fails.
+ */
+#define GET_ARG(n) do { \
+ if (get_user_ual(arg ## n, args + (n) * 4)) { \
+ return (uint32_t)-1; \
+ } \
+} while (0)
+
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
uint32_t do_arm_semihosting(CPUARMState *env)
{
target_ulong args;
+ target_ulong arg0, arg1, arg2, arg3;
char * s;
int nr;
uint32_t ret;
@@ -191,33 +194,39 @@ uint32_t do_arm_semihosting(CPUARMState *env)
args = env->regs[1];
switch (nr) {
case TARGET_SYS_OPEN:
- if (!(s = lock_user_string(ARG(0))))
+ GET_ARG(0);
+ GET_ARG(1);
+ GET_ARG(2);
+ s = lock_user_string(arg0);
+ if (!s) {
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
- if (ARG(1) >= 12) {
- unlock_user(s, ARG(0), 0);
+ }
+ if (arg1 >= 12) {
+ unlock_user(s, arg0, 0);
return (uint32_t)-1;
}
if (strcmp(s, ":tt") == 0) {
- int result_fileno = ARG(1) < 4 ? STDIN_FILENO : STDOUT_FILENO;
- unlock_user(s, ARG(0), 0);
+ int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
+ unlock_user(s, arg0, 0);
return result_fileno;
}
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
- (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
+ gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
+ (int)arg2+1, gdb_open_modeflags[arg1]);
ret = env->regs[0];
} else {
- ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
+ ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
}
- unlock_user(s, ARG(0), 0);
+ unlock_user(s, arg0, 0);
return ret;
case TARGET_SYS_CLOSE:
+ GET_ARG(0);
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
+ gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
return env->regs[0];
} else {
- return set_swi_errno(ts, close(ARG(0)));
+ return set_swi_errno(ts, close(arg0));
}
case TARGET_SYS_WRITEC:
{
@@ -248,35 +257,45 @@ uint32_t do_arm_semihosting(CPUARMState *env)
unlock_user(s, args, 0);
return ret;
case TARGET_SYS_WRITE:
- len = ARG(2);
+ GET_ARG(0);
+ GET_ARG(1);
+ GET_ARG(2);
+ len = arg2;
if (use_gdb_syscalls()) {
arm_semi_syscall_len = len;
- gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
+ gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
return env->regs[0];
} else {
- if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
+ s = lock_user(VERIFY_READ, arg1, len, 1);
+ if (!s) {
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
- ret = set_swi_errno(ts, write(ARG(0), s, len));
- unlock_user(s, ARG(1), 0);
+ }
+ ret = set_swi_errno(ts, write(arg0, s, len));
+ unlock_user(s, arg1, 0);
if (ret == (uint32_t)-1)
return -1;
return len - ret;
}
case TARGET_SYS_READ:
- len = ARG(2);
+ GET_ARG(0);
+ GET_ARG(1);
+ GET_ARG(2);
+ len = arg2;
if (use_gdb_syscalls()) {
arm_semi_syscall_len = len;
- gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
+ gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
return env->regs[0];
} else {
- if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
+ s = lock_user(VERIFY_WRITE, arg1, len, 0);
+ if (!s) {
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
- do
- ret = set_swi_errno(ts, read(ARG(0), s, len));
- while (ret == -1 && errno == EINTR);
- unlock_user(s, ARG(1), len);
+ }
+ do {
+ ret = set_swi_errno(ts, read(arg0, s, len));
+ } while (ret == -1 && errno == EINTR);
+ unlock_user(s, arg1, len);
if (ret == (uint32_t)-1)
return -1;
return len - ret;
@@ -285,30 +304,34 @@ uint32_t do_arm_semihosting(CPUARMState *env)
/* XXX: Read from debug console. Not implemented. */
return 0;
case TARGET_SYS_ISTTY:
+ GET_ARG(0);
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
+ gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
return env->regs[0];
} else {
- return isatty(ARG(0));
+ return isatty(arg0);
}
case TARGET_SYS_SEEK:
+ GET_ARG(0);
+ GET_ARG(1);
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
+ gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
return env->regs[0];
} else {
- ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
+ ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
if (ret == (uint32_t)-1)
return -1;
return 0;
}
case TARGET_SYS_FLEN:
+ GET_ARG(0);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
- ARG(0), env->regs[13]-64);
+ arg0, env->regs[13]-64);
return env->regs[0];
} else {
struct stat buf;
- ret = set_swi_errno(ts, fstat(ARG(0), &buf));
+ ret = set_swi_errno(ts, fstat(arg0, &buf));
if (ret == (uint32_t)-1)
return -1;
return buf.st_size;
@@ -317,35 +340,43 @@ uint32_t do_arm_semihosting(CPUARMState *env)
/* XXX: Not implemented. */
return -1;
case TARGET_SYS_REMOVE:
+ GET_ARG(0);
+ GET_ARG(1);
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
+ gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
ret = env->regs[0];
} else {
- if (!(s = lock_user_string(ARG(0))))
+ s = lock_user_string(arg0);
+ if (!s) {
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
+ }
ret = set_swi_errno(ts, remove(s));
- unlock_user(s, ARG(0), 0);
+ unlock_user(s, arg0, 0);
}
return ret;
case TARGET_SYS_RENAME:
+ GET_ARG(0);
+ GET_ARG(1);
+ GET_ARG(2);
+ GET_ARG(3);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
- ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
+ arg0, (int)arg1+1, arg2, (int)arg3+1);
return env->regs[0];
} else {
char *s2;
- s = lock_user_string(ARG(0));
- s2 = lock_user_string(ARG(2));
+ s = lock_user_string(arg0);
+ s2 = lock_user_string(arg2);
if (!s || !s2)
/* FIXME - should this error code be -TARGET_EFAULT ? */
ret = (uint32_t)-1;
else
ret = set_swi_errno(ts, rename(s, s2));
if (s2)
- unlock_user(s2, ARG(2), 0);
+ unlock_user(s2, arg2, 0);
if (s)
- unlock_user(s, ARG(0), 0);
+ unlock_user(s, arg0, 0);
return ret;
}
case TARGET_SYS_CLOCK:
@@ -353,15 +384,19 @@ uint32_t do_arm_semihosting(CPUARMState *env)
case TARGET_SYS_TIME:
return set_swi_errno(ts, time(NULL));
case TARGET_SYS_SYSTEM:
+ GET_ARG(0);
+ GET_ARG(1);
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
+ gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
return env->regs[0];
} else {
- if (!(s = lock_user_string(ARG(0))))
+ s = lock_user_string(arg0);
+ if (!s) {
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
+ }
ret = set_swi_errno(ts, system(s));
- unlock_user(s, ARG(0), 0);
+ unlock_user(s, arg0, 0);
return ret;
}
case TARGET_SYS_ERRNO:
@@ -375,22 +410,24 @@ uint32_t do_arm_semihosting(CPUARMState *env)
/* Build a command-line from the original argv.
*
* The inputs are:
- * * ARG(0), pointer to a buffer of at least the size
- * specified in ARG(1).
- * * ARG(1), size of the buffer pointed to by ARG(0) in
+ * * arg0, pointer to a buffer of at least the size
+ * specified in arg1.
+ * * arg1, size of the buffer pointed to by arg0 in
* bytes.
*
* The outputs are:
- * * ARG(0), pointer to null-terminated string of the
+ * * arg0, pointer to null-terminated string of the
* command line.
- * * ARG(1), length of the string pointed to by ARG(0).
+ * * arg1, length of the string pointed to by arg0.
*/
char *output_buffer;
- size_t input_size = ARG(1);
+ size_t input_size;
size_t output_size;
int status = 0;
-
+ GET_ARG(0);
+ GET_ARG(1);
+ input_size = arg1;
/* Compute the size of the output string. */
#if !defined(CONFIG_USER_ONLY)
output_size = strlen(ts->boot_info->kernel_filename)
@@ -414,10 +451,13 @@ uint32_t do_arm_semihosting(CPUARMState *env)
}
/* Adjust the command-line length. */
- SET_ARG(1, output_size - 1);
+ if (SET_ARG(1, output_size - 1)) {
+ /* Couldn't write back to argument block */
+ return -1;
+ }
/* Lock the buffer on the ARM side. */
- output_buffer = lock_user(VERIFY_WRITE, ARG(0), output_size, 0);
+ output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
if (!output_buffer) {
return -1;
}
@@ -449,7 +489,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
out:
#endif
/* Unlock the buffer on the ARM side. */
- unlock_user(output_buffer, ARG(0), output_size);
+ unlock_user(output_buffer, arg0, output_size);
return status;
}
@@ -457,6 +497,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
{
uint32_t *ptr;
uint32_t limit;
+ GET_ARG(0);
#ifdef CONFIG_USER_ONLY
/* Some C libraries assume the heap immediately follows .bss, so
@@ -477,25 +518,29 @@ uint32_t do_arm_semihosting(CPUARMState *env)
ts->heap_limit = limit;
}
- if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
+ ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
+ if (!ptr) {
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
+ }
ptr[0] = tswap32(ts->heap_base);
ptr[1] = tswap32(ts->heap_limit);
ptr[2] = tswap32(ts->stack_base);
ptr[3] = tswap32(0); /* Stack limit. */
- unlock_user(ptr, ARG(0), 16);
+ unlock_user(ptr, arg0, 16);
#else
limit = ram_size;
- if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
+ ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
+ if (!ptr) {
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
+ }
/* TODO: Make this use the limit of the loaded application. */
ptr[0] = tswap32(limit / 2);
ptr[1] = tswap32(limit);
ptr[2] = tswap32(limit); /* Stack base */
ptr[3] = tswap32(0); /* Stack limit. */
- unlock_user(ptr, ARG(0), 16);
+ unlock_user(ptr, arg0, 16);
#endif
return 0;
}
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 8f2cba6c1d..ab8b734933 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -1562,11 +1562,6 @@ uint32_t HELPER(rbit)(uint32_t x)
return x;
}
-uint32_t HELPER(abs)(uint32_t x)
-{
- return ((int32_t)x < 0) ? -x : x;
-}
-
#if defined(CONFIG_USER_ONLY)
void do_interrupt (CPUARMState *env)
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 8b9adf1311..3d23ceb257 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -1,8 +1,8 @@
#include "def-helper.h"
-DEF_HELPER_FLAGS_1(clz, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
-DEF_HELPER_FLAGS_1(sxtb16, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
-DEF_HELPER_FLAGS_1(uxtb16, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
+DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, i32, i32)
+DEF_HELPER_FLAGS_1(sxtb16, TCG_CALL_NO_RWG_SE, i32, i32)
+DEF_HELPER_FLAGS_1(uxtb16, TCG_CALL_NO_RWG_SE, i32, i32)
DEF_HELPER_3(add_setq, i32, env, i32, i32)
DEF_HELPER_3(add_saturate, i32, env, i32, i32)
@@ -10,10 +10,9 @@ DEF_HELPER_3(sub_saturate, i32, env, i32, i32)
DEF_HELPER_3(add_usaturate, i32, env, i32, i32)
DEF_HELPER_3(sub_usaturate, i32, env, i32, i32)
DEF_HELPER_2(double_saturate, i32, env, s32)
-DEF_HELPER_FLAGS_2(sdiv, TCG_CALL_CONST | TCG_CALL_PURE, s32, s32, s32)
-DEF_HELPER_FLAGS_2(udiv, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32, i32)
-DEF_HELPER_FLAGS_1(rbit, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
-DEF_HELPER_FLAGS_1(abs, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
+DEF_HELPER_FLAGS_2(sdiv, TCG_CALL_NO_RWG_SE, s32, s32, s32)
+DEF_HELPER_FLAGS_2(udiv, TCG_CALL_NO_RWG_SE, i32, i32, i32)
+DEF_HELPER_FLAGS_1(rbit, TCG_CALL_NO_RWG_SE, i32, i32)
#define PAS_OP(pfx) \
DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \
@@ -45,11 +44,11 @@ DEF_HELPER_3(usat, i32, env, i32, i32)
DEF_HELPER_3(ssat16, i32, env, i32, i32)
DEF_HELPER_3(usat16, i32, env, i32, i32)
-DEF_HELPER_FLAGS_2(usad8, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32, i32)
+DEF_HELPER_FLAGS_2(usad8, TCG_CALL_NO_RWG_SE, i32, i32, i32)
DEF_HELPER_1(logicq_cc, i32, i64)
-DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_CONST | TCG_CALL_PURE,
+DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE,
i32, i32, i32, i32)
DEF_HELPER_2(exception, void, env, i32)
DEF_HELPER_1(wfi, void, env)
@@ -339,7 +338,6 @@ DEF_HELPER_2(neon_mull_s16, i64, i32, i32)
DEF_HELPER_1(neon_negl_u16, i64, i64)
DEF_HELPER_1(neon_negl_u32, i64, i64)
-DEF_HELPER_1(neon_negl_u64, i64, i64)
DEF_HELPER_2(neon_qabs_s8, i32, env, i32)
DEF_HELPER_2(neon_qabs_s16, i32, env, i32)
diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c
index 9aa920d2ac..89280b6eeb 100644
--- a/target-arm/neon_helper.c
+++ b/target-arm/neon_helper.c
@@ -1664,12 +1664,6 @@ uint64_t HELPER(neon_negl_u32)(uint64_t x)
return low | ((uint64_t)high << 32);
}
-/* FIXME: There should be a native op for this. */
-uint64_t HELPER(neon_negl_u64)(uint64_t x)
-{
- return -x;
-}
-
/* Saturating sign manipulation. */
/* ??? Make these use NEON_VOP1 */
#define DO_QABS8(x) do { \
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index aef592ab8d..6e3ab90e3b 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -93,8 +93,6 @@ void tlb_fill(CPUARMState *env, target_ulong addr, int is_write, int mmu_idx,
}
#endif
-/* FIXME: Pass an explicit pointer to QF to CPUARMState, and move saturating
- instructions into helper.c */
uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b)
{
uint32_t res = a + b;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index daccb15c23..25433da037 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -462,8 +462,15 @@ static void gen_sar(TCGv dest, TCGv t0, TCGv t1)
tcg_temp_free_i32(tmp1);
}
-/* FIXME: Implement this natively. */
-#define tcg_gen_abs_i32(t0, t1) gen_helper_abs(t0, t1)
+static void tcg_gen_abs_i32(TCGv dest, TCGv src)
+{
+ TCGv c0 = tcg_const_i32(0);
+ TCGv tmp = tcg_temp_new_i32();
+ tcg_gen_neg_i32(tmp, src);
+ tcg_gen_movcond_i32(TCG_COND_GT, dest, src, c0, src, tmp);
+ tcg_temp_free_i32(c0);
+ tcg_temp_free_i32(tmp);
+}
static void shifter_out_im(TCGv var, int shift)
{
@@ -4184,7 +4191,9 @@ static inline void gen_neon_negl(TCGv_i64 var, int size)
switch (size) {
case 0: gen_helper_neon_negl_u16(var, var); break;
case 1: gen_helper_neon_negl_u32(var, var); break;
- case 2: gen_helper_neon_negl_u64(var, var); break;
+ case 2:
+ tcg_gen_neg_i64(var, var);
+ break;
default: abort();
}
}
diff --git a/target-cris/helper.h b/target-cris/helper.h
index 99fb326a85..fe12083a16 100644
--- a/target-cris/helper.h
+++ b/target-cris/helper.h
@@ -10,19 +10,19 @@ DEF_HELPER_1(rfn, void, env);
DEF_HELPER_3(movl_sreg_reg, void, env, i32, i32)
DEF_HELPER_3(movl_reg_sreg, void, env, i32, i32)
-DEF_HELPER_FLAGS_1(lz, TCG_CALL_PURE, i32, i32);
-DEF_HELPER_FLAGS_4(btst, TCG_CALL_PURE, i32, env, i32, i32, i32);
+DEF_HELPER_FLAGS_1(lz, TCG_CALL_NO_SE, i32, i32);
+DEF_HELPER_FLAGS_4(btst, TCG_CALL_NO_SE, i32, env, i32, i32, i32);
-DEF_HELPER_FLAGS_4(evaluate_flags_muls, TCG_CALL_PURE, i32, env, i32, i32, i32)
-DEF_HELPER_FLAGS_4(evaluate_flags_mulu, TCG_CALL_PURE, i32, env, i32, i32, i32)
-DEF_HELPER_FLAGS_5(evaluate_flags_mcp, TCG_CALL_PURE, i32, env,
+DEF_HELPER_FLAGS_4(evaluate_flags_muls, TCG_CALL_NO_SE, i32, env, i32, i32, i32)
+DEF_HELPER_FLAGS_4(evaluate_flags_mulu, TCG_CALL_NO_SE, i32, env, i32, i32, i32)
+DEF_HELPER_FLAGS_5(evaluate_flags_mcp, TCG_CALL_NO_SE, i32, env,
i32, i32, i32, i32)
-DEF_HELPER_FLAGS_5(evaluate_flags_alu_4, TCG_CALL_PURE, i32, env,
+DEF_HELPER_FLAGS_5(evaluate_flags_alu_4, TCG_CALL_NO_SE, i32, env,
i32, i32, i32, i32)
-DEF_HELPER_FLAGS_5(evaluate_flags_sub_4, TCG_CALL_PURE, i32, env,
+DEF_HELPER_FLAGS_5(evaluate_flags_sub_4, TCG_CALL_NO_SE, i32, env,
i32, i32, i32, i32)
-DEF_HELPER_FLAGS_3(evaluate_flags_move_4, TCG_CALL_PURE, i32, env, i32, i32)
-DEF_HELPER_FLAGS_3(evaluate_flags_move_2, TCG_CALL_PURE, i32, env, i32, i32)
+DEF_HELPER_FLAGS_3(evaluate_flags_move_4, TCG_CALL_NO_SE, i32, env, i32, i32)
+DEF_HELPER_FLAGS_3(evaluate_flags_move_2, TCG_CALL_NO_SE, i32, env, i32, i32)
DEF_HELPER_1(evaluate_flags, void, env)
DEF_HELPER_1(top_evaluate_flags, void, env)
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index f3708e63b7..6411042b79 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -74,6 +74,7 @@ static const char *ext2_feature_name[] = {
NULL /* pat */, NULL /* pse36 */, NULL, NULL /* Linux mp */,
"nx|xd", NULL, "mmxext", NULL /* mmx */,
NULL /* fxsr */, "fxsr_opt|ffxsr", "pdpe1gb" /* AMD Page1GB */, "rdtscp",
+ NULL, "lm|i64", "3dnowext", "3dnow",
};
static const char *ext3_feature_name[] = {
"lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */,
diff --git a/target-i386/helper.h b/target-i386/helper.h
index 93850ceecd..970fcd98ff 100644
--- a/target-i386/helper.h
+++ b/target-i386/helper.h
@@ -1,7 +1,7 @@
#include "def-helper.h"
-DEF_HELPER_FLAGS_2(cc_compute_all, TCG_CALL_PURE, i32, env, int)
-DEF_HELPER_FLAGS_2(cc_compute_c, TCG_CALL_PURE, i32, env, int)
+DEF_HELPER_FLAGS_2(cc_compute_all, TCG_CALL_NO_SE, i32, env, int)
+DEF_HELPER_FLAGS_2(cc_compute_c, TCG_CALL_NO_SE, i32, env, int)
DEF_HELPER_0(lock, void)
DEF_HELPER_0(unlock, void)
diff --git a/target-microblaze/helper.h b/target-microblaze/helper.h
index a1a732cfdc..a667122e91 100644
--- a/target-microblaze/helper.h
+++ b/target-microblaze/helper.h
@@ -2,10 +2,10 @@
DEF_HELPER_2(raise_exception, void, env, i32)
DEF_HELPER_1(debug, void, env)
-DEF_HELPER_FLAGS_3(carry, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_3(carry, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32)
DEF_HELPER_2(cmp, i32, i32, i32)
DEF_HELPER_2(cmpu, i32, i32, i32)
-DEF_HELPER_FLAGS_1(clz, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32)
+DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, i32, i32)
DEF_HELPER_3(divs, i32, env, i32, i32)
DEF_HELPER_3(divu, i32, env, i32, i32)
@@ -26,7 +26,7 @@ DEF_HELPER_3(fcmp_gt, i32, env, i32, i32)
DEF_HELPER_3(fcmp_ne, i32, env, i32, i32)
DEF_HELPER_3(fcmp_ge, i32, env, i32, i32)
-DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32)
+DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32)
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_2(mmu_read, i32, env, i32)
DEF_HELPER_3(mmu_write, void, env, i32, i32)
diff --git a/target-mips/helper.h b/target-mips/helper.h
index f35ed78c16..43ac39ff41 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -23,11 +23,11 @@ DEF_HELPER_4(scd, tl, env, tl, tl, int)
#endif
#endif
-DEF_HELPER_FLAGS_1(clo, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
-DEF_HELPER_FLAGS_1(clz, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(clo, TCG_CALL_NO_RWG_SE, tl, tl)
+DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, tl, tl)
#ifdef TARGET_MIPS64
-DEF_HELPER_FLAGS_1(dclo, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
-DEF_HELPER_FLAGS_1(dclz, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(dclo, TCG_CALL_NO_RWG_SE, tl, tl)
+DEF_HELPER_FLAGS_1(dclz, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_3(dmult, void, env, tl, tl)
DEF_HELPER_3(dmultu, void, env, tl, tl)
#endif
@@ -304,62 +304,62 @@ DEF_HELPER_2(pmon, void, env, int)
DEF_HELPER_1(wait, void, env)
/* Loongson multimedia functions. */
-DEF_HELPER_FLAGS_2(paddsh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(paddush, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(paddh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(paddw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(paddsb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(paddusb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(paddb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(paddsh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(paddush, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(paddh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(paddw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(paddsb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(paddusb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(paddb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psubsh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psubush, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psubh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psubw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psubsb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psubusb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psubb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psubsh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psubush, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psubh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psubw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psubsb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psubusb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psubb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pshufh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(packsswh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(packsshb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(packushb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pshufh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(packsswh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(packsshb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(packushb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(punpcklhw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(punpckhhw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(punpcklbh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(punpckhbh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(punpcklwd, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(punpckhwd, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(punpcklhw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(punpckhhw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(punpcklbh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(punpckhbh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(punpcklwd, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(punpckhwd, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pavgh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pavgb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pmaxsh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pminsh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pmaxub, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pminub, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pavgh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pavgb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pmaxsh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pminsh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pmaxub, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pminub, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pcmpeqw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pcmpgtw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pcmpeqh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pcmpgth, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pcmpeqb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pcmpgtb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pcmpeqw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pcmpgtw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pcmpeqh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pcmpgth, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pcmpeqb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pcmpgtb, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psllw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psllh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psrlw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psrlh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psraw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(psrah, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psllw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psllh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psrlw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psrlh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psraw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(psrah, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pmullh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pmulhh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pmulhuh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pmaddhw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pmullh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pmulhh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pmulhuh, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(pmaddhw, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(pasubub, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_1(biadd, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
-DEF_HELPER_FLAGS_1(pmovmskb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+DEF_HELPER_FLAGS_2(pasubub, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_1(biadd, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(pmovmskb, TCG_CALL_NO_RWG_SE, i64, i64)
#include "def-helper.h"
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 454e5cc818..ed55e260ac 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -575,6 +575,7 @@ static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC], cpu_ACX[MIPS_DSP_ACC];
static TCGv cpu_dspctrl, btarget, bcond;
static TCGv_i32 hflags;
static TCGv_i32 fpu_fcr0, fpu_fcr31;
+static TCGv_i64 fpu_f64[32];
static uint32_t gen_opc_hflags[OPC_BUF_SIZE];
@@ -642,26 +643,31 @@ enum {
BS_EXCP = 3, /* We reached an exception condition */
};
-static const char *regnames[] =
- { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
- "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
- "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
- "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", };
+static const char * const regnames[] = {
+ "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
+};
-static const char *regnames_HI[] =
- { "HI0", "HI1", "HI2", "HI3", };
+static const char * const regnames_HI[] = {
+ "HI0", "HI1", "HI2", "HI3",
+};
-static const char *regnames_LO[] =
- { "LO0", "LO1", "LO2", "LO3", };
+static const char * const regnames_LO[] = {
+ "LO0", "LO1", "LO2", "LO3",
+};
-static const char *regnames_ACX[] =
- { "ACX0", "ACX1", "ACX2", "ACX3", };
+static const char * const regnames_ACX[] = {
+ "ACX0", "ACX1", "ACX2", "ACX3",
+};
-static const char *fregnames[] =
- { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
- "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
- "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
- "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", };
+static const char * const fregnames[] = {
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
+};
#define MIPS_DEBUG(fmt, ...) \
do { \
@@ -758,54 +764,54 @@ static inline void gen_store_srsgpr (int from, int to)
}
/* Floating point register moves. */
-static inline void gen_load_fpr32 (TCGv_i32 t, int reg)
+static void gen_load_fpr32(TCGv_i32 t, int reg)
{
- tcg_gen_ld_i32(t, cpu_env, offsetof(CPUMIPSState, active_fpu.fpr[reg].w[FP_ENDIAN_IDX]));
+ tcg_gen_trunc_i64_i32(t, fpu_f64[reg]);
}
-static inline void gen_store_fpr32 (TCGv_i32 t, int reg)
+static void gen_store_fpr32(TCGv_i32 t, int reg)
{
- tcg_gen_st_i32(t, cpu_env, offsetof(CPUMIPSState, active_fpu.fpr[reg].w[FP_ENDIAN_IDX]));
+ TCGv_i64 t64 = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(t64, t);
+ tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 0, 32);
+ tcg_temp_free_i64(t64);
}
-static inline void gen_load_fpr32h (TCGv_i32 t, int reg)
+static void gen_load_fpr32h(TCGv_i32 t, int reg)
{
- tcg_gen_ld_i32(t, cpu_env, offsetof(CPUMIPSState, active_fpu.fpr[reg].w[!FP_ENDIAN_IDX]));
+ TCGv_i64 t64 = tcg_temp_new_i64();
+ tcg_gen_shri_i64(t64, fpu_f64[reg], 32);
+ tcg_gen_trunc_i64_i32(t, t64);
+ tcg_temp_free_i64(t64);
}
-static inline void gen_store_fpr32h (TCGv_i32 t, int reg)
+static void gen_store_fpr32h(TCGv_i32 t, int reg)
{
- tcg_gen_st_i32(t, cpu_env, offsetof(CPUMIPSState, active_fpu.fpr[reg].w[!FP_ENDIAN_IDX]));
+ TCGv_i64 t64 = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(t64, t);
+ tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 32, 32);
+ tcg_temp_free_i64(t64);
}
-static inline void gen_load_fpr64 (DisasContext *ctx, TCGv_i64 t, int reg)
+static void gen_load_fpr64(DisasContext *ctx, TCGv_i64 t, int reg)
{
if (ctx->hflags & MIPS_HFLAG_F64) {
- tcg_gen_ld_i64(t, cpu_env, offsetof(CPUMIPSState, active_fpu.fpr[reg].d));
+ tcg_gen_mov_i64(t, fpu_f64[reg]);
} else {
- TCGv_i32 t0 = tcg_temp_new_i32();
- TCGv_i32 t1 = tcg_temp_new_i32();
- gen_load_fpr32(t0, reg & ~1);
- gen_load_fpr32(t1, reg | 1);
- tcg_gen_concat_i32_i64(t, t0, t1);
- tcg_temp_free_i32(t0);
- tcg_temp_free_i32(t1);
+ tcg_gen_concat32_i64(t, fpu_f64[reg & ~1], fpu_f64[reg | 1]);
}
}
-static inline void gen_store_fpr64 (DisasContext *ctx, TCGv_i64 t, int reg)
+static void gen_store_fpr64(DisasContext *ctx, TCGv_i64 t, int reg)
{
if (ctx->hflags & MIPS_HFLAG_F64) {
- tcg_gen_st_i64(t, cpu_env, offsetof(CPUMIPSState, active_fpu.fpr[reg].d));
+ tcg_gen_mov_i64(fpu_f64[reg], t);
} else {
- TCGv_i64 t0 = tcg_temp_new_i64();
- TCGv_i32 t1 = tcg_temp_new_i32();
- tcg_gen_trunc_i64_i32(t1, t);
- gen_store_fpr32(t1, reg & ~1);
+ TCGv_i64 t0;
+ tcg_gen_deposit_i64(fpu_f64[reg & ~1], fpu_f64[reg & ~1], t, 0, 32);
+ t0 = tcg_temp_new_i64();
tcg_gen_shri_i64(t0, t, 32);
- tcg_gen_trunc_i64_i32(t1, t0);
- gen_store_fpr32(t1, reg | 1);
- tcg_temp_free_i32(t1);
+ tcg_gen_deposit_i64(fpu_f64[reg | 1], fpu_f64[reg | 1], t0, 0, 32);
tcg_temp_free_i64(t0);
}
}
@@ -13073,6 +13079,12 @@ static void mips_tcg_init(void)
cpu_gpr[i] = tcg_global_mem_new(TCG_AREG0,
offsetof(CPUMIPSState, active_tc.gpr[i]),
regnames[i]);
+
+ for (i = 0; i < 32; i++) {
+ int off = offsetof(CPUMIPSState, active_fpu.fpr[i]);
+ fpu_f64[i] = tcg_global_mem_new_i64(TCG_AREG0, off, fregnames[i]);
+ }
+
cpu_PC = tcg_global_mem_new(TCG_AREG0,
offsetof(CPUMIPSState, active_tc.PC), "PC");
for (i = 0; i < MIPS_DSP_ACC; i++) {
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 3f114c983b..286f42a808 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -1045,10 +1045,9 @@ struct CPUPPCState {
#endif
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
- hwaddr vpa;
- hwaddr slb_shadow;
- hwaddr dispatch_trace_log;
- uint32_t dtl_size;
+ hwaddr vpa_addr;
+ hwaddr slb_shadow_addr, slb_shadow_size;
+ hwaddr dtl_addr, dtl_size;
#endif /* TARGET_PPC64 */
int error_code;
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index fd04c063e2..e588370e29 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -31,24 +31,24 @@ DEF_HELPER_2(icbi, void, env, tl)
DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32)
#if defined(TARGET_PPC64)
-DEF_HELPER_FLAGS_2(mulhd, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(mulhdu, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(mulhd, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(mulhdu, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_3(mulldo, i64, env, i64, i64)
#endif
-DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
-DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
-DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_NO_RWG_SE, tl, tl)
+DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl)
+DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_3(sraw, tl, env, tl, tl)
#if defined(TARGET_PPC64)
-DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
-DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_NO_RWG_SE, tl, tl)
+DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_3(srad, tl, env, tl, tl)
#endif
-DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
-DEF_HELPER_FLAGS_1(cntlzw32, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
-DEF_HELPER_FLAGS_2(brinc, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl, tl)
+DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_NO_RWG_SE, i32, i32)
+DEF_HELPER_FLAGS_1(cntlzw32, TCG_CALL_NO_RWG_SE, i32, i32)
+DEF_HELPER_FLAGS_2(brinc, TCG_CALL_NO_RWG_SE, tl, tl, tl)
DEF_HELPER_1(float_check_status, void, env)
DEF_HELPER_1(reset_fpstatus, void, env)
@@ -345,25 +345,25 @@ DEF_HELPER_2(6xx_tlbd, void, env, tl)
DEF_HELPER_2(6xx_tlbi, void, env, tl)
DEF_HELPER_2(74xx_tlbd, void, env, tl)
DEF_HELPER_2(74xx_tlbi, void, env, tl)
-DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_CONST, void, env)
-DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_CONST, void, env, tl)
+DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_NO_RWG, void, env)
+DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_NO_RWG, void, env, tl)
#if defined(TARGET_PPC64)
-DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_CONST, void, env, tl, tl)
+DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl)
DEF_HELPER_2(load_slb_esid, tl, env, tl)
DEF_HELPER_2(load_slb_vsid, tl, env, tl)
-DEF_HELPER_FLAGS_1(slbia, TCG_CALL_CONST, void, env)
-DEF_HELPER_FLAGS_2(slbie, TCG_CALL_CONST, void, env, tl)
+DEF_HELPER_FLAGS_1(slbia, TCG_CALL_NO_RWG, void, env)
+DEF_HELPER_FLAGS_2(slbie, TCG_CALL_NO_RWG, void, env, tl)
#endif
-DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_CONST, tl, env, tl);
-DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_CONST, void, env, tl, tl)
+DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_NO_RWG, tl, env, tl);
+DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl)
-DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_1(msgsnd, void, tl)
DEF_HELPER_2(msgclr, void, env, tl)
#endif
DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32)
-DEF_HELPER_FLAGS_2(clcs, TCG_CALL_CONST | TCG_CALL_PURE, tl, env, i32)
+DEF_HELPER_FLAGS_2(clcs, TCG_CALL_NO_RWG_SE, tl, env, i32)
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_2(rac, tl, env, tl)
#endif
diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c
index 4a9bb5b965..811f47f309 100644
--- a/target-ppc/mmu_helper.c
+++ b/target-ppc/mmu_helper.c
@@ -1509,10 +1509,8 @@ static void mmubooke_dump_mmu(FILE *f, fprintf_function cpu_fprintf,
mask = ~(entry->size - 1);
ea = entry->EPN & mask;
pa = entry->RPN & mask;
-#if (TARGET_PHYS_ADDR_SPACE_BITS >= 36)
/* Extend the physical address to 36 bits */
pa |= (hwaddr)(entry->RPN & 0xF) << 32;
-#endif
size /= 1024;
if (size >= 1024) {
snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / 1024);
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index a972287035..e63627cac1 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -1498,7 +1498,7 @@ static void gen_spr_BookE (CPUPPCState *env, uint64_t ivor_mask)
/* XXX : not implemented */
spr_register(env, SPR_BOOKE_DBCR0, "DBCR0",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_40x_dbcr0,
0x00000000);
/* XXX : not implemented */
spr_register(env, SPR_BOOKE_DBCR1, "DBCR1",
@@ -10425,9 +10425,10 @@ static void ppc_cpu_reset(CPUState *s)
env->error_code = 0;
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
- env->vpa = 0;
- env->slb_shadow = 0;
- env->dispatch_trace_log = 0;
+ env->vpa_addr = 0;
+ env->slb_shadow_addr = 0;
+ env->slb_shadow_size = 0;
+ env->dtl_addr = 0;
env->dtl_size = 0;
#endif /* TARGET_PPC64 */
diff --git a/target-s390x/helper.h b/target-s390x/helper.h
index 5419f37dca..ac44eabd53 100644
--- a/target-s390x/helper.h
+++ b/target-s390x/helper.h
@@ -7,21 +7,21 @@ DEF_HELPER_4(xc, i32, env, i32, i64, i64)
DEF_HELPER_4(mvc, void, env, i32, i64, i64)
DEF_HELPER_4(clc, i32, env, i32, i64, i64)
DEF_HELPER_3(mvcl, i32, env, i32, i32)
-DEF_HELPER_FLAGS_1(set_cc_comp_s32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32)
-DEF_HELPER_FLAGS_1(set_cc_comp_s64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64)
-DEF_HELPER_FLAGS_2(set_cc_icm, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32)
+DEF_HELPER_FLAGS_1(set_cc_comp_s32, TCG_CALL_NO_RWG_SE, i32, s32)
+DEF_HELPER_FLAGS_1(set_cc_comp_s64, TCG_CALL_NO_RWG_SE, i32, s64)
+DEF_HELPER_FLAGS_2(set_cc_icm, TCG_CALL_NO_RWG_SE, i32, i32, i32)
DEF_HELPER_4(clm, i32, env, i32, i32, i64)
DEF_HELPER_4(stcm, void, env, i32, i32, i64)
DEF_HELPER_3(mlg, void, env, i32, i64)
DEF_HELPER_3(dlg, void, env, i32, i64)
-DEF_HELPER_FLAGS_3(set_cc_add64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64, s64, s64)
-DEF_HELPER_FLAGS_3(set_cc_addu64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64)
-DEF_HELPER_FLAGS_3(set_cc_add32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32, s32, s32)
-DEF_HELPER_FLAGS_3(set_cc_addu32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32)
-DEF_HELPER_FLAGS_3(set_cc_sub64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64, s64, s64)
-DEF_HELPER_FLAGS_3(set_cc_subu64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64)
-DEF_HELPER_FLAGS_3(set_cc_sub32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32, s32, s32)
-DEF_HELPER_FLAGS_3(set_cc_subu32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_3(set_cc_add64, TCG_CALL_NO_RWG_SE, i32, s64, s64, s64)
+DEF_HELPER_FLAGS_3(set_cc_addu64, TCG_CALL_NO_RWG_SE, i32, i64, i64, i64)
+DEF_HELPER_FLAGS_3(set_cc_add32, TCG_CALL_NO_RWG_SE, i32, s32, s32, s32)
+DEF_HELPER_FLAGS_3(set_cc_addu32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_3(set_cc_sub64, TCG_CALL_NO_RWG_SE, i32, s64, s64, s64)
+DEF_HELPER_FLAGS_3(set_cc_subu64, TCG_CALL_NO_RWG_SE, i32, i64, i64, i64)
+DEF_HELPER_FLAGS_3(set_cc_sub32, TCG_CALL_NO_RWG_SE, i32, s32, s32, s32)
+DEF_HELPER_FLAGS_3(set_cc_subu32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32)
DEF_HELPER_4(srst, i32, env, i32, i32, i32)
DEF_HELPER_4(clst, i32, env, i32, i32, i32)
DEF_HELPER_4(mvpg, void, env, i64, i64, i64)
@@ -30,15 +30,15 @@ DEF_HELPER_4(csg, i32, env, i32, i64, i32)
DEF_HELPER_4(cdsg, i32, env, i32, i64, i32)
DEF_HELPER_4(cs, i32, env, i32, i64, i32)
DEF_HELPER_5(ex, i32, env, i32, i64, i64, i64)
-DEF_HELPER_FLAGS_1(abs_i32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32)
-DEF_HELPER_FLAGS_1(nabs_i32, TCG_CALL_PURE|TCG_CALL_CONST, s32, s32)
-DEF_HELPER_FLAGS_1(abs_i64, TCG_CALL_PURE|TCG_CALL_CONST, i64, s64)
-DEF_HELPER_FLAGS_1(nabs_i64, TCG_CALL_PURE|TCG_CALL_CONST, s64, s64)
+DEF_HELPER_FLAGS_1(abs_i32, TCG_CALL_NO_RWG_SE, i32, s32)
+DEF_HELPER_FLAGS_1(nabs_i32, TCG_CALL_NO_RWG_SE, s32, s32)
+DEF_HELPER_FLAGS_1(abs_i64, TCG_CALL_NO_RWG_SE, i64, s64)
+DEF_HELPER_FLAGS_1(nabs_i64, TCG_CALL_NO_RWG_SE, s64, s64)
DEF_HELPER_4(stcmh, void, env, i32, i64, i32)
DEF_HELPER_4(icmh, i32, env, i32, i64, i32)
DEF_HELPER_3(ipm, void, env, i32, i32)
-DEF_HELPER_FLAGS_3(addc_u32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32)
-DEF_HELPER_FLAGS_3(set_cc_addc_u64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64)
+DEF_HELPER_FLAGS_3(addc_u32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_3(set_cc_addc_u64, TCG_CALL_NO_RWG_SE, i32, i64, i64, i64)
DEF_HELPER_4(stam, void, env, i32, i64, i32)
DEF_HELPER_4(lam, void, env, i32, i64, i32)
DEF_HELPER_4(mvcle, i32, env, i32, i64, i32)
@@ -82,9 +82,9 @@ DEF_HELPER_3(seb, void, env, i32, i32)
DEF_HELPER_3(sdb, i32, env, i32, i64)
DEF_HELPER_3(mdb, void, env, i32, i64)
DEF_HELPER_3(ddb, void, env, i32, i64)
-DEF_HELPER_FLAGS_3(cebr, TCG_CALL_PURE, i32, env, i32, i32)
-DEF_HELPER_FLAGS_3(cdbr, TCG_CALL_PURE, i32, env, i32, i32)
-DEF_HELPER_FLAGS_3(cxbr, TCG_CALL_PURE, i32, env, i32, i32)
+DEF_HELPER_FLAGS_3(cebr, TCG_CALL_NO_SE, i32, env, i32, i32)
+DEF_HELPER_FLAGS_3(cdbr, TCG_CALL_NO_SE, i32, env, i32, i32)
+DEF_HELPER_FLAGS_3(cxbr, TCG_CALL_NO_SE, i32, env, i32, i32)
DEF_HELPER_4(cgebr, i32, env, i32, i32, i32)
DEF_HELPER_4(cgdbr, i32, env, i32, i32, i32)
DEF_HELPER_4(cgxbr, i32, env, i32, i32, i32)
@@ -104,12 +104,12 @@ DEF_HELPER_4(madbr, void, env, i32, i32, i32)
DEF_HELPER_4(msdbr, void, env, i32, i32, i32)
DEF_HELPER_3(ldeb, void, env, i32, i64)
DEF_HELPER_3(lxdb, void, env, i32, i64)
-DEF_HELPER_FLAGS_3(tceb, TCG_CALL_PURE, i32, env, i32, i64)
-DEF_HELPER_FLAGS_3(tcdb, TCG_CALL_PURE, i32, env, i32, i64)
-DEF_HELPER_FLAGS_3(tcxb, TCG_CALL_PURE, i32, env, i32, i64)
+DEF_HELPER_FLAGS_3(tceb, TCG_CALL_NO_SE, i32, env, i32, i64)
+DEF_HELPER_FLAGS_3(tcdb, TCG_CALL_NO_SE, i32, env, i32, i64)
+DEF_HELPER_FLAGS_3(tcxb, TCG_CALL_NO_SE, i32, env, i32, i64)
DEF_HELPER_3(flogr, i32, env, i32, i64)
DEF_HELPER_3(sqdbr, void, env, i32, i32)
-DEF_HELPER_FLAGS_1(cvd, TCG_CALL_PURE|TCG_CALL_CONST, i64, s32)
+DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32)
DEF_HELPER_4(unpk, void, env, i32, i64, i64)
DEF_HELPER_4(tr, void, env, i32, i64, i64)
@@ -117,36 +117,36 @@ DEF_HELPER_3(servc, i32, env, i32, i64)
DEF_HELPER_4(diag, i64, env, i32, i64, i64)
DEF_HELPER_3(load_psw, void, env, i64, i64)
DEF_HELPER_1(program_interrupt, void, i32)
-DEF_HELPER_FLAGS_2(stidp, TCG_CALL_CONST, void, env, i64)
-DEF_HELPER_FLAGS_2(spx, TCG_CALL_CONST, void, env, i64)
-DEF_HELPER_FLAGS_1(sck, TCG_CALL_CONST, i32, i64)
+DEF_HELPER_FLAGS_2(stidp, TCG_CALL_NO_RWG, void, env, i64)
+DEF_HELPER_FLAGS_2(spx, TCG_CALL_NO_RWG, void, env, i64)
+DEF_HELPER_FLAGS_1(sck, TCG_CALL_NO_RWG, i32, i64)
DEF_HELPER_2(stck, i32, env, i64)
DEF_HELPER_2(stcke, i32, env, i64)
-DEF_HELPER_FLAGS_2(sckc, TCG_CALL_CONST, void, env, i64)
-DEF_HELPER_FLAGS_2(stckc, TCG_CALL_CONST, void, env, i64)
-DEF_HELPER_FLAGS_2(spt, TCG_CALL_CONST, void, env, i64)
-DEF_HELPER_FLAGS_2(stpt, TCG_CALL_CONST, void, env, i64)
+DEF_HELPER_FLAGS_2(sckc, TCG_CALL_NO_RWG, void, env, i64)
+DEF_HELPER_FLAGS_2(stckc, TCG_CALL_NO_RWG, void, env, i64)
+DEF_HELPER_FLAGS_2(spt, TCG_CALL_NO_RWG, void, env, i64)
+DEF_HELPER_FLAGS_2(stpt, TCG_CALL_NO_RWG, void, env, i64)
DEF_HELPER_4(stsi, i32, env, i64, i32, i32)
DEF_HELPER_4(lctl, void, env, i32, i64, i32)
DEF_HELPER_4(lctlg, void, env, i32, i64, i32)
DEF_HELPER_4(stctl, void, env, i32, i64, i32)
DEF_HELPER_4(stctg, void, env, i32, i64, i32)
-DEF_HELPER_FLAGS_2(tprot, TCG_CALL_CONST, i32, i64, i64)
-DEF_HELPER_FLAGS_2(iske, TCG_CALL_PURE|TCG_CALL_CONST, i64, env, i64)
-DEF_HELPER_FLAGS_3(sske, TCG_CALL_CONST, void, env, i32, i64)
-DEF_HELPER_FLAGS_3(rrbe, TCG_CALL_CONST, i32, env, i32, i64)
+DEF_HELPER_FLAGS_2(tprot, TCG_CALL_NO_RWG, i32, i64, i64)
+DEF_HELPER_FLAGS_2(iske, TCG_CALL_NO_RWG_SE, i64, env, i64)
+DEF_HELPER_FLAGS_3(sske, TCG_CALL_NO_RWG, void, env, i32, i64)
+DEF_HELPER_FLAGS_3(rrbe, TCG_CALL_NO_RWG, i32, env, i32, i64)
DEF_HELPER_3(csp, i32, env, i32, i32)
DEF_HELPER_4(mvcs, i32, env, i64, i64, i64)
DEF_HELPER_4(mvcp, i32, env, i64, i64, i64)
DEF_HELPER_4(sigp, i32, env, i64, i32, i64)
DEF_HELPER_2(sacf, void, env, i64)
-DEF_HELPER_FLAGS_3(ipte, TCG_CALL_CONST, void, env, i64, i64)
-DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_CONST, void, env)
+DEF_HELPER_FLAGS_3(ipte, TCG_CALL_NO_RWG, void, env, i64, i64)
+DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_3(lra, i32, env, i64, i32)
DEF_HELPER_3(stura, void, env, i64, i32)
DEF_HELPER_3(cksm, void, env, i32, i32)
-DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_PURE|TCG_CALL_CONST,
+DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE,
i32, env, i32, i64, i64, i64)
#include "def-helper.h"
diff --git a/target-sh4/helper.h b/target-sh4/helper.h
index 6c1a47da9f..304b77ba3f 100644
--- a/target-sh4/helper.h
+++ b/target-sh4/helper.h
@@ -19,8 +19,8 @@ DEF_HELPER_3(macw, void, env, i32, i32)
DEF_HELPER_2(ld_fpscr, void, env, i32)
-DEF_HELPER_FLAGS_1(fabs_FT, TCG_CALL_CONST | TCG_CALL_PURE, f32, f32)
-DEF_HELPER_FLAGS_1(fabs_DT, TCG_CALL_CONST | TCG_CALL_PURE, f64, f64)
+DEF_HELPER_FLAGS_1(fabs_FT, TCG_CALL_NO_RWG_SE, f32, f32)
+DEF_HELPER_FLAGS_1(fabs_DT, TCG_CALL_NO_RWG_SE, f64, f64)
DEF_HELPER_3(fadd_FT, f32, env, f32, f32)
DEF_HELPER_3(fadd_DT, f64, env, f64, f64)
DEF_HELPER_2(fcnvsd_FT_DT, f64, env, f32)
@@ -37,7 +37,7 @@ DEF_HELPER_2(float_DT, f64, env, i32)
DEF_HELPER_4(fmac_FT, f32, env, f32, f32, f32)
DEF_HELPER_3(fmul_FT, f32, env, f32, f32)
DEF_HELPER_3(fmul_DT, f64, env, f64, f64)
-DEF_HELPER_FLAGS_1(fneg_T, TCG_CALL_CONST | TCG_CALL_PURE, f32, f32)
+DEF_HELPER_FLAGS_1(fneg_T, TCG_CALL_NO_RWG_SE, f32, f32)
DEF_HELPER_3(fsub_FT, f32, env, f32, f32)
DEF_HELPER_3(fsub_DT, f64, env, f64, f64)
DEF_HELPER_2(fsqrt_FT, f32, env, f32)
diff --git a/target-sparc/helper.h b/target-sparc/helper.h
index e1ae3c7086..098c482216 100644
--- a/target-sparc/helper.h
+++ b/target-sparc/helper.h
@@ -16,7 +16,7 @@ DEF_HELPER_1(rdccr, tl, env)
DEF_HELPER_2(wrccr, void, env, tl)
DEF_HELPER_1(rdcwp, tl, env)
DEF_HELPER_2(wrcwp, void, env, tl)
-DEF_HELPER_FLAGS_2(array8, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl, tl)
+DEF_HELPER_FLAGS_2(array8, TCG_CALL_NO_RWG_SE, tl, tl, tl)
DEF_HELPER_1(popc, tl, tl)
DEF_HELPER_4(ldda_asi, void, env, tl, int, int)
DEF_HELPER_5(ldf_asi, void, env, tl, int, int, int)
@@ -51,7 +51,7 @@ DEF_HELPER_5(ld_asi, i64, env, tl, int, int, int)
DEF_HELPER_5(st_asi, void, env, tl, i64, int, int)
#endif
DEF_HELPER_2(ldfsr, void, env, i32)
-DEF_HELPER_FLAGS_1(fabss, TCG_CALL_CONST | TCG_CALL_PURE, f32, f32)
+DEF_HELPER_FLAGS_1(fabss, TCG_CALL_NO_RWG_SE, f32, f32)
DEF_HELPER_2(fsqrts, f32, env, f32)
DEF_HELPER_2(fsqrtd, f64, env, f64)
DEF_HELPER_3(fcmps, void, env, f32, f32)
@@ -63,7 +63,7 @@ DEF_HELPER_1(fcmpq, void, env)
DEF_HELPER_1(fcmpeq, void, env)
#ifdef TARGET_SPARC64
DEF_HELPER_2(ldxfsr, void, env, i64)
-DEF_HELPER_FLAGS_1(fabsd, TCG_CALL_CONST | TCG_CALL_PURE, f64, f64)
+DEF_HELPER_FLAGS_1(fabsd, TCG_CALL_NO_RWG_SE, f64, f64)
DEF_HELPER_3(fcmps_fcc1, void, env, f32, f32)
DEF_HELPER_3(fcmps_fcc2, void, env, f32, f32)
DEF_HELPER_3(fcmps_fcc3, void, env, f32, f32)
@@ -104,14 +104,14 @@ DEF_HELPER_3(fdivs, f32, env, f32, f32)
DEF_HELPER_3(fsmuld, f64, env, f32, f32)
DEF_HELPER_3(fdmulq, void, env, f64, f64);
-DEF_HELPER_FLAGS_1(fnegs, TCG_CALL_CONST | TCG_CALL_PURE, f32, f32)
+DEF_HELPER_FLAGS_1(fnegs, TCG_CALL_NO_RWG_SE, f32, f32)
DEF_HELPER_2(fitod, f64, env, s32)
DEF_HELPER_2(fitoq, void, env, s32)
DEF_HELPER_2(fitos, f32, env, s32)
#ifdef TARGET_SPARC64
-DEF_HELPER_FLAGS_1(fnegd, TCG_CALL_CONST | TCG_CALL_PURE, f64, f64)
+DEF_HELPER_FLAGS_1(fnegd, TCG_CALL_NO_RWG_SE, f64, f64)
DEF_HELPER_1(fnegq, void, env)
DEF_HELPER_2(fxtos, f32, env, s64)
DEF_HELPER_2(fxtod, f64, env, s64)
@@ -131,36 +131,36 @@ DEF_HELPER_2(fstox, s64, env, f32)
DEF_HELPER_2(fdtox, s64, env, f64)
DEF_HELPER_1(fqtox, s64, env)
-DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fexpand, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
-DEF_HELPER_FLAGS_3(pdist, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64, i64)
-DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fpackfix, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64, i64)
-DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fexpand, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_3(pdist, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64)
+DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fpackfix, TCG_CALL_NO_RWG_SE, i32, i64, i64)
+DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
#define VIS_HELPER(name) \
- DEF_HELPER_FLAGS_2(f ## name ## 16, TCG_CALL_CONST | TCG_CALL_PURE, \
+ DEF_HELPER_FLAGS_2(f ## name ## 16, TCG_CALL_NO_RWG_SE, \
i64, i64, i64) \
- DEF_HELPER_FLAGS_2(f ## name ## 16s, TCG_CALL_CONST | TCG_CALL_PURE, \
+ DEF_HELPER_FLAGS_2(f ## name ## 16s, TCG_CALL_NO_RWG_SE, \
i32, i32, i32) \
- DEF_HELPER_FLAGS_2(f ## name ## 32, TCG_CALL_CONST | TCG_CALL_PURE, \
+ DEF_HELPER_FLAGS_2(f ## name ## 32, TCG_CALL_NO_RWG_SE, \
i64, i64, i64) \
- DEF_HELPER_FLAGS_2(f ## name ## 32s, TCG_CALL_CONST | TCG_CALL_PURE, \
+ DEF_HELPER_FLAGS_2(f ## name ## 32s, TCG_CALL_NO_RWG_SE, \
i32, i32, i32)
VIS_HELPER(padd);
VIS_HELPER(psub);
#define VIS_CMPHELPER(name) \
- DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_CONST | TCG_CALL_PURE, \
+ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \
i64, i64, i64) \
- DEF_HELPER_FLAGS_2(f##name##32, TCG_CALL_CONST | TCG_CALL_PURE, \
+ DEF_HELPER_FLAGS_2(f##name##32, TCG_CALL_NO_RWG_SE, \
i64, i64, i64)
VIS_CMPHELPER(cmpgt);
VIS_CMPHELPER(cmpeq);
diff --git a/target-xtensa/helper.h b/target-xtensa/helper.h
index 4cc0088c36..1163c09836 100644
--- a/target-xtensa/helper.h
+++ b/target-xtensa/helper.h
@@ -5,8 +5,8 @@ DEF_HELPER_3(exception_cause, noreturn, env, i32, i32)
DEF_HELPER_4(exception_cause_vaddr, noreturn, env, i32, i32, i32)
DEF_HELPER_3(debug_exception, noreturn, env, i32, i32)
-DEF_HELPER_FLAGS_1(nsa, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
-DEF_HELPER_FLAGS_1(nsau, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
+DEF_HELPER_FLAGS_1(nsa, TCG_CALL_NO_RWG_SE, i32, i32)
+DEF_HELPER_FLAGS_1(nsau, TCG_CALL_NO_RWG_SE, i32, i32)
DEF_HELPER_2(wsr_windowbase, void, env, i32)
DEF_HELPER_4(entry, void, env, i32, i32, i32)
DEF_HELPER_2(retw, i32, env, i32)
@@ -25,8 +25,8 @@ DEF_HELPER_2(advance_ccount, void, env, i32)
DEF_HELPER_1(check_interrupts, void, env)
DEF_HELPER_2(wsr_rasid, void, env, i32)
-DEF_HELPER_FLAGS_3(rtlb0, TCG_CALL_CONST | TCG_CALL_PURE, i32, env, i32, i32)
-DEF_HELPER_FLAGS_3(rtlb1, TCG_CALL_CONST | TCG_CALL_PURE, i32, env, i32, i32)
+DEF_HELPER_FLAGS_3(rtlb0, TCG_CALL_NO_RWG_SE, i32, env, i32, i32)
+DEF_HELPER_FLAGS_3(rtlb1, TCG_CALL_NO_RWG_SE, i32, env, i32, i32)
DEF_HELPER_3(itlb, void, env, i32, i32)
DEF_HELPER_3(ptlb, i32, env, i32, i32)
DEF_HELPER_4(wtlb, void, env, i32, i32, i32)
@@ -37,15 +37,15 @@ DEF_HELPER_3(wsr_dbreaka, void, env, i32, i32)
DEF_HELPER_3(wsr_dbreakc, void, env, i32, i32)
DEF_HELPER_2(wur_fcr, void, env, i32)
-DEF_HELPER_FLAGS_1(abs_s, TCG_CALL_CONST | TCG_CALL_PURE, f32, f32)
-DEF_HELPER_FLAGS_1(neg_s, TCG_CALL_CONST | TCG_CALL_PURE, f32, f32)
+DEF_HELPER_FLAGS_1(abs_s, TCG_CALL_NO_RWG_SE, f32, f32)
+DEF_HELPER_FLAGS_1(neg_s, TCG_CALL_NO_RWG_SE, f32, f32)
DEF_HELPER_3(add_s, f32, env, f32, f32)
DEF_HELPER_3(sub_s, f32, env, f32, f32)
DEF_HELPER_3(mul_s, f32, env, f32, f32)
DEF_HELPER_4(madd_s, f32, env, f32, f32, f32)
DEF_HELPER_4(msub_s, f32, env, f32, f32, f32)
-DEF_HELPER_FLAGS_3(ftoi, TCG_CALL_CONST | TCG_CALL_PURE, i32, f32, i32, i32)
-DEF_HELPER_FLAGS_3(ftoui, TCG_CALL_CONST | TCG_CALL_PURE, i32, f32, i32, i32)
+DEF_HELPER_FLAGS_3(ftoi, TCG_CALL_NO_RWG_SE, i32, f32, i32, i32)
+DEF_HELPER_FLAGS_3(ftoui, TCG_CALL_NO_RWG_SE, i32, f32, i32, i32)
DEF_HELPER_3(itof, f32, env, i32, i32)
DEF_HELPER_3(uitof, f32, env, i32, i32)
diff --git a/tcg/README b/tcg/README
index aa86992bca..ec1ac79375 100644
--- a/tcg/README
+++ b/tcg/README
@@ -77,11 +77,20 @@ destroyed, but local temporaries and globals are preserved.
Using the tcg_gen_helper_x_y it is possible to call any function
taking i32, i64 or pointer types. By default, before calling a helper,
all globals are stored at their canonical location and it is assumed
-that the function can modify them. This can be overridden by the
-TCG_CALL_CONST function modifier. By default, the helper is allowed to
-modify the CPU state or raise an exception. This can be overridden by
-the TCG_CALL_PURE function modifier, in which case the call to the
-function is removed if the return value is not used.
+that the function can modify them. By default, the helper is allowed to
+modify the CPU state or raise an exception.
+
+This can be overridden using the following function modifiers:
+- TCG_CALL_NO_READ_GLOBALS means that the helper does not read globals,
+ either directly or via an exception. They will not be saved to their
+ canonical locations before calling the helper.
+- TCG_CALL_NO_WRITE_GLOBALS means that the helper does not modify any globals.
+ They will only be saved to their canonical location before calling helpers,
+ but they won't be reloaded afterwise.
+- TCG_CALL_NO_SIDE_EFFECTS means that the call to the function is removed if
+ the return value is not used.
+
+Note that TCG_CALL_NO_READ_GLOBALS implies TCG_CALL_NO_WRITE_GLOBALS.
On some TCG targets (e.g. x86), several calling conventions are
supported.
@@ -349,6 +358,9 @@ st32_i64 t0, t1, offset
write(t0, t1 + offset)
Write 8, 16, 32 or 64 bits to host memory.
+All this opcodes assume that the pointed host memory doesn't correspond
+to a global. In the latter case the behaviour is unpredictable.
+
********* 64-bit target on 32-bit host support
The following opcodes are internal to TCG. Thus they are to be implemented by
diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c
index 4952c057b3..e45a5a0426 100644
--- a/tcg/i386/tcg-target.c
+++ b/tcg/i386/tcg-target.c
@@ -92,7 +92,6 @@ static const int tcg_target_call_oarg_regs[] = {
#if TCG_TARGET_REG_BITS == 64
# define TCG_REG_L0 tcg_target_call_iarg_regs[0]
# define TCG_REG_L1 tcg_target_call_iarg_regs[1]
-# define TCG_REG_L2 tcg_target_call_iarg_regs[2]
#else
# define TCG_REG_L0 TCG_REG_EAX
# define TCG_REG_L1 TCG_REG_EDX
@@ -181,14 +180,11 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
ct->ct |= TCG_CT_REG;
#if TCG_TARGET_REG_BITS == 64
tcg_regset_set32(ct->u.regs, 0, 0xffff);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_L0);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_L1);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_L2);
#else
tcg_regset_set32(ct->u.regs, 0, 0xff);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_L0);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_L1);
#endif
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_L0);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_L1);
break;
case 'e':
@@ -236,11 +232,13 @@ static inline int tcg_target_const_match(tcg_target_long val,
# define P_REXW 0x800 /* Set REX.W = 1 */
# define P_REXB_R 0x1000 /* REG field as byte register */
# define P_REXB_RM 0x2000 /* R/M field as byte register */
+# define P_GS 0x4000 /* gs segment override */
#else
# define P_ADDR32 0
# define P_REXW 0
# define P_REXB_R 0
# define P_REXB_RM 0
+# define P_GS 0
#endif
#define OPC_ARITH_EvIz (0x81)
@@ -356,6 +354,9 @@ static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x)
{
int rex;
+ if (opc & P_GS) {
+ tcg_out8(s, 0x65);
+ }
if (opc & P_DATA16) {
/* We should never be asking for both 16 and 64-bit operation. */
assert((opc & P_REXW) == 0);
@@ -1016,12 +1017,12 @@ static const void *qemu_st_helpers[4] = {
LABEL_PTRS is filled with 1 (32-bit addresses) or 2 (64-bit addresses)
positions of the displacements of forward jumps to the TLB miss case.
- First argument register is loaded with the low part of the address.
+ Second argument register is loaded with the low part of the address.
In the TLB hit case, it has been adjusted as indicated by the TLB
and so is a host address. In the TLB miss case, it continues to
hold a guest address.
- Second argument register is clobbered. */
+ First argument register is clobbered. */
static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx,
int mem_index, int s_bits,
@@ -1039,25 +1040,25 @@ static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx,
rexw = P_REXW;
}
- tcg_out_mov(s, type, r1, addrlo);
tcg_out_mov(s, type, r0, addrlo);
+ tcg_out_mov(s, type, r1, addrlo);
- tcg_out_shifti(s, SHIFT_SHR + rexw, r1,
+ tcg_out_shifti(s, SHIFT_SHR + rexw, r0,
TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
- tgen_arithi(s, ARITH_AND + rexw, r0,
- TARGET_PAGE_MASK | ((1 << s_bits) - 1), 0);
tgen_arithi(s, ARITH_AND + rexw, r1,
+ TARGET_PAGE_MASK | ((1 << s_bits) - 1), 0);
+ tgen_arithi(s, ARITH_AND + rexw, r0,
(CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0);
- tcg_out_modrm_sib_offset(s, OPC_LEA + P_REXW, r1, TCG_AREG0, r1, 0,
+ tcg_out_modrm_sib_offset(s, OPC_LEA + P_REXW, r0, TCG_AREG0, r0, 0,
offsetof(CPUArchState, tlb_table[mem_index][0])
+ which);
- /* cmp 0(r1), r0 */
- tcg_out_modrm_offset(s, OPC_CMP_GvEv + rexw, r0, r1, 0);
+ /* cmp 0(r0), r1 */
+ tcg_out_modrm_offset(s, OPC_CMP_GvEv + rexw, r1, r0, 0);
- tcg_out_mov(s, type, r0, addrlo);
+ tcg_out_mov(s, type, r1, addrlo);
/* jne label1 */
tcg_out8(s, OPC_JCC_short + JCC_JNE);
@@ -1065,8 +1066,8 @@ static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx,
s->code_ptr++;
if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) {
- /* cmp 4(r1), addrhi */
- tcg_out_modrm_offset(s, OPC_CMP_GvEv, args[addrlo_idx+1], r1, 4);
+ /* cmp 4(r0), addrhi */
+ tcg_out_modrm_offset(s, OPC_CMP_GvEv, args[addrlo_idx+1], r0, 4);
/* jne label1 */
tcg_out8(s, OPC_JCC_short + JCC_JNE);
@@ -1076,14 +1077,31 @@ static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx,
/* TLB Hit. */
- /* add addend(r1), r0 */
- tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r0, r1,
+ /* add addend(r0), r1 */
+ tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r1, r0,
offsetof(CPUTLBEntry, addend) - which);
}
-#endif
+#elif defined(__x86_64__) && defined(__linux__)
+# include <asm/prctl.h>
+# include <sys/prctl.h>
+
+int arch_prctl(int code, unsigned long addr);
+
+static int guest_base_flags;
+static inline void setup_guest_base_seg(void)
+{
+ if (arch_prctl(ARCH_SET_GS, GUEST_BASE) == 0) {
+ guest_base_flags = P_GS;
+ }
+}
+#else
+# define guest_base_flags 0
+static inline void setup_guest_base_seg(void) { }
+#endif /* SOFTMMU */
static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
- int base, tcg_target_long ofs, int sizeop)
+ int base, tcg_target_long ofs, int seg,
+ int sizeop)
{
#ifdef TARGET_WORDS_BIGENDIAN
const int bswap = 1;
@@ -1092,28 +1110,29 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
#endif
switch (sizeop) {
case 0:
- tcg_out_modrm_offset(s, OPC_MOVZBL, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVZBL + seg, datalo, base, ofs);
break;
case 0 | 4:
- tcg_out_modrm_offset(s, OPC_MOVSBL + P_REXW, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVSBL + P_REXW + seg, datalo, base, ofs);
break;
case 1:
- tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVZWL + seg, datalo, base, ofs);
if (bswap) {
tcg_out_rolw_8(s, datalo);
}
break;
case 1 | 4:
if (bswap) {
- tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVZWL + seg, datalo, base, ofs);
tcg_out_rolw_8(s, datalo);
tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo);
} else {
- tcg_out_modrm_offset(s, OPC_MOVSWL + P_REXW, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVSWL + P_REXW + seg,
+ datalo, base, ofs);
}
break;
case 2:
- tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg, datalo, base, ofs);
if (bswap) {
tcg_out_bswap32(s, datalo);
}
@@ -1121,17 +1140,18 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
#if TCG_TARGET_REG_BITS == 64
case 2 | 4:
if (bswap) {
- tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg, datalo, base, ofs);
tcg_out_bswap32(s, datalo);
tcg_out_ext32s(s, datalo, datalo);
} else {
- tcg_out_modrm_offset(s, OPC_MOVSLQ, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVSLQ + seg, datalo, base, ofs);
}
break;
#endif
case 3:
if (TCG_TARGET_REG_BITS == 64) {
- tcg_out_ld(s, TCG_TYPE_I64, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_GvEv + P_REXW + seg,
+ datalo, base, ofs);
if (bswap) {
tcg_out_bswap64(s, datalo);
}
@@ -1142,11 +1162,15 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
datahi = t;
}
if (base != datalo) {
- tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs);
- tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4);
+ tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg,
+ datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg,
+ datahi, base, ofs + 4);
} else {
- tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4);
- tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg,
+ datahi, base, ofs + 4);
+ tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg,
+ datalo, base, ofs);
}
if (bswap) {
tcg_out_bswap32(s, datalo);
@@ -1169,9 +1193,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
int addrlo_idx;
#if defined(CONFIG_SOFTMMU)
int mem_index, s_bits;
-#if TCG_TARGET_REG_BITS == 64
- int arg_idx;
-#else
+#if TCG_TARGET_REG_BITS == 32
int stack_adjust;
#endif
uint8_t *label_ptr[3];
@@ -1192,7 +1214,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
label_ptr, offsetof(CPUTLBEntry, addr_read));
/* TLB Hit. */
- tcg_out_qemu_ld_direct(s, data_reg, data_reg2, TCG_REG_L0, 0, opc);
+ tcg_out_qemu_ld_direct(s, data_reg, data_reg2, TCG_REG_L1, 0, 0, opc);
/* jmp label2 */
tcg_out8(s, OPC_JMP_short);
@@ -1220,15 +1242,9 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
tcg_out_push(s, TCG_AREG0);
stack_adjust += 4;
#else
- /* The first argument is already loaded with addrlo. */
- arg_idx = 1;
- tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx],
- mem_index);
- /* XXX/FIXME: suboptimal */
- tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[3], TCG_REG_L2);
- tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[2], TCG_REG_L1);
- tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[1], TCG_REG_L0);
tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[0], TCG_AREG0);
+ /* The second argument is already loaded with addrlo. */
+ tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[2], mem_index);
#endif
tcg_out_calli(s, (tcg_target_long)qemu_ld_helpers[s_bits]);
@@ -1285,29 +1301,31 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
{
int32_t offset = GUEST_BASE;
int base = args[addrlo_idx];
-
- if (TCG_TARGET_REG_BITS == 64) {
- /* ??? We assume all operations have left us with register
- contents that are zero extended. So far this appears to
- be true. If we want to enforce this, we can either do
- an explicit zero-extension here, or (if GUEST_BASE == 0)
- use the ADDR32 prefix. For now, do nothing. */
-
- if (offset != GUEST_BASE) {
- tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L0, GUEST_BASE);
- tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L0, base);
- base = TCG_REG_L0;
- offset = 0;
- }
+ int seg = 0;
+
+ /* ??? We assume all operations have left us with register contents
+ that are zero extended. So far this appears to be true. If we
+ want to enforce this, we can either do an explicit zero-extension
+ here, or (if GUEST_BASE == 0, or a segment register is in use)
+ use the ADDR32 prefix. For now, do nothing. */
+ if (GUEST_BASE && guest_base_flags) {
+ seg = guest_base_flags;
+ offset = 0;
+ } else if (TCG_TARGET_REG_BITS == 64 && offset != GUEST_BASE) {
+ tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, GUEST_BASE);
+ tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L1, base);
+ base = TCG_REG_L1;
+ offset = 0;
}
- tcg_out_qemu_ld_direct(s, data_reg, data_reg2, base, offset, opc);
+ tcg_out_qemu_ld_direct(s, data_reg, data_reg2, base, offset, seg, opc);
}
#endif
}
static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
- int base, tcg_target_long ofs, int sizeop)
+ int base, tcg_target_long ofs, int seg,
+ int sizeop)
{
#ifdef TARGET_WORDS_BIGENDIAN
const int bswap = 1;
@@ -1317,12 +1335,13 @@ static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
/* ??? Ideally we wouldn't need a scratch register. For user-only,
we could perform the bswap twice to restore the original value
instead of moving to the scratch. But as it is, the L constraint
- means that TCG_REG_L1 is definitely free here. */
- const int scratch = TCG_REG_L1;
+ means that TCG_REG_L0 is definitely free here. */
+ const int scratch = TCG_REG_L0;
switch (sizeop) {
case 0:
- tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R + seg,
+ datalo, base, ofs);
break;
case 1:
if (bswap) {
@@ -1330,7 +1349,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
tcg_out_rolw_8(s, scratch);
datalo = scratch;
}
- tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_DATA16, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_DATA16 + seg,
+ datalo, base, ofs);
break;
case 2:
if (bswap) {
@@ -1338,7 +1358,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
tcg_out_bswap32(s, scratch);
datalo = scratch;
}
- tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, datalo, base, ofs);
break;
case 3:
if (TCG_TARGET_REG_BITS == 64) {
@@ -1347,17 +1367,18 @@ static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
tcg_out_bswap64(s, scratch);
datalo = scratch;
}
- tcg_out_st(s, TCG_TYPE_I64, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_REXW + seg,
+ datalo, base, ofs);
} else if (bswap) {
tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi);
tcg_out_bswap32(s, scratch);
- tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, scratch, base, ofs);
tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo);
tcg_out_bswap32(s, scratch);
- tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs + 4);
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, scratch, base, ofs+4);
} else {
- tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs);
- tcg_out_st(s, TCG_TYPE_I32, datahi, base, ofs + 4);
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, datalo, base, ofs);
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, datahi, base, ofs+4);
}
break;
default:
@@ -1391,7 +1412,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
label_ptr, offsetof(CPUTLBEntry, addr_write));
/* TLB Hit. */
- tcg_out_qemu_st_direct(s, data_reg, data_reg2, TCG_REG_L0, 0, opc);
+ tcg_out_qemu_st_direct(s, data_reg, data_reg2, TCG_REG_L1, 0, 0, opc);
/* jmp label2 */
tcg_out8(s, OPC_JMP_short);
@@ -1425,15 +1446,12 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
tcg_out_push(s, TCG_AREG0);
stack_adjust += 4;
#else
+ tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[0], TCG_AREG0);
+ /* The second argument is already loaded with addrlo. */
tcg_out_mov(s, (opc == 3 ? TCG_TYPE_I64 : TCG_TYPE_I32),
- TCG_REG_L1, data_reg);
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_L2, mem_index);
+ tcg_target_call_iarg_regs[2], data_reg);
+ tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[3], mem_index);
stack_adjust = 0;
- /* XXX/FIXME: suboptimal */
- tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[3], TCG_REG_L2);
- tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[2], TCG_REG_L1);
- tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[1], TCG_REG_L0);
- tcg_out_mov(s, TCG_TYPE_I64, tcg_target_call_iarg_regs[0], TCG_AREG0);
#endif
tcg_out_calli(s, (tcg_target_long)qemu_st_helpers[s_bits]);
@@ -1451,23 +1469,24 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
{
int32_t offset = GUEST_BASE;
int base = args[addrlo_idx];
-
- if (TCG_TARGET_REG_BITS == 64) {
- /* ??? We assume all operations have left us with register
- contents that are zero extended. So far this appears to
- be true. If we want to enforce this, we can either do
- an explicit zero-extension here, or (if GUEST_BASE == 0)
- use the ADDR32 prefix. For now, do nothing. */
-
- if (offset != GUEST_BASE) {
- tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L0, GUEST_BASE);
- tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L0, base);
- base = TCG_REG_L0;
- offset = 0;
- }
+ int seg = 0;
+
+ /* ??? We assume all operations have left us with register contents
+ that are zero extended. So far this appears to be true. If we
+ want to enforce this, we can either do an explicit zero-extension
+ here, or (if GUEST_BASE == 0, or a segment register is in use)
+ use the ADDR32 prefix. For now, do nothing. */
+ if (GUEST_BASE && guest_base_flags) {
+ seg = guest_base_flags;
+ offset = 0;
+ } else if (TCG_TARGET_REG_BITS == 64 && offset != GUEST_BASE) {
+ tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, GUEST_BASE);
+ tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L1, base);
+ base = TCG_REG_L1;
+ offset = 0;
}
- tcg_out_qemu_st_direct(s, data_reg, data_reg2, base, offset, opc);
+ tcg_out_qemu_st_direct(s, data_reg, data_reg2, base, offset, seg, opc);
}
#endif
}
@@ -2061,6 +2080,13 @@ static void tcg_target_qemu_prologue(TCGContext *s)
tcg_out_pop(s, tcg_target_callee_save_regs[i]);
}
tcg_out_opc(s, OPC_RET, 0, 0, 0);
+
+#if !defined(CONFIG_SOFTMMU)
+ /* Try to set up a segment register to point to GUEST_BASE. */
+ if (GUEST_BASE) {
+ setup_guest_base_seg();
+ }
+#endif
}
static void tcg_target_init(TCGContext *s)
diff --git a/tcg/optimize.c b/tcg/optimize.c
index a06c8eb43e..8e5d918030 100644
--- a/tcg/optimize.c
+++ b/tcg/optimize.c
@@ -915,7 +915,8 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr,
case INDEX_op_call:
nb_call_args = (args[0] >> 16) + (args[0] & 0xffff);
- if (!(args[nb_call_args + 1] & (TCG_CALL_CONST | TCG_CALL_PURE))) {
+ if (!(args[nb_call_args + 1] & (TCG_CALL_NO_READ_GLOBALS |
+ TCG_CALL_NO_WRITE_GLOBALS))) {
for (i = 0; i < nb_globals; i++) {
reset_temp(i);
}
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index 8100a5a2e0..8d1da2b670 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -401,10 +401,10 @@ static inline void tcg_gen_helperN(void *func, int flags, int sizemask,
}
/* Note: Both tcg_gen_helper32() and tcg_gen_helper64() are currently
- reserved for helpers in tcg-runtime.c. These helpers are all const
- and pure, hence the call to tcg_gen_callN() with TCG_CALL_CONST |
- TCG_CALL_PURE. This may need to be adjusted if these functions
- start to be used with other helpers. */
+ reserved for helpers in tcg-runtime.c. These helpers all do not read
+ globals and do not have side effects, hence the call to tcg_gen_callN()
+ with TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_SIDE_EFFECTS. This may need
+ to be adjusted if these functions start to be used with other helpers. */
static inline void tcg_gen_helper32(void *func, int sizemask, TCGv_i32 ret,
TCGv_i32 a, TCGv_i32 b)
{
@@ -413,8 +413,9 @@ static inline void tcg_gen_helper32(void *func, int sizemask, TCGv_i32 ret,
fn = tcg_const_ptr(func);
args[0] = GET_TCGV_I32(a);
args[1] = GET_TCGV_I32(b);
- tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask,
- GET_TCGV_I32(ret), 2, args);
+ tcg_gen_callN(&tcg_ctx, fn,
+ TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_SIDE_EFFECTS,
+ sizemask, GET_TCGV_I32(ret), 2, args);
tcg_temp_free_ptr(fn);
}
@@ -426,8 +427,9 @@ static inline void tcg_gen_helper64(void *func, int sizemask, TCGv_i64 ret,
fn = tcg_const_ptr(func);
args[0] = GET_TCGV_I64(a);
args[1] = GET_TCGV_I64(b);
- tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask,
- GET_TCGV_I64(ret), 2, args);
+ tcg_gen_callN(&tcg_ctx, fn,
+ TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_SIDE_EFFECTS,
+ sizemask, GET_TCGV_I64(ret), 2, args);
tcg_temp_free_ptr(fn);
}
diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h
index 04cb7ca038..9651063414 100644
--- a/tcg/tcg-opc.h
+++ b/tcg/tcg-opc.h
@@ -37,8 +37,8 @@ DEF(nopn, 0, 0, 1, 0) /* variable number of parameters */
DEF(discard, 1, 0, 0, 0)
DEF(set_label, 0, 0, 1, TCG_OPF_BB_END)
-DEF(call, 0, 1, 2, TCG_OPF_SIDE_EFFECTS) /* variable number of parameters */
-DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF(call, 0, 1, 2, TCG_OPF_CALL_CLOBBER) /* variable number of parameters */
+DEF(br, 0, 0, 1, TCG_OPF_BB_END)
#define IMPL(X) (X ? 0 : TCG_OPF_NOT_PRESENT)
#if TCG_TARGET_REG_BITS == 32
@@ -57,9 +57,9 @@ DEF(ld8s_i32, 1, 1, 1, 0)
DEF(ld16u_i32, 1, 1, 1, 0)
DEF(ld16s_i32, 1, 1, 1, 0)
DEF(ld_i32, 1, 1, 1, 0)
-DEF(st8_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
-DEF(st16_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
-DEF(st_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF(st8_i32, 0, 2, 1, 0)
+DEF(st16_i32, 0, 2, 1, 0)
+DEF(st_i32, 0, 2, 1, 0)
/* arith */
DEF(add_i32, 1, 2, 0, 0)
DEF(sub_i32, 1, 2, 0, 0)
@@ -81,12 +81,11 @@ DEF(rotl_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rot_i32))
DEF(rotr_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rot_i32))
DEF(deposit_i32, 1, 2, 2, IMPL(TCG_TARGET_HAS_deposit_i32))
-DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END)
DEF(add2_i32, 2, 4, 0, IMPL(TCG_TARGET_REG_BITS == 32))
DEF(sub2_i32, 2, 4, 0, IMPL(TCG_TARGET_REG_BITS == 32))
-DEF(brcond2_i32, 0, 4, 2,
- TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS | IMPL(TCG_TARGET_REG_BITS == 32))
+DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | IMPL(TCG_TARGET_REG_BITS == 32))
DEF(mulu2_i32, 2, 2, 0, IMPL(TCG_TARGET_REG_BITS == 32))
DEF(setcond2_i32, 1, 4, 1, IMPL(TCG_TARGET_REG_BITS == 32))
@@ -116,10 +115,10 @@ DEF(ld16s_i64, 1, 1, 1, IMPL64)
DEF(ld32u_i64, 1, 1, 1, IMPL64)
DEF(ld32s_i64, 1, 1, 1, IMPL64)
DEF(ld_i64, 1, 1, 1, IMPL64)
-DEF(st8_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS | IMPL64)
-DEF(st16_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS | IMPL64)
-DEF(st32_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS | IMPL64)
-DEF(st_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS | IMPL64)
+DEF(st8_i64, 0, 2, 1, IMPL64)
+DEF(st16_i64, 0, 2, 1, IMPL64)
+DEF(st32_i64, 0, 2, 1, IMPL64)
+DEF(st_i64, 0, 2, 1, IMPL64)
/* arith */
DEF(add_i64, 1, 2, 0, IMPL64)
DEF(sub_i64, 1, 2, 0, IMPL64)
@@ -141,7 +140,7 @@ DEF(rotl_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rot_i64))
DEF(rotr_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rot_i64))
DEF(deposit_i64, 1, 2, 2, IMPL64 | IMPL(TCG_TARGET_HAS_deposit_i64))
-DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS | IMPL64)
+DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | IMPL64)
DEF(ext8s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext8s_i64))
DEF(ext16s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext16s_i64))
DEF(ext32s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext32s_i64))
@@ -165,8 +164,8 @@ DEF(debug_insn_start, 0, 0, 2, 0)
#else
DEF(debug_insn_start, 0, 0, 1, 0)
#endif
-DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
-DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END)
+DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END)
/* Note: even if TARGET_LONG_BITS is not defined, the INDEX_op
constants must be defined */
#if TCG_TARGET_REG_BITS == 32
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 5faaca5ebf..c3a7f19bd7 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -776,7 +776,11 @@ static void tcg_reg_alloc_start(TCGContext *s)
}
for(i = s->nb_globals; i < s->nb_temps; i++) {
ts = &s->temps[i];
- ts->val_type = TEMP_VAL_DEAD;
+ if (ts->temp_local) {
+ ts->val_type = TEMP_VAL_MEM;
+ } else {
+ ts->val_type = TEMP_VAL_DEAD;
+ }
ts->mem_allocated = 0;
ts->fixed_reg = 0;
}
@@ -1180,31 +1184,27 @@ static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr,
}
}
-/* liveness analysis: end of function: globals are live, temps are
- dead. */
-/* XXX: at this stage, not used as there would be little gains because
- most TBs end with a conditional jump. */
-static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps)
+/* liveness analysis: end of function: all temps are dead, and globals
+ should be in memory. */
+static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps,
+ uint8_t *mem_temps)
{
- memset(dead_temps, 0, s->nb_globals);
- memset(dead_temps + s->nb_globals, 1, s->nb_temps - s->nb_globals);
+ memset(dead_temps, 1, s->nb_temps);
+ memset(mem_temps, 1, s->nb_globals);
+ memset(mem_temps + s->nb_globals, 0, s->nb_temps - s->nb_globals);
}
-/* liveness analysis: end of basic block: globals are live, temps are
- dead, local temps are live. */
-static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps)
+/* liveness analysis: end of basic block: all temps are dead, globals
+ and local temps should be in memory. */
+static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps,
+ uint8_t *mem_temps)
{
int i;
- TCGTemp *ts;
- memset(dead_temps, 0, s->nb_globals);
- ts = &s->temps[s->nb_globals];
+ memset(dead_temps, 1, s->nb_temps);
+ memset(mem_temps, 1, s->nb_globals);
for(i = s->nb_globals; i < s->nb_temps; i++) {
- if (ts->temp_local)
- dead_temps[i] = 0;
- else
- dead_temps[i] = 1;
- ts++;
+ mem_temps[i] = s->temps[i].temp_local;
}
}
@@ -1217,17 +1217,20 @@ static void tcg_liveness_analysis(TCGContext *s)
TCGOpcode op;
TCGArg *args;
const TCGOpDef *def;
- uint8_t *dead_temps;
- unsigned int dead_args;
+ uint8_t *dead_temps, *mem_temps;
+ uint16_t dead_args;
+ uint8_t sync_args;
gen_opc_ptr++; /* skip end */
nb_ops = gen_opc_ptr - gen_opc_buf;
s->op_dead_args = tcg_malloc(nb_ops * sizeof(uint16_t));
+ s->op_sync_args = tcg_malloc(nb_ops * sizeof(uint8_t));
dead_temps = tcg_malloc(s->nb_temps);
- memset(dead_temps, 1, s->nb_temps);
+ mem_temps = tcg_malloc(s->nb_temps);
+ tcg_la_func_end(s, dead_temps, mem_temps);
args = gen_opparam_ptr;
op_index = nb_ops - 1;
@@ -1248,11 +1251,12 @@ static void tcg_liveness_analysis(TCGContext *s)
/* pure functions can be removed if their result is not
used */
- if (call_flags & TCG_CALL_PURE) {
+ if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) {
for(i = 0; i < nb_oargs; i++) {
arg = args[i];
- if (!dead_temps[arg])
+ if (!dead_temps[arg] || mem_temps[arg]) {
goto do_not_remove_call;
+ }
}
tcg_set_nop(s, gen_opc_buf + op_index,
args - 1, nb_args);
@@ -1261,17 +1265,27 @@ static void tcg_liveness_analysis(TCGContext *s)
/* output args are dead */
dead_args = 0;
+ sync_args = 0;
for(i = 0; i < nb_oargs; i++) {
arg = args[i];
if (dead_temps[arg]) {
dead_args |= (1 << i);
}
+ if (mem_temps[arg]) {
+ sync_args |= (1 << i);
+ }
dead_temps[arg] = 1;
+ mem_temps[arg] = 0;
+ }
+
+ if (!(call_flags & TCG_CALL_NO_READ_GLOBALS)) {
+ /* globals should be synced to memory */
+ memset(mem_temps, 1, s->nb_globals);
}
-
- if (!(call_flags & TCG_CALL_CONST)) {
- /* globals are live (they may be used by the call) */
- memset(dead_temps, 0, s->nb_globals);
+ if (!(call_flags & (TCG_CALL_NO_WRITE_GLOBALS |
+ TCG_CALL_NO_READ_GLOBALS))) {
+ /* globals should go back to memory */
+ memset(dead_temps, 1, s->nb_globals);
}
/* input args are live */
@@ -1285,6 +1299,7 @@ static void tcg_liveness_analysis(TCGContext *s)
}
}
s->op_dead_args[op_index] = dead_args;
+ s->op_sync_args[op_index] = sync_args;
}
args--;
}
@@ -1300,6 +1315,7 @@ static void tcg_liveness_analysis(TCGContext *s)
args--;
/* mark the temporary as dead */
dead_temps[args[0]] = 1;
+ mem_temps[args[0]] = 0;
break;
case INDEX_op_end:
break;
@@ -1365,8 +1381,9 @@ static void tcg_liveness_analysis(TCGContext *s)
if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) {
for(i = 0; i < nb_oargs; i++) {
arg = args[i];
- if (!dead_temps[arg])
+ if (!dead_temps[arg] || mem_temps[arg]) {
goto do_not_remove;
+ }
}
do_remove:
tcg_set_nop(s, gen_opc_buf + op_index, args, def->nb_args);
@@ -1378,20 +1395,25 @@ static void tcg_liveness_analysis(TCGContext *s)
/* output args are dead */
dead_args = 0;
+ sync_args = 0;
for(i = 0; i < nb_oargs; i++) {
arg = args[i];
if (dead_temps[arg]) {
dead_args |= (1 << i);
}
+ if (mem_temps[arg]) {
+ sync_args |= (1 << i);
+ }
dead_temps[arg] = 1;
+ mem_temps[arg] = 0;
}
/* if end of basic block, update */
if (def->flags & TCG_OPF_BB_END) {
- tcg_la_bb_end(s, dead_temps);
- } else if (def->flags & TCG_OPF_CALL_CLOBBER) {
- /* globals are live */
- memset(dead_temps, 0, s->nb_globals);
+ tcg_la_bb_end(s, dead_temps, mem_temps);
+ } else if (def->flags & TCG_OPF_SIDE_EFFECTS) {
+ /* globals should be synced to memory */
+ memset(mem_temps, 1, s->nb_globals);
}
/* input args are live */
@@ -1403,6 +1425,7 @@ static void tcg_liveness_analysis(TCGContext *s)
dead_temps[arg] = 0;
}
s->op_dead_args[op_index] = dead_args;
+ s->op_sync_args[op_index] = sync_args;
}
break;
}
@@ -1421,6 +1444,8 @@ static void tcg_liveness_analysis(TCGContext *s)
s->op_dead_args = tcg_malloc(nb_ops * sizeof(uint16_t));
memset(s->op_dead_args, 0, nb_ops * sizeof(uint16_t));
+ s->op_sync_args = tcg_malloc(nb_ops * sizeof(uint8_t));
+ memset(s->op_sync_args, 0, nb_ops * sizeof(uint8_t));
}
#endif
@@ -1517,22 +1542,33 @@ static void temp_allocate_frame(TCGContext *s, int temp)
s->current_frame_offset += (tcg_target_long)sizeof(tcg_target_long);
}
+/* sync register 'reg' by saving it to the corresponding temporary */
+static inline void tcg_reg_sync(TCGContext *s, int reg)
+{
+ TCGTemp *ts;
+ int temp;
+
+ temp = s->reg_to_temp[reg];
+ ts = &s->temps[temp];
+ assert(ts->val_type == TEMP_VAL_REG);
+ if (!ts->mem_coherent && !ts->fixed_reg) {
+ if (!ts->mem_allocated) {
+ temp_allocate_frame(s, temp);
+ }
+ tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+ }
+ ts->mem_coherent = 1;
+}
+
/* free register 'reg' by spilling the corresponding temporary if necessary */
static void tcg_reg_free(TCGContext *s, int reg)
{
- TCGTemp *ts;
int temp;
temp = s->reg_to_temp[reg];
if (temp != -1) {
- ts = &s->temps[temp];
- assert(ts->val_type == TEMP_VAL_REG);
- if (!ts->mem_coherent) {
- if (!ts->mem_allocated)
- temp_allocate_frame(s, temp);
- tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
- }
- ts->val_type = TEMP_VAL_MEM;
+ tcg_reg_sync(s, reg);
+ s->temps[temp].val_type = TEMP_VAL_MEM;
s->reg_to_temp[reg] = -1;
}
}
@@ -1564,31 +1600,45 @@ static int tcg_reg_alloc(TCGContext *s, TCGRegSet reg1, TCGRegSet reg2)
tcg_abort();
}
-/* save a temporary to memory. 'allocated_regs' is used in case a
+/* mark a temporary as dead. */
+static inline void temp_dead(TCGContext *s, int temp)
+{
+ TCGTemp *ts;
+
+ ts = &s->temps[temp];
+ if (!ts->fixed_reg) {
+ if (ts->val_type == TEMP_VAL_REG) {
+ s->reg_to_temp[ts->reg] = -1;
+ }
+ if (temp < s->nb_globals || (ts->temp_local && ts->mem_allocated)) {
+ ts->val_type = TEMP_VAL_MEM;
+ } else {
+ ts->val_type = TEMP_VAL_DEAD;
+ }
+ }
+}
+
+/* sync a temporary to memory. 'allocated_regs' is used in case a
temporary registers needs to be allocated to store a constant. */
-static void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs)
+static inline void temp_sync(TCGContext *s, int temp, TCGRegSet allocated_regs)
{
TCGTemp *ts;
- int reg;
ts = &s->temps[temp];
if (!ts->fixed_reg) {
switch(ts->val_type) {
+ case TEMP_VAL_CONST:
+ ts->reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type],
+ allocated_regs);
+ ts->val_type = TEMP_VAL_REG;
+ s->reg_to_temp[ts->reg] = temp;
+ ts->mem_coherent = 0;
+ tcg_out_movi(s, ts->type, ts->reg, ts->val);
+ /* fallthrough*/
case TEMP_VAL_REG:
- tcg_reg_free(s, ts->reg);
+ tcg_reg_sync(s, ts->reg);
break;
case TEMP_VAL_DEAD:
- ts->val_type = TEMP_VAL_MEM;
- break;
- case TEMP_VAL_CONST:
- reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type],
- allocated_regs);
- if (!ts->mem_allocated)
- temp_allocate_frame(s, temp);
- tcg_out_movi(s, ts->type, reg, ts->val);
- tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
- ts->val_type = TEMP_VAL_MEM;
- break;
case TEMP_VAL_MEM:
break;
default:
@@ -1597,6 +1647,20 @@ static void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs)
}
}
+/* save a temporary to memory. 'allocated_regs' is used in case a
+ temporary registers needs to be allocated to store a constant. */
+static inline void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs)
+{
+#ifdef USE_LIVENESS_ANALYSIS
+ /* The liveness analysis already ensures that globals are back
+ in memory. Keep an assert for safety. */
+ assert(s->temps[temp].val_type == TEMP_VAL_MEM || s->temps[temp].fixed_reg);
+#else
+ temp_sync(s, temp, allocated_regs);
+ temp_dead(s, temp);
+#endif
+}
+
/* save globals to their canonical location and assume they can be
modified be the following code. 'allocated_regs' is used in case a
temporary registers needs to be allocated to store a constant. */
@@ -1609,6 +1673,23 @@ static void save_globals(TCGContext *s, TCGRegSet allocated_regs)
}
}
+/* sync globals to their canonical location and assume they can be
+ read by the following code. 'allocated_regs' is used in case a
+ temporary registers needs to be allocated to store a constant. */
+static void sync_globals(TCGContext *s, TCGRegSet allocated_regs)
+{
+ int i;
+
+ for (i = 0; i < s->nb_globals; i++) {
+#ifdef USE_LIVENESS_ANALYSIS
+ assert(s->temps[i].val_type != TEMP_VAL_REG || s->temps[i].fixed_reg ||
+ s->temps[i].mem_coherent);
+#else
+ temp_sync(s, i, allocated_regs);
+#endif
+ }
+}
+
/* at the end of a basic block, we assume all temporaries are dead and
all globals are stored at their canonical location. */
static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs)
@@ -1621,10 +1702,13 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs)
if (ts->temp_local) {
temp_save(s, i, allocated_regs);
} else {
- if (ts->val_type == TEMP_VAL_REG) {
- s->reg_to_temp[ts->reg] = -1;
- }
- ts->val_type = TEMP_VAL_DEAD;
+#ifdef USE_LIVENESS_ANALYSIS
+ /* The liveness analysis already ensures that temps are dead.
+ Keep an assert for safety. */
+ assert(ts->val_type == TEMP_VAL_DEAD);
+#else
+ temp_dead(s, i);
+#endif
}
}
@@ -1632,8 +1716,10 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs)
}
#define IS_DEAD_ARG(n) ((dead_args >> (n)) & 1)
+#define NEED_SYNC_ARG(n) ((sync_args >> (n)) & 1)
-static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args)
+static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args,
+ uint16_t dead_args, uint8_t sync_args)
{
TCGTemp *ots;
tcg_target_ulong val;
@@ -1652,71 +1738,99 @@ static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args)
ots->val_type = TEMP_VAL_CONST;
ots->val = val;
}
+ if (NEED_SYNC_ARG(0)) {
+ temp_sync(s, args[0], s->reserved_regs);
+ }
+ if (IS_DEAD_ARG(0)) {
+ temp_dead(s, args[0]);
+ }
}
static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def,
- const TCGArg *args,
- unsigned int dead_args)
+ const TCGArg *args, uint16_t dead_args,
+ uint8_t sync_args)
{
+ TCGRegSet allocated_regs;
TCGTemp *ts, *ots;
- int reg;
- const TCGArgConstraint *arg_ct;
+ const TCGArgConstraint *arg_ct, *oarg_ct;
+ tcg_regset_set(allocated_regs, s->reserved_regs);
ots = &s->temps[args[0]];
ts = &s->temps[args[1]];
- arg_ct = &def->args_ct[0];
+ oarg_ct = &def->args_ct[0];
+ arg_ct = &def->args_ct[1];
+
+ /* If the source value is not in a register, and we're going to be
+ forced to have it in a register in order to perform the copy,
+ then copy the SOURCE value into its own register first. That way
+ we don't have to reload SOURCE the next time it is used. */
+ if (((NEED_SYNC_ARG(0) || ots->fixed_reg) && ts->val_type != TEMP_VAL_REG)
+ || ts->val_type == TEMP_VAL_MEM) {
+ ts->reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+ if (ts->val_type == TEMP_VAL_MEM) {
+ tcg_out_ld(s, ts->type, ts->reg, ts->mem_reg, ts->mem_offset);
+ ts->mem_coherent = 1;
+ } else if (ts->val_type == TEMP_VAL_CONST) {
+ tcg_out_movi(s, ts->type, ts->reg, ts->val);
+ }
+ s->reg_to_temp[ts->reg] = args[1];
+ ts->val_type = TEMP_VAL_REG;
+ }
- /* XXX: always mark arg dead if IS_DEAD_ARG(1) */
- if (ts->val_type == TEMP_VAL_REG) {
+ if (IS_DEAD_ARG(0) && !ots->fixed_reg) {
+ /* mov to a non-saved dead register makes no sense (even with
+ liveness analysis disabled). */
+ assert(NEED_SYNC_ARG(0));
+ /* The code above should have moved the temp to a register. */
+ assert(ts->val_type == TEMP_VAL_REG);
+ if (!ots->mem_allocated) {
+ temp_allocate_frame(s, args[0]);
+ }
+ tcg_out_st(s, ots->type, ts->reg, ots->mem_reg, ots->mem_offset);
+ if (IS_DEAD_ARG(1)) {
+ temp_dead(s, args[1]);
+ }
+ temp_dead(s, args[0]);
+ } else if (ts->val_type == TEMP_VAL_CONST) {
+ /* propagate constant */
+ if (ots->val_type == TEMP_VAL_REG) {
+ s->reg_to_temp[ots->reg] = -1;
+ }
+ ots->val_type = TEMP_VAL_CONST;
+ ots->val = ts->val;
+ } else {
+ /* The code in the first if block should have moved the
+ temp to a register. */
+ assert(ts->val_type == TEMP_VAL_REG);
if (IS_DEAD_ARG(1) && !ts->fixed_reg && !ots->fixed_reg) {
/* the mov can be suppressed */
- if (ots->val_type == TEMP_VAL_REG)
- s->reg_to_temp[ots->reg] = -1;
- reg = ts->reg;
- s->reg_to_temp[reg] = -1;
- ts->val_type = TEMP_VAL_DEAD;
- } else {
if (ots->val_type == TEMP_VAL_REG) {
- reg = ots->reg;
- } else {
- reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs);
- }
- if (ts->reg != reg) {
- tcg_out_mov(s, ots->type, reg, ts->reg);
+ s->reg_to_temp[ots->reg] = -1;
}
- }
- } else if (ts->val_type == TEMP_VAL_MEM) {
- if (ots->val_type == TEMP_VAL_REG) {
- reg = ots->reg;
+ ots->reg = ts->reg;
+ temp_dead(s, args[1]);
} else {
- reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs);
+ if (ots->val_type != TEMP_VAL_REG) {
+ /* When allocating a new register, make sure to not spill the
+ input one. */
+ tcg_regset_set_reg(allocated_regs, ts->reg);
+ ots->reg = tcg_reg_alloc(s, oarg_ct->u.regs, allocated_regs);
+ }
+ tcg_out_mov(s, ots->type, ots->reg, ts->reg);
}
- tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
- } else if (ts->val_type == TEMP_VAL_CONST) {
- if (ots->fixed_reg) {
- reg = ots->reg;
- tcg_out_movi(s, ots->type, reg, ts->val);
- } else {
- /* propagate constant */
- if (ots->val_type == TEMP_VAL_REG)
- s->reg_to_temp[ots->reg] = -1;
- ots->val_type = TEMP_VAL_CONST;
- ots->val = ts->val;
- return;
+ ots->val_type = TEMP_VAL_REG;
+ ots->mem_coherent = 0;
+ s->reg_to_temp[ots->reg] = args[0];
+ if (NEED_SYNC_ARG(0)) {
+ tcg_reg_sync(s, ots->reg);
}
- } else {
- tcg_abort();
}
- s->reg_to_temp[reg] = args[0];
- ots->reg = reg;
- ots->val_type = TEMP_VAL_REG;
- ots->mem_coherent = 0;
}
static void tcg_reg_alloc_op(TCGContext *s,
const TCGOpDef *def, TCGOpcode opc,
- const TCGArg *args,
- unsigned int dead_args)
+ const TCGArg *args, uint16_t dead_args,
+ uint8_t sync_args)
{
TCGRegSet allocated_regs;
int i, k, nb_iargs, nb_oargs, reg;
@@ -1796,22 +1910,16 @@ static void tcg_reg_alloc_op(TCGContext *s,
iarg_end: ;
}
+ /* mark dead temporaries and free the associated registers */
+ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
+ if (IS_DEAD_ARG(i)) {
+ temp_dead(s, args[i]);
+ }
+ }
+
if (def->flags & TCG_OPF_BB_END) {
tcg_reg_alloc_bb_end(s, allocated_regs);
} else {
- /* mark dead temporaries and free the associated registers */
- for(i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
- arg = args[i];
- if (IS_DEAD_ARG(i)) {
- ts = &s->temps[arg];
- if (!ts->fixed_reg) {
- if (ts->val_type == TEMP_VAL_REG)
- s->reg_to_temp[ts->reg] = -1;
- ts->val_type = TEMP_VAL_DEAD;
- }
- }
- }
-
if (def->flags & TCG_OPF_CALL_CLOBBER) {
/* XXX: permit generic clobber register list ? */
for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
@@ -1819,12 +1927,11 @@ static void tcg_reg_alloc_op(TCGContext *s,
tcg_reg_free(s, reg);
}
}
- /* XXX: for load/store we could do that only for the slow path
- (i.e. when a memory callback is called) */
-
- /* store globals and free associated registers (we assume the insn
- can modify any global. */
- save_globals(s, allocated_regs);
+ }
+ if (def->flags & TCG_OPF_SIDE_EFFECTS) {
+ /* sync globals if the op has side effects and might trigger
+ an exception. */
+ sync_globals(s, allocated_regs);
}
/* satisfy the output constraints */
@@ -1848,18 +1955,15 @@ static void tcg_reg_alloc_op(TCGContext *s,
tcg_regset_set_reg(allocated_regs, reg);
/* if a fixed register is used, then a move will be done afterwards */
if (!ts->fixed_reg) {
- if (ts->val_type == TEMP_VAL_REG)
+ if (ts->val_type == TEMP_VAL_REG) {
s->reg_to_temp[ts->reg] = -1;
- if (IS_DEAD_ARG(i)) {
- ts->val_type = TEMP_VAL_DEAD;
- } else {
- ts->val_type = TEMP_VAL_REG;
- ts->reg = reg;
- /* temp value is modified, so the value kept in memory is
- potentially not the same */
- ts->mem_coherent = 0;
- s->reg_to_temp[reg] = arg;
- }
+ }
+ ts->val_type = TEMP_VAL_REG;
+ ts->reg = reg;
+ /* temp value is modified, so the value kept in memory is
+ potentially not the same */
+ ts->mem_coherent = 0;
+ s->reg_to_temp[reg] = arg;
}
oarg_end:
new_args[i] = reg;
@@ -1876,6 +1980,12 @@ static void tcg_reg_alloc_op(TCGContext *s,
if (ts->fixed_reg && ts->reg != reg) {
tcg_out_mov(s, ts->type, ts->reg, reg);
}
+ if (NEED_SYNC_ARG(i)) {
+ tcg_reg_sync(s, reg);
+ }
+ if (IS_DEAD_ARG(i)) {
+ temp_dead(s, args[i]);
+ }
}
}
@@ -1887,7 +1997,7 @@ static void tcg_reg_alloc_op(TCGContext *s,
static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
TCGOpcode opc, const TCGArg *args,
- unsigned int dead_args)
+ uint16_t dead_args, uint8_t sync_args)
{
int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params;
TCGArg arg, func_arg;
@@ -2011,14 +2121,8 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
/* mark dead temporaries and free the associated registers */
for(i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
- arg = args[i];
if (IS_DEAD_ARG(i)) {
- ts = &s->temps[arg];
- if (!ts->fixed_reg) {
- if (ts->val_type == TEMP_VAL_REG)
- s->reg_to_temp[ts->reg] = -1;
- ts->val_type = TEMP_VAL_DEAD;
- }
+ temp_dead(s, args[i]);
}
}
@@ -2028,10 +2132,14 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
tcg_reg_free(s, reg);
}
}
-
- /* store globals and free associated registers (we assume the call
- can modify any global. */
- if (!(flags & TCG_CALL_CONST)) {
+
+ /* Save globals if they might be written by the helper, sync them if
+ they might be read. */
+ if (flags & TCG_CALL_NO_READ_GLOBALS) {
+ /* Nothing to do */
+ } else if (flags & TCG_CALL_NO_WRITE_GLOBALS) {
+ sync_globals(s, allocated_regs);
+ } else {
save_globals(s, allocated_regs);
}
@@ -2048,15 +2156,18 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
tcg_out_mov(s, ts->type, ts->reg, reg);
}
} else {
- if (ts->val_type == TEMP_VAL_REG)
+ if (ts->val_type == TEMP_VAL_REG) {
s->reg_to_temp[ts->reg] = -1;
+ }
+ ts->val_type = TEMP_VAL_REG;
+ ts->reg = reg;
+ ts->mem_coherent = 0;
+ s->reg_to_temp[reg] = arg;
+ if (NEED_SYNC_ARG(i)) {
+ tcg_reg_sync(s, reg);
+ }
if (IS_DEAD_ARG(i)) {
- ts->val_type = TEMP_VAL_DEAD;
- } else {
- ts->val_type = TEMP_VAL_REG;
- ts->reg = reg;
- ts->mem_coherent = 0;
- s->reg_to_temp[reg] = arg;
+ temp_dead(s, args[i]);
}
}
}
@@ -2087,7 +2198,6 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
TCGOpcode opc;
int op_index;
const TCGOpDef *def;
- unsigned int dead_args;
const TCGArg *args;
#ifdef DEBUG_DISAS
@@ -2148,12 +2258,13 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
switch(opc) {
case INDEX_op_mov_i32:
case INDEX_op_mov_i64:
- dead_args = s->op_dead_args[op_index];
- tcg_reg_alloc_mov(s, def, args, dead_args);
+ tcg_reg_alloc_mov(s, def, args, s->op_dead_args[op_index],
+ s->op_sync_args[op_index]);
break;
case INDEX_op_movi_i32:
case INDEX_op_movi_i64:
- tcg_reg_alloc_movi(s, args);
+ tcg_reg_alloc_movi(s, args, s->op_dead_args[op_index],
+ s->op_sync_args[op_index]);
break;
case INDEX_op_debug_insn_start:
/* debug instruction */
@@ -2167,24 +2278,16 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
args += args[0];
goto next;
case INDEX_op_discard:
- {
- TCGTemp *ts;
- ts = &s->temps[args[0]];
- /* mark the temporary as dead */
- if (!ts->fixed_reg) {
- if (ts->val_type == TEMP_VAL_REG)
- s->reg_to_temp[ts->reg] = -1;
- ts->val_type = TEMP_VAL_DEAD;
- }
- }
+ temp_dead(s, args[0]);
break;
case INDEX_op_set_label:
tcg_reg_alloc_bb_end(s, s->reserved_regs);
tcg_out_label(s, args[0], s->code_ptr);
break;
case INDEX_op_call:
- dead_args = s->op_dead_args[op_index];
- args += tcg_reg_alloc_call(s, def, opc, args, dead_args);
+ args += tcg_reg_alloc_call(s, def, opc, args,
+ s->op_dead_args[op_index],
+ s->op_sync_args[op_index]);
goto next;
case INDEX_op_end:
goto the_end;
@@ -2196,8 +2299,8 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
/* Note: in order to speed up the code, it would be much
faster to have specialized register allocator functions for
some common argument patterns */
- dead_args = s->op_dead_args[op_index];
- tcg_reg_alloc_op(s, def, opc, args, dead_args);
+ tcg_reg_alloc_op(s, def, opc, args, s->op_dead_args[op_index],
+ s->op_sync_args[op_index]);
break;
}
args += def->nb_args;
diff --git a/tcg/tcg.h b/tcg/tcg.h
index 45e94f536c..a6c9256f8b 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -253,14 +253,20 @@ typedef int TCGv_i64;
#define TCGV_UNUSED_I64(x) x = MAKE_TCGV_I64(-1)
/* call flags */
-/* A pure function only reads its arguments and TCG global variables
- and cannot raise exceptions. Hence a call to a pure function can be
- safely suppressed if the return value is not used. */
-#define TCG_CALL_PURE 0x0010
-/* A const function only reads its arguments and does not use TCG
- global variables. Hence a call to such a function does not
- save TCG global variables back to their canonical location. */
-#define TCG_CALL_CONST 0x0020
+/* Helper does not read globals (either directly or through an exception). It
+ implies TCG_CALL_NO_WRITE_GLOBALS. */
+#define TCG_CALL_NO_READ_GLOBALS 0x0010
+/* Helper does not write globals */
+#define TCG_CALL_NO_WRITE_GLOBALS 0x0020
+/* Helper can be safely suppressed if the return value is not used. */
+#define TCG_CALL_NO_SIDE_EFFECTS 0x0040
+
+/* convenience version of most used call flags */
+#define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS
+#define TCG_CALL_NO_WG TCG_CALL_NO_WRITE_GLOBALS
+#define TCG_CALL_NO_SE TCG_CALL_NO_SIDE_EFFECTS
+#define TCG_CALL_NO_RWG_SE (TCG_CALL_NO_RWG | TCG_CALL_NO_SE)
+#define TCG_CALL_NO_WG_SE (TCG_CALL_NO_WG | TCG_CALL_NO_SE)
/* used to align parameters */
#define TCG_CALL_DUMMY_TCGV MAKE_TCGV_I32(-1)
@@ -381,6 +387,9 @@ struct TCGContext {
/* liveness analysis */
uint16_t *op_dead_args; /* for each operation, each bit tells if the
corresponding argument is dead */
+ uint8_t *op_sync_args; /* for each operation, each bit tells if the
+ corresponding output argument needs to be
+ sync to memory. */
/* tells in which temporary a given register is. It does not take
into account fixed registers */
@@ -527,8 +536,8 @@ enum {
TCG_OPF_BB_END = 0x01,
/* Instruction clobbers call registers and potentially update globals. */
TCG_OPF_CALL_CLOBBER = 0x02,
- /* Instruction has side effects: it cannot be removed
- if its outputs are not used. */
+ /* Instruction has side effects: it cannot be removed if its outputs
+ are not used, and might trigger exceptions. */
TCG_OPF_SIDE_EFFECTS = 0x04,
/* Instruction operands are 64-bits (otherwise 32-bits). */
TCG_OPF_64BIT = 0x08,
diff --git a/tests/Makefile b/tests/Makefile
index 26a67ce6f5..86c9b79ebe 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -42,11 +42,11 @@ test-qapi-obj-y += module.o
$(test-obj-y): QEMU_INCLUDES += -Itests
-tests/check-qint$(EXESUF): tests/check-qint.o qint.o $(tools-obj-y)
-tests/check-qstring$(EXESUF): tests/check-qstring.o qstring.o $(tools-obj-y)
-tests/check-qdict$(EXESUF): tests/check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
-tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o $(tools-obj-y)
-tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o $(tools-obj-y)
+tests/check-qint$(EXESUF): tests/check-qint.o qint.o
+tests/check-qstring$(EXESUF): tests/check-qstring.o qstring.o
+tests/check-qdict$(EXESUF): tests/check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o
+tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o
+tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o
tests/check-qjson$(EXESUF): tests/check-qjson.o $(qobject-obj-y) $(tools-obj-y)
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(coroutine-obj-y) $(tools-obj-y)
tests/test-iov$(EXESUF): tests/test-iov.o iov.o
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index 258e7eae38..aad535a74b 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -26,6 +26,7 @@ import os
import iotests
from iotests import qemu_img, qemu_io
import struct
+import errno
backing_img = os.path.join(iotests.test_dir, 'backing.img')
mid_img = os.path.join(iotests.test_dir, 'mid.img')
@@ -111,7 +112,7 @@ class TestSingleDrive(ImageCommitTestCase):
self.assert_no_active_commit()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img)
self.assert_qmp(result, 'error/class', 'GenericError')
- self.assert_qmp(result, 'error/desc', 'Invalid files for merge: top and base are the same')
+ self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % backing_img)
def test_top_invalid(self):
self.assert_no_active_commit()
@@ -135,7 +136,7 @@ class TestSingleDrive(ImageCommitTestCase):
self.assert_no_active_commit()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img)
self.assert_qmp(result, 'error/class', 'GenericError')
- self.assert_qmp(result, 'error/desc', 'Base (%(1)s) is not reachable from top (%(2)s)' % {"1" : mid_img, "2" : backing_img})
+ self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img)
def test_top_omitted(self):
self.assert_no_active_commit()
@@ -143,6 +144,107 @@ class TestSingleDrive(ImageCommitTestCase):
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', "Parameter 'top' is missing")
+class TestRelativePaths(ImageCommitTestCase):
+ image_len = 1 * 1024 * 1024
+ test_len = 1 * 1024 * 256
+
+ dir1 = "dir1"
+ dir2 = "dir2/"
+ dir3 = "dir2/dir3/"
+
+ test_img = os.path.join(iotests.test_dir, dir3, 'test.img')
+ mid_img = "../mid.img"
+ backing_img = "../dir1/backing.img"
+
+ backing_img_abs = os.path.join(iotests.test_dir, dir1, 'backing.img')
+ mid_img_abs = os.path.join(iotests.test_dir, dir2, 'mid.img')
+
+ def setUp(self):
+ try:
+ os.mkdir(os.path.join(iotests.test_dir, self.dir1))
+ os.mkdir(os.path.join(iotests.test_dir, self.dir2))
+ os.mkdir(os.path.join(iotests.test_dir, self.dir3))
+ except OSError as exception:
+ if exception.errno != errno.EEXIST:
+ raise
+ self.create_image(self.backing_img_abs, TestRelativePaths.image_len)
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.backing_img_abs, self.mid_img_abs)
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.mid_img_abs, self.test_img)
+ qemu_img('rebase', '-u', '-b', self.backing_img, self.mid_img_abs)
+ qemu_img('rebase', '-u', '-b', self.mid_img, self.test_img)
+ qemu_io('-c', 'write -P 0xab 0 524288', self.backing_img_abs)
+ qemu_io('-c', 'write -P 0xef 524288 524288', self.mid_img_abs)
+ self.vm = iotests.VM().add_drive(self.test_img)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(self.test_img)
+ os.remove(self.mid_img_abs)
+ os.remove(self.backing_img_abs)
+ try:
+ os.rmdir(os.path.join(iotests.test_dir, self.dir1))
+ os.rmdir(os.path.join(iotests.test_dir, self.dir3))
+ os.rmdir(os.path.join(iotests.test_dir, self.dir2))
+ except OSError as exception:
+ if exception.errno != errno.EEXIST and exception.errno != errno.ENOTEMPTY:
+ raise
+
+ def test_commit(self):
+ self.assert_no_active_commit()
+ result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img)
+ self.assert_qmp(result, 'return', {})
+
+ completed = False
+ while not completed:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_COMPLETED':
+ self.assert_qmp(event, 'data/type', 'commit')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/offset', self.image_len)
+ self.assert_qmp(event, 'data/len', self.image_len)
+ completed = True
+
+ self.assert_no_active_commit()
+ self.vm.shutdown()
+
+ self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
+ self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
+
+ def test_device_not_found(self):
+ result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img)
+ self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+
+ def test_top_same_base(self):
+ self.assert_no_active_commit()
+ result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='%s' % self.mid_img)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % self.mid_img)
+
+ def test_top_invalid(self):
+ self.assert_no_active_commit()
+ result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % self.backing_img)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', 'Top image file badfile not found')
+
+ def test_base_invalid(self):
+ self.assert_no_active_commit()
+ result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='badfile')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
+
+ def test_top_is_active(self):
+ self.assert_no_active_commit()
+ result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.test_img, base='%s' % self.backing_img)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', 'Top image as the active layer is currently unsupported')
+
+ def test_top_and_base_reversed(self):
+ self.assert_no_active_commit()
+ result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.backing_img, base='%s' % self.mid_img)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % self.mid_img)
+
class TestSetSpeed(ImageCommitTestCase):
image_len = 80 * 1024 * 1024 # MB
diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out
index dae404e278..b6f257674e 100644
--- a/tests/qemu-iotests/040.out
+++ b/tests/qemu-iotests/040.out
@@ -1,5 +1,5 @@
-.........
+................
----------------------------------------------------------------------
-Ran 9 tests
+Ran 16 tests
OK
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
new file mode 100755
index 0000000000..c6eb851871
--- /dev/null
+++ b/tests/qemu-iotests/041
@@ -0,0 +1,615 @@
+#!/usr/bin/env python
+#
+# Tests for image mirroring.
+#
+# Copyright (C) 2012 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import time
+import os
+import iotests
+from iotests import qemu_img, qemu_io
+import struct
+
+backing_img = os.path.join(iotests.test_dir, 'backing.img')
+target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img')
+test_img = os.path.join(iotests.test_dir, 'test.img')
+target_img = os.path.join(iotests.test_dir, 'target.img')
+
+class ImageMirroringTestCase(iotests.QMPTestCase):
+ '''Abstract base class for image mirroring test cases'''
+
+ def assert_no_active_mirrors(self):
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return', [])
+
+ def cancel_and_wait(self, drive='drive0', wait_ready=True):
+ '''Cancel a block job and wait for it to finish'''
+ if wait_ready:
+ ready = False
+ while not ready:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_READY':
+ self.assert_qmp(event, 'data/type', 'mirror')
+ self.assert_qmp(event, 'data/device', drive)
+ ready = True
+
+ result = self.vm.qmp('block-job-cancel', device=drive,
+ force=not wait_ready)
+ self.assert_qmp(result, 'return', {})
+
+ cancelled = False
+ while not cancelled:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_COMPLETED' or \
+ event['event'] == 'BLOCK_JOB_CANCELLED':
+ self.assert_qmp(event, 'data/type', 'mirror')
+ self.assert_qmp(event, 'data/device', drive)
+ if wait_ready:
+ self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
+ self.assert_qmp(event, 'data/offset', self.image_len)
+ self.assert_qmp(event, 'data/len', self.image_len)
+ cancelled = True
+
+ self.assert_no_active_mirrors()
+
+ def complete_and_wait(self, drive='drive0', wait_ready=True):
+ '''Complete a block job and wait for it to finish'''
+ if wait_ready:
+ ready = False
+ while not ready:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_READY':
+ self.assert_qmp(event, 'data/type', 'mirror')
+ self.assert_qmp(event, 'data/device', drive)
+ ready = True
+
+ result = self.vm.qmp('block-job-complete', device=drive)
+ self.assert_qmp(result, 'return', {})
+
+ completed = False
+ while not completed:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_COMPLETED':
+ self.assert_qmp(event, 'data/type', 'mirror')
+ self.assert_qmp(event, 'data/device', drive)
+ self.assert_qmp_absent(event, 'data/error')
+ self.assert_qmp(event, 'data/offset', self.image_len)
+ self.assert_qmp(event, 'data/len', self.image_len)
+ completed = True
+
+ self.assert_no_active_mirrors()
+
+ def create_image(self, name, size):
+ file = open(name, 'w')
+ i = 0
+ while i < size:
+ sector = struct.pack('>l504xl', i / 512, i / 512)
+ file.write(sector)
+ i = i + 512
+ file.close()
+
+ def compare_images(self, img1, img2):
+ try:
+ qemu_img('convert', '-f', iotests.imgfmt, '-O', 'raw', img1, img1 + '.raw')
+ qemu_img('convert', '-f', iotests.imgfmt, '-O', 'raw', img2, img2 + '.raw')
+ file1 = open(img1 + '.raw', 'r')
+ file2 = open(img2 + '.raw', 'r')
+ return file1.read() == file2.read()
+ finally:
+ if file1 is not None:
+ file1.close()
+ if file2 is not None:
+ file2.close()
+ try:
+ os.remove(img1 + '.raw')
+ except OSError:
+ pass
+ try:
+ os.remove(img2 + '.raw')
+ except OSError:
+ pass
+
+class TestSingleDrive(ImageMirroringTestCase):
+ image_len = 1 * 1024 * 1024 # MB
+
+ def setUp(self):
+ self.create_image(backing_img, TestSingleDrive.image_len)
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ self.vm = iotests.VM().add_drive(test_img)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(test_img)
+ os.remove(backing_img)
+ try:
+ os.remove(target_img)
+ except OSError:
+ pass
+
+ def test_complete(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ self.complete_and_wait()
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/inserted/file', target_img)
+ self.vm.shutdown()
+ self.assertTrue(self.compare_images(test_img, target_img),
+ 'target image does not match source after mirroring')
+
+ def test_cancel(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ self.cancel_and_wait(wait_ready=False)
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/inserted/file', test_img)
+ self.vm.shutdown()
+
+ def test_cancel_after_ready(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ self.cancel_and_wait()
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/inserted/file', test_img)
+ self.vm.shutdown()
+ self.assertTrue(self.compare_images(test_img, target_img),
+ 'target image does not match source after mirroring')
+
+ def test_pause(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('block-job-pause', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ time.sleep(1)
+ result = self.vm.qmp('query-block-jobs')
+ offset = self.dictpath(result, 'return[0]/offset')
+
+ time.sleep(1)
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/offset', offset)
+
+ result = self.vm.qmp('block-job-resume', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.complete_and_wait()
+ self.vm.shutdown()
+ self.assertTrue(self.compare_images(test_img, target_img),
+ 'target image does not match source after mirroring')
+
+ def test_large_cluster(self):
+ self.assert_no_active_mirrors()
+
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
+ % (TestSingleDrive.image_len, backing_img), target_img)
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ mode='existing', target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ self.complete_and_wait()
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/inserted/file', target_img)
+ self.vm.shutdown()
+ self.assertTrue(self.compare_images(test_img, target_img),
+ 'target image does not match source after mirroring')
+
+ def test_medium_not_found(self):
+ result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+ def test_image_not_found(self):
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ mode='existing', target=target_img)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+ def test_device_not_found(self):
+ result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+
+class TestMirrorNoBacking(ImageMirroringTestCase):
+ image_len = 2 * 1024 * 1024 # MB
+
+ def complete_and_wait(self, drive='drive0', wait_ready=True):
+ self.create_image(target_backing_img, TestMirrorNoBacking.image_len)
+ return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready)
+
+ def compare_images(self, img1, img2):
+ self.create_image(target_backing_img, TestMirrorNoBacking.image_len)
+ return ImageMirroringTestCase.compare_images(self, img1, img2)
+
+ def setUp(self):
+ self.create_image(backing_img, TestMirrorNoBacking.image_len)
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ self.vm = iotests.VM().add_drive(test_img)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(test_img)
+ os.remove(backing_img)
+ os.remove(target_backing_img)
+ os.remove(target_img)
+
+ def test_complete(self):
+ self.assert_no_active_mirrors()
+
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ mode='existing', target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ self.complete_and_wait()
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/inserted/file', target_img)
+ self.vm.shutdown()
+ self.assertTrue(self.compare_images(test_img, target_img),
+ 'target image does not match source after mirroring')
+
+ def test_cancel(self):
+ self.assert_no_active_mirrors()
+
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ mode='existing', target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ self.cancel_and_wait()
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/inserted/file', test_img)
+ self.vm.shutdown()
+ self.assertTrue(self.compare_images(test_img, target_img),
+ 'target image does not match source after mirroring')
+
+class TestReadErrors(ImageMirroringTestCase):
+ image_len = 2 * 1024 * 1024 # MB
+
+ # this should be a multiple of twice the default granularity
+ # so that we hit this offset first in state 1
+ MIRROR_GRANULARITY = 1024 * 1024
+
+ def create_blkdebug_file(self, name, event, errno):
+ file = open(name, 'w')
+ file.write('''
+[inject-error]
+state = "1"
+event = "%s"
+errno = "%d"
+immediately = "off"
+once = "on"
+sector = "%d"
+
+[set-state]
+state = "1"
+event = "%s"
+new_state = "2"
+
+[set-state]
+state = "2"
+event = "%s"
+new_state = "1"
+''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
+ file.close()
+
+ def setUp(self):
+ self.blkdebug_file = backing_img + ".blkdebug"
+ self.create_image(backing_img, TestReadErrors.image_len)
+ self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
+ % (self.blkdebug_file, backing_img),
+ test_img)
+ self.vm = iotests.VM().add_drive(test_img)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(test_img)
+ os.remove(backing_img)
+ os.remove(self.blkdebug_file)
+
+ def test_report_read(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ completed = False
+ error = False
+ while not completed:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_ERROR':
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/operation', 'read')
+ error = True
+ elif event['event'] == 'BLOCK_JOB_READY':
+ self.assertTrue(False, 'job completed unexpectedly')
+ elif event['event'] == 'BLOCK_JOB_COMPLETED':
+ self.assertTrue(error, 'job completed unexpectedly')
+ self.assert_qmp(event, 'data/type', 'mirror')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/error', 'Input/output error')
+ self.assert_qmp(event, 'data/len', self.image_len)
+ completed = True
+
+ self.assert_no_active_mirrors()
+ self.vm.shutdown()
+
+ def test_ignore_read(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img, on_source_error='ignore')
+ self.assert_qmp(result, 'return', {})
+
+ event = self.vm.get_qmp_event(wait=True)
+ self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/operation', 'read')
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/paused', False)
+ self.complete_and_wait()
+ self.vm.shutdown()
+
+ def test_stop_read(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img, on_source_error='stop')
+ self.assert_qmp(result, 'return', {})
+
+ error = False
+ ready = False
+ while not ready:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_ERROR':
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/operation', 'read')
+
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/paused', True)
+ self.assert_qmp(result, 'return[0]/io-status', 'failed')
+
+ result = self.vm.qmp('block-job-resume', device='drive0')
+ self.assert_qmp(result, 'return', {})
+ error = True
+ elif event['event'] == 'BLOCK_JOB_READY':
+ self.assertTrue(error, 'job completed unexpectedly')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ ready = True
+
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/paused', False)
+ self.assert_qmp(result, 'return[0]/io-status', 'ok')
+
+ self.complete_and_wait(wait_ready=False)
+ self.assert_no_active_mirrors()
+ self.vm.shutdown()
+
+class TestWriteErrors(ImageMirroringTestCase):
+ image_len = 2 * 1024 * 1024 # MB
+
+ # this should be a multiple of twice the default granularity
+ # so that we hit this offset first in state 1
+ MIRROR_GRANULARITY = 1024 * 1024
+
+ def create_blkdebug_file(self, name, event, errno):
+ file = open(name, 'w')
+ file.write('''
+[inject-error]
+state = "1"
+event = "%s"
+errno = "%d"
+immediately = "off"
+once = "on"
+sector = "%d"
+
+[set-state]
+state = "1"
+event = "%s"
+new_state = "2"
+
+[set-state]
+state = "2"
+event = "%s"
+new_state = "1"
+''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
+ file.close()
+
+ def setUp(self):
+ self.blkdebug_file = target_img + ".blkdebug"
+ self.create_image(backing_img, TestWriteErrors.image_len)
+ self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
+ qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
+ self.vm = iotests.VM().add_drive(test_img)
+ self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
+ qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(test_img)
+ os.remove(backing_img)
+ os.remove(self.blkdebug_file)
+
+ def test_report_write(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ mode='existing', target=self.target_img)
+ self.assert_qmp(result, 'return', {})
+
+ completed = False
+ error = False
+ while not completed:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_ERROR':
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/operation', 'write')
+ error = True
+ elif event['event'] == 'BLOCK_JOB_READY':
+ self.assertTrue(False, 'job completed unexpectedly')
+ elif event['event'] == 'BLOCK_JOB_COMPLETED':
+ self.assertTrue(error, 'job completed unexpectedly')
+ self.assert_qmp(event, 'data/type', 'mirror')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/error', 'Input/output error')
+ self.assert_qmp(event, 'data/len', self.image_len)
+ completed = True
+
+ self.assert_no_active_mirrors()
+ self.vm.shutdown()
+
+ def test_ignore_write(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ mode='existing', target=self.target_img,
+ on_target_error='ignore')
+ self.assert_qmp(result, 'return', {})
+
+ event = self.vm.get_qmp_event(wait=True)
+ self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/operation', 'write')
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/paused', False)
+ self.complete_and_wait()
+ self.vm.shutdown()
+
+ def test_stop_write(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ mode='existing', target=self.target_img,
+ on_target_error='stop')
+ self.assert_qmp(result, 'return', {})
+
+ error = False
+ ready = False
+ while not ready:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_ERROR':
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/operation', 'write')
+
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/paused', True)
+ self.assert_qmp(result, 'return[0]/io-status', 'failed')
+
+ result = self.vm.qmp('block-job-resume', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/paused', False)
+ self.assert_qmp(result, 'return[0]/io-status', 'ok')
+ error = True
+ elif event['event'] == 'BLOCK_JOB_READY':
+ self.assertTrue(error, 'job completed unexpectedly')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ ready = True
+
+ self.complete_and_wait(wait_ready=False)
+ self.assert_no_active_mirrors()
+ self.vm.shutdown()
+
+class TestSetSpeed(ImageMirroringTestCase):
+ image_len = 80 * 1024 * 1024 # MB
+
+ def setUp(self):
+ qemu_img('create', backing_img, str(TestSetSpeed.image_len))
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ self.vm = iotests.VM().add_drive(test_img)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(test_img)
+ os.remove(backing_img)
+ os.remove(target_img)
+
+ def test_set_speed(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ # Default speed is 0
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/device', 'drive0')
+ self.assert_qmp(result, 'return[0]/speed', 0)
+
+ result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
+ self.assert_qmp(result, 'return', {})
+
+ # Ensure the speed we set was accepted
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/device', 'drive0')
+ self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
+
+ self.cancel_and_wait()
+
+ # Check setting speed in drive-mirror works
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img, speed=4*1024*1024)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/device', 'drive0')
+ self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
+
+ self.cancel_and_wait()
+
+ def test_set_speed_invalid(self):
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img, speed=-1)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+ self.assert_no_active_mirrors()
+
+ result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+ target=target_img)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+ self.cancel_and_wait()
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=['qcow2', 'qed'])
diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out
new file mode 100644
index 0000000000..71009c239f
--- /dev/null
+++ b/tests/qemu-iotests/041.out
@@ -0,0 +1,5 @@
+..................
+----------------------------------------------------------------------
+Ran 18 tests
+
+OK
diff --git a/tests/qemu-iotests/042 b/tests/qemu-iotests/042
new file mode 100755
index 0000000000..c3c3ca8d02
--- /dev/null
+++ b/tests/qemu-iotests/042
@@ -0,0 +1,78 @@
+#!/bin/bash
+#
+# Test qemu-img operation on zero size images
+#
+# Copyright (C) 2012 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=kwolf@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2 qcow qed vmdk
+_supported_proto file
+_supported_os Linux
+
+echo
+echo "== Creating zero size image =="
+
+_make_test_img 0
+_check_test_img
+
+mv $TEST_IMG $TEST_IMG.orig
+
+echo
+echo "== Converting the image =="
+
+$QEMU_IMG convert -O $IMGFMT $TEST_IMG.orig $TEST_IMG
+_check_test_img
+
+echo
+echo "== Converting the image, compressed =="
+
+if [ "$IMGFMT" == "qcow2" ]; then
+ $QEMU_IMG convert -c -O $IMGFMT $TEST_IMG.orig $TEST_IMG
+fi
+_check_test_img
+
+echo
+echo "== Rebasing the image =="
+
+$QEMU_IMG rebase -u -b $TEST_IMG.orig $TEST_IMG
+$QEMU_IMG rebase -b $TEST_IMG.orig $TEST_IMG
+_check_test_img
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
+
diff --git a/tests/qemu-iotests/042.out b/tests/qemu-iotests/042.out
new file mode 100644
index 0000000000..dc80f4b5b3
--- /dev/null
+++ b/tests/qemu-iotests/042.out
@@ -0,0 +1,15 @@
+QA output created by 042
+
+== Creating zero size image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=0
+No errors were found on the image.
+
+== Converting the image ==
+No errors were found on the image.
+
+== Converting the image, compressed ==
+No errors were found on the image.
+
+== Rebasing the image ==
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/043 b/tests/qemu-iotests/043
new file mode 100755
index 0000000000..3ba08dc94e
--- /dev/null
+++ b/tests/qemu-iotests/043
@@ -0,0 +1,95 @@
+#!/bin/bash
+#
+# Test that qemu-img info --backing-chain detects infinite loops
+#
+# Copyright (C) 2012 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=stefanha@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+ rm -f $TEST_IMG.[123].base
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+# Any format supporting backing files
+_supported_fmt qcow qcow2 vmdk qed
+_supported_proto generic
+_supported_os Linux
+
+
+size=128M
+_make_test_img $size
+$QEMU_IMG rebase -u -b $TEST_IMG $TEST_IMG
+
+echo
+echo "== backing file references self =="
+_img_info --backing-chain
+
+_make_test_img $size
+mv $TEST_IMG $TEST_IMG.base
+_make_test_img -b $TEST_IMG.base $size
+$QEMU_IMG rebase -u -b $TEST_IMG $TEST_IMG.base
+
+echo
+echo "== parent references self =="
+_img_info --backing-chain
+
+_make_test_img $size
+mv $TEST_IMG $TEST_IMG.1.base
+_make_test_img -b $TEST_IMG.1.base $size
+mv $TEST_IMG $TEST_IMG.2.base
+_make_test_img -b $TEST_IMG.2.base $size
+mv $TEST_IMG $TEST_IMG.3.base
+_make_test_img -b $TEST_IMG.3.base $size
+$QEMU_IMG rebase -u -b $TEST_IMG.2.base $TEST_IMG.1.base
+
+echo
+echo "== ancestor references another ancestor =="
+_img_info --backing-chain
+
+_make_test_img $size
+mv $TEST_IMG $TEST_IMG.1.base
+_make_test_img -b $TEST_IMG.1.base $size
+mv $TEST_IMG $TEST_IMG.2.base
+_make_test_img -b $TEST_IMG.2.base $size
+
+echo
+echo "== finite chain of length 3 (human) =="
+_img_info --backing-chain
+
+echo
+echo "== finite chain of length 3 (json) =="
+_img_info --backing-chain --output=json
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/043.out b/tests/qemu-iotests/043.out
new file mode 100644
index 0000000000..ad23337db5
--- /dev/null
+++ b/tests/qemu-iotests/043.out
@@ -0,0 +1,66 @@
+QA output created by 043
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+
+== backing file references self ==
+qemu-img: Backing file 'TEST_DIR/t.IMGFMT' creates an infinite loop.
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base'
+
+== parent references self ==
+qemu-img: Backing file 'TEST_DIR/t.IMGFMT' creates an infinite loop.
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.1.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.2.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.3.base'
+
+== ancestor references another ancestor ==
+qemu-img: Backing file 'TEST_DIR/t.IMGFMT.2.base' creates an infinite loop.
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.1.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.2.base'
+
+== finite chain of length 3 (human) ==
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 128M (134217728 bytes)
+cluster_size: 65536
+backing file: TEST_DIR/t.IMGFMT.2.base
+
+image: TEST_DIR/t.IMGFMT.2.base
+file format: IMGFMT
+virtual size: 128M (134217728 bytes)
+cluster_size: 65536
+backing file: TEST_DIR/t.IMGFMT.1.base
+
+image: TEST_DIR/t.IMGFMT.1.base
+file format: IMGFMT
+virtual size: 128M (134217728 bytes)
+cluster_size: 65536
+
+== finite chain of length 3 (json) ==
+[
+ {
+ "virtual-size": 134217728,
+ "filename": "TEST_DIR/t.IMGFMT",
+ "cluster-size": 65536,
+ "format": "IMGFMT",
+ "backing-filename": "TEST_DIR/t.IMGFMT.2.base",
+ "dirty-flag": false
+ },
+ {
+ "virtual-size": 134217728,
+ "filename": "TEST_DIR/t.IMGFMT.2.base",
+ "cluster-size": 65536,
+ "format": "IMGFMT",
+ "backing-filename": "TEST_DIR/t.IMGFMT.1.base",
+ "dirty-flag": false
+ },
+ {
+ "virtual-size": 134217728,
+ "filename": "TEST_DIR/t.IMGFMT.1.base",
+ "cluster-size": 65536,
+ "format": "IMGFMT",
+ "dirty-flag": false
+ }
+]
+*** done
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index d534e9466d..334534f22c 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -145,6 +145,16 @@ _check_test_img()
sed -e 's/qemu-img\: This image format does not support checks/No errors were found on the image./'
}
+_img_info()
+{
+ $QEMU_IMG info "$@" $TEST_IMG 2>&1 | \
+ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
+ -e "s#$TEST_DIR#TEST_DIR#g" \
+ -e "s#$IMGFMT#IMGFMT#g" \
+ -e "/^disk size:/ D" \
+ -e "/actual-size/ D"
+}
+
_get_pids_by_name()
{
if [ $# -ne 1 ]
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 66d2ba9689..ac86f54ae3 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -47,3 +47,6 @@
038 rw auto backing
039 rw auto
040 rw auto
+041 rw auto backing
+042 rw auto quick
+043 rw auto backing
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 3c60b2d163..735c6745d7 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -106,6 +106,10 @@ class VM(object):
return self._qmp.cmd(cmd, args=qmp_args)
+ def get_qmp_event(self, wait=False):
+ '''Poll for one queued QMP events and return it'''
+ return self._qmp.pull_event(wait=wait)
+
def get_qmp_events(self, wait=False):
'''Poll for queued QMP events and return a list of dicts'''
events = self._qmp.get_events(wait=wait)
diff --git a/tests/tcg/hello-i386.c b/tests/tcg/hello-i386.c
index 86afc34fb1..fa00380de2 100644
--- a/tests/tcg/hello-i386.c
+++ b/tests/tcg/hello-i386.c
@@ -1,6 +1,6 @@
#include <asm/unistd.h>
-static inline volatile void exit(int status)
+static inline void exit(int status)
{
int __res;
__asm__ volatile ("movl %%ecx,%%ebx\n"\
@@ -17,6 +17,7 @@ static inline int write(int fd, const char * buf, int len)
"popl %%ebx\n"\
: "=a" (status) \
: "0" (__NR_write),"S" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(len)));
+ return status;
}
void _start(void)
diff --git a/tests/tcg/test-i386.c b/tests/tcg/test-i386.c
index 64d929e482..40392ac51d 100644
--- a/tests/tcg/test-i386.c
+++ b/tests/tcg/test-i386.c
@@ -785,7 +785,7 @@ void fpu_clear_exceptions(void)
long double fpregs[8];
} float_env32;
- asm volatile ("fnstenv %0\n" : : "m" (float_env32));
+ asm volatile ("fnstenv %0\n" : "=m" (float_env32));
float_env32.fpus &= ~0x7f;
asm volatile ("fldenv %0\n" : : "m" (float_env32));
}
diff --git a/trace-events b/trace-events
index ed136a56a1..7ee21e58ea 100644
--- a/trace-events
+++ b/trace-events
@@ -77,10 +77,18 @@ stream_start(void *bs, void *base, void *s, void *co, void *opaque) "bs %p base
commit_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
commit_start(void *bs, void *base, void *top, void *s, void *co, void *opaque) "bs %p base %p top %p s %p co %p opaque %p"
+# block/mirror.c
+mirror_start(void *bs, void *s, void *co, void *opaque) "bs %p s %p co %p opaque %p"
+mirror_before_flush(void *s) "s %p"
+mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64
+mirror_before_sleep(void *s, int64_t cnt, int synced) "s %p dirty count %"PRId64" synced %d"
+mirror_one_iteration(void *s, int64_t sector_num, int nb_sectors) "s %p sector_num %"PRId64" nb_sectors %d"
+
# blockdev.c
qmp_block_job_cancel(void *job) "job %p"
qmp_block_job_pause(void *job) "job %p"
qmp_block_job_resume(void *job) "job %p"
+qmp_block_job_complete(void *job) "job %p"
block_job_cb(void *bs, void *job, int ret) "bs %p job %p ret %d"
qmp_block_stream(void *bs, void *job) "bs %p job %p"
diff --git a/ui/vnc.c b/ui/vnc.c
index 66ae93010f..d0ffcc54af 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2850,7 +2850,7 @@ char *vnc_display_local_addr(DisplayState *ds)
return vnc_socket_local_addr("%s:%s", vs->lsock);
}
-int vnc_display_open(DisplayState *ds, const char *display)
+void vnc_display_open(DisplayState *ds, const char *display, Error **errp)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
const char *options;
@@ -2868,14 +2868,15 @@ int vnc_display_open(DisplayState *ds, const char *display)
#endif
int lock_key_sync = 1;
- if (!vnc_display)
- return -1;
+ if (!vnc_display) {
+ error_setg(errp, "VNC display not active");
+ return;
+ }
vnc_display_close(ds);
if (strcmp(display, "none") == 0)
- return 0;
+ return;
- if (!(vs->display = strdup(display)))
- return -1;
+ vs->display = g_strdup(display);
vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
options = display;
@@ -2883,13 +2884,11 @@ int vnc_display_open(DisplayState *ds, const char *display)
options++;
if (strncmp(options, "password", 8) == 0) {
if (fips_get_state()) {
- fprintf(stderr,
- "VNC password auth disabled due to FIPS mode, "
- "consider using the VeNCrypt or SASL authentication "
- "methods as an alternative\n");
- g_free(vs->display);
- vs->display = NULL;
- return -1;
+ error_setg(errp,
+ "VNC password auth disabled due to FIPS mode, "
+ "consider using the VeNCrypt or SASL authentication "
+ "methods as an alternative");
+ goto fail;
}
password = 1; /* Require password auth */
} else if (strncmp(options, "reverse", 7) == 0) {
@@ -2919,18 +2918,14 @@ int vnc_display_open(DisplayState *ds, const char *display)
VNC_DEBUG("Trying certificate path '%s'\n", path);
if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
- fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
+ error_setg(errp, "Failed to find x509 certificates/keys in %s", path);
g_free(path);
- g_free(vs->display);
- vs->display = NULL;
- return -1;
+ goto fail;
}
g_free(path);
} else {
- fprintf(stderr, "No certificate path provided\n");
- g_free(vs->display);
- vs->display = NULL;
- return -1;
+ error_setg(errp, "No certificate path provided");
+ goto fail;
}
#endif
#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
@@ -2949,10 +2944,8 @@ int vnc_display_open(DisplayState *ds, const char *display)
} else if (strncmp(options+6, "force-shared", 12) == 0) {
vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
} else {
- fprintf(stderr, "unknown vnc share= option\n");
- g_free(vs->display);
- vs->display = NULL;
- return -1;
+ error_setg(errp, "unknown vnc share= option");
+ goto fail;
}
}
}
@@ -3053,52 +3046,50 @@ int vnc_display_open(DisplayState *ds, const char *display)
#ifdef CONFIG_VNC_SASL
if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
- fprintf(stderr, "Failed to initialize SASL auth %s",
- sasl_errstring(saslErr, NULL, NULL));
- g_free(vs->display);
- vs->display = NULL;
- return -1;
+ error_setg(errp, "Failed to initialize SASL auth: %s",
+ sasl_errstring(saslErr, NULL, NULL));
+ goto fail;
}
#endif
vs->lock_key_sync = lock_key_sync;
if (reverse) {
/* connect to viewer */
- if (strncmp(display, "unix:", 5) == 0)
- vs->lsock = unix_connect(display+5);
- else
- vs->lsock = inet_connect(display, NULL);
- if (-1 == vs->lsock) {
- g_free(vs->display);
- vs->display = NULL;
- return -1;
+ int csock;
+ vs->lsock = -1;
+ if (strncmp(display, "unix:", 5) == 0) {
+ csock = unix_connect(display+5, errp);
} else {
- int csock = vs->lsock;
- vs->lsock = -1;
- vnc_connect(vs, csock, 0);
+ csock = inet_connect(display, errp);
}
- return 0;
-
+ if (csock < 0) {
+ goto fail;
+ }
+ vnc_connect(vs, csock, 0);
} else {
/* listen for connects */
char *dpy;
dpy = g_malloc(256);
if (strncmp(display, "unix:", 5) == 0) {
pstrcpy(dpy, 256, "unix:");
- vs->lsock = unix_listen(display+5, dpy+5, 256-5);
+ vs->lsock = unix_listen(display+5, dpy+5, 256-5, errp);
} else {
vs->lsock = inet_listen(display, dpy, 256,
- SOCK_STREAM, 5900, NULL);
+ SOCK_STREAM, 5900, errp);
}
- if (-1 == vs->lsock) {
+ if (vs->lsock < 0) {
g_free(dpy);
- return -1;
- } else {
- g_free(vs->display);
- vs->display = dpy;
+ goto fail;
}
+ g_free(vs->display);
+ vs->display = dpy;
+ qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
}
- return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
+ return;
+
+fail:
+ g_free(vs->display);
+ vs->display = NULL;
}
void vnc_display_add_client(DisplayState *ds, int csock, int skipauth)
diff --git a/vl.c b/vl.c
index ee3c43ae2f..5a3d316980 100644
--- a/vl.c
+++ b/vl.c
@@ -203,7 +203,6 @@ CharDriverState *serial_hds[MAX_SERIAL_PORTS];
CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
int win2k_install_hack = 0;
-int usb_enabled = 0;
int singlestep = 0;
int smp_cpus = 1;
int max_cpus = 0;
@@ -341,7 +340,7 @@ static const RunStateTransition runstate_transitions_def[] = {
{ RUN_STATE_DEBUG, RUN_STATE_RUNNING },
{ RUN_STATE_INMIGRATE, RUN_STATE_RUNNING },
- { RUN_STATE_INMIGRATE, RUN_STATE_PRELAUNCH },
+ { RUN_STATE_INMIGRATE, RUN_STATE_PAUSED },
{ RUN_STATE_INTERNAL_ERROR, RUN_STATE_PAUSED },
{ RUN_STATE_INTERNAL_ERROR, RUN_STATE_FINISH_MIGRATE },
@@ -790,6 +789,89 @@ static int parse_sandbox(QemuOpts *opts, void *opaque)
return 0;
}
+/*********QEMU USB setting******/
+bool usb_enabled(bool default_usb)
+{
+ QemuOpts *mach_opts;
+ mach_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
+ if (mach_opts) {
+ return qemu_opt_get_bool(mach_opts, "usb", default_usb);
+ }
+ return default_usb;
+}
+
+#ifndef _WIN32
+static int parse_add_fd(QemuOpts *opts, void *opaque)
+{
+ int fd, dupfd, flags;
+ int64_t fdset_id;
+ const char *fd_opaque = NULL;
+
+ fd = qemu_opt_get_number(opts, "fd", -1);
+ fdset_id = qemu_opt_get_number(opts, "set", -1);
+ fd_opaque = qemu_opt_get(opts, "opaque");
+
+ if (fd < 0) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR,
+ "fd option is required and must be non-negative");
+ return -1;
+ }
+
+ if (fd <= STDERR_FILENO) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR,
+ "fd cannot be a standard I/O stream");
+ return -1;
+ }
+
+ /*
+ * All fds inherited across exec() necessarily have FD_CLOEXEC
+ * clear, while qemu sets FD_CLOEXEC on all other fds used internally.
+ */
+ flags = fcntl(fd, F_GETFD);
+ if (flags == -1 || (flags & FD_CLOEXEC)) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR,
+ "fd is not valid or already in use");
+ return -1;
+ }
+
+ if (fdset_id < 0) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR,
+ "set option is required and must be non-negative");
+ return -1;
+ }
+
+#ifdef F_DUPFD_CLOEXEC
+ dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+#else
+ dupfd = dup(fd);
+ if (dupfd != -1) {
+ qemu_set_cloexec(dupfd);
+ }
+#endif
+ if (dupfd == -1) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR,
+ "Error duplicating fd: %s", strerror(errno));
+ return -1;
+ }
+
+ /* add the duplicate fd, and optionally the opaque string, to the fd set */
+ monitor_fdset_add_fd(dupfd, true, fdset_id, fd_opaque ? true : false,
+ fd_opaque, NULL);
+
+ return 0;
+}
+
+static int cleanup_add_fd(QemuOpts *opts, void *opaque)
+{
+ int fd;
+
+ fd = qemu_opt_get_number(opts, "fd", -1);
+ close(fd);
+
+ return 0;
+}
+#endif
+
/***********************************************************/
/* QEMU Block devices */
@@ -1077,8 +1159,9 @@ static int usb_device_add(const char *devname)
const char *p;
USBDevice *dev = NULL;
- if (!usb_enabled)
+ if (!usb_enabled(false)) {
return -1;
+ }
/* drivers with .usbdevice_name entry in USBDeviceInfo */
dev = usbdevice_create(devname);
@@ -1114,8 +1197,9 @@ static int usb_device_del(const char *devname)
if (strstart(devname, "host:", &p))
return usb_host_device_close(p);
- if (!usb_enabled)
+ if (!usb_enabled(false)) {
return -1;
+ }
p = strchr(devname, '.');
if (!p)
@@ -3083,10 +3167,16 @@ int main(int argc, char **argv, char **envp)
}
break;
case QEMU_OPTION_usb:
- usb_enabled = 1;
+ machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
+ if (machine_opts) {
+ qemu_opt_set_bool(machine_opts, "usb", true);
+ }
break;
case QEMU_OPTION_usbdevice:
- usb_enabled = 1;
+ machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
+ if (machine_opts) {
+ qemu_opt_set_bool(machine_opts, "usb", true);
+ }
add_device_config(DEV_USB, optarg);
break;
case QEMU_OPTION_device:
@@ -3309,6 +3399,18 @@ int main(int argc, char **argv, char **envp)
exit(0);
}
break;
+ case QEMU_OPTION_add_fd:
+#ifndef _WIN32
+ opts = qemu_opts_parse(qemu_find_opts("add-fd"), optarg, 0);
+ if (!opts) {
+ exit(0);
+ }
+#else
+ error_report("File descriptor passing is disabled on this "
+ "platform");
+ exit(1);
+#endif
+ break;
default:
os_parse_cmd_args(popt->index, optarg);
}
@@ -3320,6 +3422,16 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
+#ifndef _WIN32
+ if (qemu_opts_foreach(qemu_find_opts("add-fd"), parse_add_fd, NULL, 1)) {
+ exit(1);
+ }
+
+ if (qemu_opts_foreach(qemu_find_opts("add-fd"), cleanup_add_fd, NULL, 1)) {
+ exit(1);
+ }
+#endif
+
if (machine == NULL) {
fprintf(stderr, "No machine found.\n");
exit(1);
@@ -3653,7 +3765,7 @@ int main(int argc, char **argv, char **envp)
current_machine = machine;
/* init USB devices */
- if (usb_enabled) {
+ if (usb_enabled(false)) {
if (foreach_device_config(DEV_USB, usb_parse) < 0)
exit(1);
}
@@ -3711,10 +3823,13 @@ int main(int argc, char **argv, char **envp)
#ifdef CONFIG_VNC
/* init remote displays */
if (vnc_display) {
+ Error *local_err = NULL;
vnc_display_init(ds);
- if (vnc_display_open(ds, vnc_display) < 0) {
- fprintf(stderr, "Failed to start VNC server on `%s'\n",
- vnc_display);
+ vnc_display_open(ds, vnc_display, &local_err);
+ if (local_err != NULL) {
+ fprintf(stderr, "Failed to start VNC server on `%s': %s\n",
+ vnc_display, error_get_pretty(local_err));
+ error_free(local_err);
exit(1);
}
@@ -3766,16 +3881,12 @@ int main(int argc, char **argv, char **envp)
}
if (incoming) {
- Error *errp = NULL;
- int ret = qemu_start_incoming_migration(incoming, &errp);
- if (ret < 0) {
- if (error_is_set(&errp)) {
- fprintf(stderr, "Migrate: %s\n", error_get_pretty(errp));
- error_free(errp);
- }
- fprintf(stderr, "Migration failed. Exit code %s(%d), exiting.\n",
- incoming, ret);
- exit(ret);
+ Error *local_err = NULL;
+ qemu_start_incoming_migration(incoming, &local_err);
+ if (local_err) {
+ fprintf(stderr, "-incoming %s: %s\n", incoming, error_get_pretty(local_err));
+ error_free(local_err);
+ exit(1);
}
} else if (autostart) {
vm_start();