aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile45
-rw-r--r--Makefile.objs11
-rw-r--r--Makefile.target5
-rw-r--r--block.c19
-rw-r--r--block.h1
-rw-r--r--block/qcow2-cache.c12
-rw-r--r--block/qcow2-refcount.c38
-rw-r--r--block/qcow2.h2
-rw-r--r--block/raw-posix.c21
-rw-r--r--block/raw-win32.c29
-rw-r--r--block/sheepdog.c71
-rw-r--r--block/vmdk.c1297
-rw-r--r--block_int.h2
-rw-r--r--blockdev.c4
-rw-r--r--compiler.h34
-rwxr-xr-xconfigure44
-rw-r--r--cpu-common.h19
-rw-r--r--docs/qapi-code-gen.txt316
-rw-r--r--error.h1
-rw-r--r--exec-all.h2
-rw-r--r--exec.c12
-rw-r--r--hmp-commands.hx2
-rw-r--r--hw/ds1225y.c152
-rw-r--r--hw/esp.c2
-rw-r--r--hw/lsi53c895a.c22
-rw-r--r--hw/mips.h4
-rw-r--r--hw/mips_jazz.c10
-rw-r--r--hw/scsi-bus.c9
-rw-r--r--hw/scsi-disk.c21
-rw-r--r--hw/scsi-generic.c5
-rw-r--r--hw/scsi.h10
-rw-r--r--hw/spapr_vscsi.c28
-rw-r--r--hw/usb-hid.c9
-rw-r--r--hw/usb-msd.c9
-rw-r--r--hw/usb-uhci.c6
-rw-r--r--hw/virtio-net.c2
-rw-r--r--hw/virtio-serial-bus.c2
-rw-r--r--iov.c49
-rw-r--r--iov.h10
-rw-r--r--module.h2
-rw-r--r--pc-bios/README7
-rw-r--r--pc-bios/openbios-sparc32bin377388 -> 381484 bytes
-rw-r--r--pc-bios/openbios-sparc64bin1598328 -> 1598328 bytes
-rw-r--r--poison.h9
-rw-r--r--qapi-schema-guest.json217
-rw-r--r--qapi-schema-test.json22
-rw-r--r--qapi/qapi-dealloc-visitor.c147
-rw-r--r--qapi/qapi-dealloc-visitor.h26
-rw-r--r--qapi/qapi-types-core.h20
-rw-r--r--qapi/qapi-visit-core.c118
-rw-r--r--qapi/qapi-visit-core.h76
-rw-r--r--qapi/qmp-core.h41
-rw-r--r--qapi/qmp-dispatch.c124
-rw-r--r--qapi/qmp-input-visitor.c301
-rw-r--r--qapi/qmp-input-visitor.h27
-rw-r--r--qapi/qmp-output-visitor.c239
-rw-r--r--qapi/qmp-output-visitor.h28
-rw-r--r--qapi/qmp-registry.c40
-rw-r--r--qemu-common.h25
-rw-r--r--qemu-config.c6
-rw-r--r--qemu-ga.c654
-rw-r--r--qemu-img-cmds.hx4
-rw-r--r--qemu-img.c31
-rw-r--r--qemu-img.texi6
-rw-r--r--qemu-io.c2653
-rw-r--r--qemu-options.hx8
-rw-r--r--qerror.c13
-rw-r--r--qerror.h9
-rw-r--r--qga/guest-agent-command-state.c73
-rw-r--r--qga/guest-agent-commands.c518
-rw-r--r--qga/guest-agent-core.h31
-rw-r--r--qlist.h11
-rw-r--r--qmp-commands.hx34
-rwxr-xr-xscripts/checkpatch.pl1
-rw-r--r--scripts/ordereddict.py127
-rw-r--r--scripts/qapi-commands.py385
-rw-r--r--scripts/qapi-types.py270
-rw-r--r--scripts/qapi-visit.py246
-rw-r--r--scripts/qapi.py203
-rw-r--r--target-alpha/cpu.h5
-rw-r--r--target-alpha/op_helper.c6
-rw-r--r--target-arm/cpu.h8
-rw-r--r--target-arm/helper.c193
-rw-r--r--target-arm/helper.h293
-rw-r--r--target-arm/iwmmxt_helper.c80
-rw-r--r--target-arm/machine.c12
-rw-r--r--target-arm/neon_helper.c201
-rw-r--r--target-arm/translate.c457
-rw-r--r--target-microblaze/cpu.h4
-rw-r--r--target-microblaze/op_helper.c14
-rw-r--r--target-mips/cpu.h4
-rw-r--r--target-mips/op_helper.c6
-rw-r--r--target-sparc/cpu.h39
-rw-r--r--target-sparc/helper.c149
-rw-r--r--target-sparc/helper.h4
-rw-r--r--target-sparc/op_helper.c88
-rw-r--r--target-sparc/translate.c33
-rw-r--r--tcg/mips/tcg-target.c2
-rw-r--r--test-qmp-commands.c113
-rw-r--r--test-visitor.c306
-rw-r--r--trace-events10
-rw-r--r--usb-redir.c1218
102 files changed, 9727 insertions, 2607 deletions
diff --git a/Makefile b/Makefile
index b3ffbe2407..f3a03ad87e 100644
--- a/Makefile
+++ b/Makefile
@@ -106,6 +106,8 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
QEMU_CFLAGS+=$(CURL_CFLAGS)
+QEMU_CFLAGS+=$(GLIB_CFLAGS)
+
ui/cocoa.o: ui/cocoa.m
ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
@@ -138,7 +140,7 @@ endif
######################################################################
qemu-img.o: qemu-img-cmds.h
-qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
+qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
@@ -160,19 +162,54 @@ check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o error.o qerror.o qemu-error.o $(CHECK_PROG_DEPS)
+$(qapi-obj-y): $(GENERATED_HEADERS)
+qapi-dir := qapi-generated
+test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
+
+$(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h
+$(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
+ $(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
+$(qapi-dir)/test-qapi-visit.c: $(qapi-dir)/test-qapi-visit.h
+$(qapi-dir)/test-qapi-visit.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py
+ $(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
+$(qapi-dir)/test-qmp-commands.h: $(qapi-dir)/test-qmp-marshal.c
+$(qapi-dir)/test-qmp-marshal.c: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
+ $(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
+
+$(qapi-dir)/qga-qapi-types.c: $(qapi-dir)/qga-qapi-types.h
+$(qapi-dir)/qga-qapi-types.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py
+ $(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
+$(qapi-dir)/qga-qapi-visit.c: $(qapi-dir)/qga-qapi-visit.h
+$(qapi-dir)/qga-qapi-visit.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py
+ $(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
+$(qapi-dir)/qga-qmp-marshal.c: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py
+ $(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
+
+test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
+test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+
+test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
+test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
+
+QGALIB=qga/guest-agent-command-state.o qga/guest-agent-commands.o
+
+qemu-ga.o: $(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c) $(qapi-obj-y)
+qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o $(qapi-dir)/qga-qapi-visit.o $(qapi-dir)/qga-qapi-types.o $(qapi-dir)/qga-qmp-marshal.o
+
QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
rm -f qemu-options.def
- rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
+ rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
rm -Rf .libs
- rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
+ rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
rm -f qemu-img-cmds.h
rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
rm -f trace-dtrace.h trace-dtrace.h-timestamp
+ rm -rf $(qapi-dir)
$(MAKE) -C tests clean
for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
@@ -361,4 +398,4 @@ tarbin:
$(mandir)/man8/qemu-nbd.8
# Include automatically generated dependency files
--include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d)
+-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
diff --git a/Makefile.objs b/Makefile.objs
index 1635df6e2f..6991a9f52a 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -171,7 +171,7 @@ user-obj-y += cutils.o cache-utils.o
hw-obj-y =
hw-obj-y += vl.o loader.o
-hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o
+hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
hw-obj-y += fw_cfg.o
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
@@ -205,6 +205,7 @@ hw-obj-$(CONFIG_HPET) += hpet.o
hw-obj-$(CONFIG_APPLESMC) += applesmc.o
hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
+hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o
# PPC devices
hw-obj-$(CONFIG_OPENPIC) += openpic.o
@@ -372,7 +373,15 @@ endif
libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
+######################################################################
+# qapi
+
+qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o
+qapi-nested-y += qmp-registry.o qmp-dispatch.o
+qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y))
+
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
+vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS)
diff --git a/Makefile.target b/Makefile.target
index c566eb1149..cde509ba76 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -95,7 +95,7 @@ tcg/tcg.o: cpu.h
# HELPER_CFLAGS is used for all the code compiled with static register
# variables
-%_helper.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
+op_helper.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
# Note: this is a workaround. The real fix is to avoid compiling
# cpu_signal_handler() in user-exec.c.
@@ -191,7 +191,7 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
obj-$(CONFIG_NO_PCI) += pci-stub.o
-obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
+obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
obj-y += vhost_net.o
obj-$(CONFIG_VHOST_NET) += vhost.o
obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
@@ -204,6 +204,7 @@ QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
+QEMU_CFLAGS += $(GLIB_CFLAGS)
# xen support
obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o xen-mapcache.o
diff --git a/block.c b/block.c
index 24a25d569b..9549b9eff9 100644
--- a/block.c
+++ b/block.c
@@ -1147,6 +1147,25 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
}
/**
+ * Length of a allocated file in bytes. Sparse files are counted by actual
+ * allocated space. Return < 0 if error or unknown.
+ */
+int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (drv->bdrv_get_allocated_file_size) {
+ return drv->bdrv_get_allocated_file_size(bs);
+ }
+ if (bs->file) {
+ return bdrv_get_allocated_file_size(bs->file);
+ }
+ return -ENOTSUP;
+}
+
+/**
* Length of a file in bytes. Return < 0 if error or unknown.
*/
int64_t bdrv_getlength(BlockDriverState *bs)
diff --git a/block.h b/block.h
index 859d1d9835..59cc410e3b 100644
--- a/block.h
+++ b/block.h
@@ -89,6 +89,7 @@ int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
int bdrv_truncate(BlockDriverState *bs, int64_t offset);
int64_t bdrv_getlength(BlockDriverState *bs);
+int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs);
int bdrv_commit(BlockDriverState *bs);
diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c
index 382473933c..84088477a4 100644
--- a/block/qcow2-cache.c
+++ b/block/qcow2-cache.c
@@ -312,3 +312,15 @@ found:
c->entries[i].dirty = true;
}
+bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
+ bool enable)
+{
+ bool old = c->writethrough;
+
+ if (!old && enable) {
+ qcow2_cache_flush(bs, c);
+ }
+
+ c->writethrough = enable;
+ return old;
+}
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index ac95b88fe1..14b2f67f14 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -705,8 +705,15 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
BDRVQcowState *s = bs->opaque;
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
int64_t old_offset, old_l2_offset;
- int i, j, l1_modified, nb_csectors, refcount;
+ int i, j, l1_modified = 0, nb_csectors, refcount;
int ret;
+ bool old_l2_writethrough, old_refcount_writethrough;
+
+ /* Switch caches to writeback mode during update */
+ old_l2_writethrough =
+ qcow2_cache_set_writethrough(bs, s->l2_table_cache, false);
+ old_refcount_writethrough =
+ qcow2_cache_set_writethrough(bs, s->refcount_block_cache, false);
l2_table = NULL;
l1_table = NULL;
@@ -720,7 +727,11 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l1_allocated = 1;
if (bdrv_pread(bs->file, l1_table_offset,
l1_table, l1_size2) != l1_size2)
+ {
+ ret = -EIO;
goto fail;
+ }
+
for(i = 0;i < l1_size; i++)
be64_to_cpus(&l1_table[i]);
} else {
@@ -729,7 +740,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l1_allocated = 0;
}
- l1_modified = 0;
for(i = 0; i < l1_size; i++) {
l2_offset = l1_table[i];
if (l2_offset) {
@@ -773,6 +783,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
if (refcount < 0) {
+ ret = -EIO;
goto fail;
}
}
@@ -803,6 +814,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
}
if (refcount < 0) {
+ ret = -EIO;
goto fail;
} else if (refcount == 1) {
l2_offset |= QCOW_OFLAG_COPIED;
@@ -813,6 +825,18 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
}
}
+
+ ret = 0;
+fail:
+ if (l2_table) {
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ }
+
+ /* Enable writethrough cache mode again */
+ qcow2_cache_set_writethrough(bs, s->l2_table_cache, old_l2_writethrough);
+ qcow2_cache_set_writethrough(bs, s->refcount_block_cache,
+ old_refcount_writethrough);
+
if (l1_modified) {
for(i = 0; i < l1_size; i++)
cpu_to_be64s(&l1_table[i]);
@@ -824,15 +848,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
if (l1_allocated)
qemu_free(l1_table);
- return 0;
- fail:
- if (l2_table) {
- qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
- }
-
- if (l1_allocated)
- qemu_free(l1_table);
- return -EIO;
+ return ret;
}
diff --git a/block/qcow2.h b/block/qcow2.h
index e1ae3e8c2b..6a0a21b694 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -228,6 +228,8 @@ int qcow2_read_snapshots(BlockDriverState *bs);
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
bool writethrough);
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
+bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
+ bool enable);
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 34b64aa205..cd89c8312a 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -793,6 +793,17 @@ static int64_t raw_getlength(BlockDriverState *bs)
}
#endif
+static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
+{
+ struct stat st;
+ BDRVRawState *s = bs->opaque;
+
+ if (fstat(s->fd, &st) < 0) {
+ return -errno;
+ }
+ return (int64_t)st.st_blocks * 512;
+}
+
static int raw_create(const char *filename, QEMUOptionParameter *options)
{
int fd;
@@ -888,6 +899,8 @@ static BlockDriver bdrv_file = {
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
.create_options = raw_create_options,
};
@@ -1156,6 +1169,8 @@ static BlockDriver bdrv_host_device = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
/* generic scsi device */
#ifdef __linux__
@@ -1277,6 +1292,8 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = floppy_is_inserted,
@@ -1380,6 +1397,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = cdrom_is_inserted,
@@ -1503,6 +1522,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = cdrom_is_inserted,
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 56bd7195a1..91067e7595 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -213,6 +213,31 @@ static int64_t raw_getlength(BlockDriverState *bs)
return l.QuadPart;
}
+static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
+{
+ typedef DWORD (WINAPI * get_compressed_t)(const char *filename,
+ DWORD * high);
+ get_compressed_t get_compressed;
+ struct _stati64 st;
+ const char *filename = bs->filename;
+ /* WinNT support GetCompressedFileSize to determine allocate size */
+ get_compressed =
+ (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"),
+ "GetCompressedFileSizeA");
+ if (get_compressed) {
+ DWORD high, low;
+ low = get_compressed(filename, &high);
+ if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) {
+ return (((int64_t) high) << 32) + low;
+ }
+ }
+
+ if (_stati64(filename, &st) < 0) {
+ return -1;
+ }
+ return st.st_size;
+}
+
static int raw_create(const char *filename, QEMUOptionParameter *options)
{
int fd;
@@ -257,6 +282,8 @@ static BlockDriver bdrv_file = {
.bdrv_write = raw_write,
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
.create_options = raw_create_options,
};
@@ -419,6 +446,8 @@ static BlockDriver bdrv_host_device = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
};
static void bdrv_file_init(void)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 80d106c2b2..77a4de5100 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -1286,6 +1286,49 @@ static int do_sd_create(char *filename, int64_t vdi_size,
return 0;
}
+static int sd_prealloc(const char *filename)
+{
+ BlockDriverState *bs = NULL;
+ uint32_t idx, max_idx;
+ int64_t vdi_size;
+ void *buf = qemu_mallocz(SD_DATA_OBJ_SIZE);
+ int ret;
+
+ ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR);
+ if (ret < 0) {
+ goto out;
+ }
+
+ vdi_size = bdrv_getlength(bs);
+ if (vdi_size < 0) {
+ ret = vdi_size;
+ goto out;
+ }
+ max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE);
+
+ for (idx = 0; idx < max_idx; idx++) {
+ /*
+ * The created image can be a cloned image, so we need to read
+ * a data from the source image.
+ */
+ ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
+ if (ret < 0) {
+ goto out;
+ }
+ ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
+ if (ret < 0) {
+ goto out;
+ }
+ }
+out:
+ if (bs) {
+ bdrv_delete(bs);
+ }
+ qemu_free(buf);
+
+ return ret;
+}
+
static int sd_create(const char *filename, QEMUOptionParameter *options)
{
int ret;
@@ -1295,13 +1338,15 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
BDRVSheepdogState s;
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid;
+ int prealloc = 0;
+ const char *vdiname;
- strstart(filename, "sheepdog:", (const char **)&filename);
+ strstart(filename, "sheepdog:", &vdiname);
memset(&s, 0, sizeof(s));
memset(vdi, 0, sizeof(vdi));
memset(tag, 0, sizeof(tag));
- if (parse_vdiname(&s, filename, vdi, &snapid, tag) < 0) {
+ if (parse_vdiname(&s, vdiname, vdi, &snapid, tag) < 0) {
error_report("invalid filename");
return -EINVAL;
}
@@ -1311,6 +1356,16 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
vdi_size = options->value.n;
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
backing_file = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
+ if (!options->value.s || !strcmp(options->value.s, "off")) {
+ prealloc = 0;
+ } else if (!strcmp(options->value.s, "full")) {
+ prealloc = 1;
+ } else {
+ error_report("Invalid preallocation mode: '%s'",
+ options->value.s);
+ return -EINVAL;
+ }
}
options++;
}
@@ -1348,7 +1403,12 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
bdrv_delete(bs);
}
- return do_sd_create((char *)vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port);
+ ret = do_sd_create(vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port);
+ if (!prealloc || ret) {
+ return ret;
+ }
+
+ return sd_prealloc(filename);
}
static void sd_close(BlockDriverState *bs)
@@ -1984,6 +2044,11 @@ static QEMUOptionParameter sd_create_options[] = {
.type = OPT_STRING,
.help = "File name of a base image"
},
+ {
+ .name = BLOCK_OPT_PREALLOC,
+ .type = OPT_STRING,
+ .help = "Preallocation mode (allowed values: off, full)"
+ },
{ NULL }
};
diff --git a/block/vmdk.c b/block/vmdk.c
index 922b23d8f5..37478d2553 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -60,7 +60,12 @@ typedef struct {
#define L2_CACHE_SIZE 16
-typedef struct BDRVVmdkState {
+typedef struct VmdkExtent {
+ BlockDriverState *file;
+ bool flat;
+ int64_t sectors;
+ int64_t end_sector;
+ int64_t flat_start_offset;
int64_t l1_table_offset;
int64_t l1_backup_table_offset;
uint32_t *l1_table;
@@ -74,7 +79,15 @@ typedef struct BDRVVmdkState {
uint32_t l2_cache_counts[L2_CACHE_SIZE];
unsigned int cluster_sectors;
+} VmdkExtent;
+
+typedef struct BDRVVmdkState {
+ int desc_offset;
+ bool cid_updated;
uint32_t parent_cid;
+ int num_extents;
+ /* Extent array with num_extents entries, ascend ordered by address */
+ VmdkExtent *extents;
} BDRVVmdkState;
typedef struct VmdkMetaData {
@@ -89,21 +102,77 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
{
uint32_t magic;
- if (buf_size < 4)
+ if (buf_size < 4) {
return 0;
+ }
magic = be32_to_cpu(*(uint32_t *)buf);
if (magic == VMDK3_MAGIC ||
- magic == VMDK4_MAGIC)
+ magic == VMDK4_MAGIC) {
return 100;
- else
+ } else {
+ const char *p = (const char *)buf;
+ const char *end = p + buf_size;
+ while (p < end) {
+ if (*p == '#') {
+ /* skip comment line */
+ while (p < end && *p != '\n') {
+ p++;
+ }
+ p++;
+ continue;
+ }
+ if (*p == ' ') {
+ while (p < end && *p == ' ') {
+ p++;
+ }
+ /* skip '\r' if windows line endings used. */
+ if (p < end && *p == '\r') {
+ p++;
+ }
+ /* only accept blank lines before 'version=' line */
+ if (p == end || *p != '\n') {
+ return 0;
+ }
+ p++;
+ continue;
+ }
+ if (end - p >= strlen("version=X\n")) {
+ if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 ||
+ strncmp("version=2\n", p, strlen("version=2\n")) == 0) {
+ return 100;
+ }
+ }
+ if (end - p >= strlen("version=X\r\n")) {
+ if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 ||
+ strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) {
+ return 100;
+ }
+ }
+ return 0;
+ }
return 0;
+ }
}
#define CHECK_CID 1
#define SECTOR_SIZE 512
-#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each
-#define HEADER_SIZE 512 // first sector of 512 bytes
+#define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */
+#define BUF_SIZE 4096
+#define HEADER_SIZE 512 /* first sector of 512 bytes */
+
+static void vmdk_free_extents(BlockDriverState *bs)
+{
+ int i;
+ BDRVVmdkState *s = bs->opaque;
+
+ for (i = 0; i < s->num_extents; i++) {
+ qemu_free(s->extents[i].l1_table);
+ qemu_free(s->extents[i].l2_cache);
+ qemu_free(s->extents[i].l1_backup_table);
+ }
+ qemu_free(s->extents);
+}
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
{
@@ -111,10 +180,11 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
uint32_t cid;
const char *p_name, *cid_str;
size_t cid_str_size;
+ BDRVVmdkState *s = bs->opaque;
- /* the descriptor offset = 0x200 */
- if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+ if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
return 0;
+ }
if (parent) {
cid_str = "parentCID";
@@ -124,9 +194,10 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
cid_str_size = sizeof("CID");
}
- if ((p_name = strstr(desc,cid_str)) != NULL) {
+ p_name = strstr(desc, cid_str);
+ if (p_name != NULL) {
p_name += cid_str_size;
- sscanf(p_name,"%x",&cid);
+ sscanf(p_name, "%x", &cid);
}
return cid;
@@ -136,21 +207,25 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
{
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
char *p_name, *tmp_str;
+ BDRVVmdkState *s = bs->opaque;
- /* the descriptor offset = 0x200 */
- if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
- return -1;
+ memset(desc, 0, sizeof(desc));
+ if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
+ return -EIO;
+ }
- tmp_str = strstr(desc,"parentCID");
+ tmp_str = strstr(desc, "parentCID");
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
- if ((p_name = strstr(desc,"CID")) != NULL) {
+ p_name = strstr(desc, "CID");
+ if (p_name != NULL) {
p_name += sizeof("CID");
snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid);
pstrcat(desc, sizeof(desc), tmp_desc);
}
- if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0)
- return -1;
+ if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) {
+ return -EIO;
+ }
return 0;
}
@@ -162,302 +237,387 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
uint32_t cur_pcid;
if (p_bs) {
- cur_pcid = vmdk_read_cid(p_bs,0);
- if (s->parent_cid != cur_pcid)
- // CID not valid
+ cur_pcid = vmdk_read_cid(p_bs, 0);
+ if (s->parent_cid != cur_pcid) {
+ /* CID not valid */
return 0;
+ }
}
#endif
- // CID valid
+ /* CID valid */
return 1;
}
-static int vmdk_snapshot_create(const char *filename, const char *backing_file)
+static int vmdk_parent_open(BlockDriverState *bs)
{
- int snp_fd, p_fd;
- int ret;
- uint32_t p_cid;
- char *p_name, *gd_buf, *rgd_buf;
- const char *real_filename, *temp_str;
- VMDK4Header header;
- uint32_t gde_entries, gd_size;
- int64_t gd_offset, rgd_offset, capacity, gt_size;
- char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE];
- static const char desc_template[] =
- "# Disk DescriptorFile\n"
- "version=1\n"
- "CID=%x\n"
- "parentCID=%x\n"
- "createType=\"monolithicSparse\"\n"
- "parentFileNameHint=\"%s\"\n"
- "\n"
- "# Extent description\n"
- "RW %u SPARSE \"%s\"\n"
- "\n"
- "# The Disk Data Base \n"
- "#DDB\n"
- "\n";
-
- snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644);
- if (snp_fd < 0)
- return -errno;
- p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE);
- if (p_fd < 0) {
- close(snp_fd);
- return -errno;
- }
+ char *p_name;
+ char desc[DESC_SIZE + 1];
+ BDRVVmdkState *s = bs->opaque;
- /* read the header */
- if (lseek(p_fd, 0x0, SEEK_SET) == -1) {
- ret = -errno;
- goto fail;
- }
- if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) {
- ret = -errno;
- goto fail;
+ desc[DESC_SIZE] = '\0';
+ if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
+ return -1;
}
- /* write the header */
- if (lseek(snp_fd, 0x0, SEEK_SET) == -1) {
- ret = -errno;
- goto fail;
- }
- if (write(snp_fd, hdr, HEADER_SIZE) == -1) {
- ret = -errno;
- goto fail;
+ p_name = strstr(desc, "parentFileNameHint");
+ if (p_name != NULL) {
+ char *end_name;
+
+ p_name += sizeof("parentFileNameHint") + 1;
+ end_name = strchr(p_name, '\"');
+ if (end_name == NULL) {
+ return -1;
+ }
+ if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
+ return -1;
+ }
+
+ pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
}
- memset(&header, 0, sizeof(header));
- memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC
+ return 0;
+}
- if (ftruncate(snp_fd, header.grain_offset << 9)) {
- ret = -errno;
- goto fail;
+/* Create and append extent to the extent array. Return the added VmdkExtent
+ * address. return NULL if allocation failed. */
+static VmdkExtent *vmdk_add_extent(BlockDriverState *bs,
+ BlockDriverState *file, bool flat, int64_t sectors,
+ int64_t l1_offset, int64_t l1_backup_offset,
+ uint32_t l1_size,
+ int l2_size, unsigned int cluster_sectors)
+{
+ VmdkExtent *extent;
+ BDRVVmdkState *s = bs->opaque;
+
+ s->extents = qemu_realloc(s->extents,
+ (s->num_extents + 1) * sizeof(VmdkExtent));
+ extent = &s->extents[s->num_extents];
+ s->num_extents++;
+
+ memset(extent, 0, sizeof(VmdkExtent));
+ extent->file = file;
+ extent->flat = flat;
+ extent->sectors = sectors;
+ extent->l1_table_offset = l1_offset;
+ extent->l1_backup_table_offset = l1_backup_offset;
+ extent->l1_size = l1_size;
+ extent->l1_entry_sectors = l2_size * cluster_sectors;
+ extent->l2_size = l2_size;
+ extent->cluster_sectors = cluster_sectors;
+
+ if (s->num_extents > 1) {
+ extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
+ } else {
+ extent->end_sector = extent->sectors;
}
- /* the descriptor offset = 0x200 */
- if (lseek(p_fd, 0x200, SEEK_SET) == -1) {
- ret = -errno;
- goto fail;
+ bs->total_sectors = extent->end_sector;
+ return extent;
+}
+
+static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
+{
+ int ret;
+ int l1_size, i;
+
+ /* read the L1 table */
+ l1_size = extent->l1_size * sizeof(uint32_t);
+ extent->l1_table = qemu_malloc(l1_size);
+ ret = bdrv_pread(extent->file,
+ extent->l1_table_offset,
+ extent->l1_table,
+ l1_size);
+ if (ret < 0) {
+ goto fail_l1;
}
- if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) {
- ret = -errno;
- goto fail;
+ for (i = 0; i < extent->l1_size; i++) {
+ le32_to_cpus(&extent->l1_table[i]);
}
- if ((p_name = strstr(p_desc,"CID")) != NULL) {
- p_name += sizeof("CID");
- sscanf(p_name,"%x",&p_cid);
+ if (extent->l1_backup_table_offset) {
+ extent->l1_backup_table = qemu_malloc(l1_size);
+ ret = bdrv_pread(extent->file,
+ extent->l1_backup_table_offset,
+ extent->l1_backup_table,
+ l1_size);
+ if (ret < 0) {
+ goto fail_l1b;
+ }
+ for (i = 0; i < extent->l1_size; i++) {
+ le32_to_cpus(&extent->l1_backup_table[i]);
+ }
}
- real_filename = filename;
- if ((temp_str = strrchr(real_filename, '\\')) != NULL)
- real_filename = temp_str + 1;
- if ((temp_str = strrchr(real_filename, '/')) != NULL)
- real_filename = temp_str + 1;
- if ((temp_str = strrchr(real_filename, ':')) != NULL)
- real_filename = temp_str + 1;
+ extent->l2_cache =
+ qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
+ return 0;
+ fail_l1b:
+ qemu_free(extent->l1_backup_table);
+ fail_l1:
+ qemu_free(extent->l1_table);
+ return ret;
+}
- snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file,
- (uint32_t)header.capacity, real_filename);
+static int vmdk_open_vmdk3(BlockDriverState *bs, int flags)
+{
+ int ret;
+ uint32_t magic;
+ VMDK3Header header;
+ BDRVVmdkState *s = bs->opaque;
+ VmdkExtent *extent;
- /* write the descriptor */
- if (lseek(snp_fd, 0x200, SEEK_SET) == -1) {
- ret = -errno;
+ s->desc_offset = 0x200;
+ ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header));
+ if (ret < 0) {
goto fail;
}
- if (write(snp_fd, s_desc, strlen(s_desc)) == -1) {
- ret = -errno;
+ extent = vmdk_add_extent(bs,
+ bs->file, false,
+ le32_to_cpu(header.disk_sectors),
+ le32_to_cpu(header.l1dir_offset) << 9,
+ 0, 1 << 6, 1 << 9,
+ le32_to_cpu(header.granularity));
+ ret = vmdk_init_tables(bs, extent);
+ if (ret) {
+ /* vmdk_init_tables cleans up on fail, so only free allocation of
+ * vmdk_add_extent here. */
goto fail;
}
+ return 0;
+ fail:
+ vmdk_free_extents(bs);
+ return ret;
+}
- gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table
- rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table
- capacity = header.capacity * SECTOR_SIZE; // Extent size
- /*
- * Each GDE span 32M disk, means:
- * 512 GTE per GT, each GTE points to grain
- */
- gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE;
- if (!gt_size) {
- ret = -EINVAL;
- goto fail;
- }
- gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde
- gd_size = gde_entries * sizeof(uint32_t);
+static int vmdk_open_vmdk4(BlockDriverState *bs, int flags)
+{
+ int ret;
+ uint32_t magic;
+ uint32_t l1_size, l1_entry_sectors;
+ VMDK4Header header;
+ BDRVVmdkState *s = bs->opaque;
+ VmdkExtent *extent;
- /* write RGD */
- rgd_buf = qemu_malloc(gd_size);
- if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) {
- ret = -errno;
- goto fail_rgd;
+ s->desc_offset = 0x200;
+ ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header));
+ if (ret < 0) {
+ goto fail;
}
- if (read(p_fd, rgd_buf, gd_size) != gd_size) {
- ret = -errno;
- goto fail_rgd;
+ l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte)
+ * le64_to_cpu(header.granularity);
+ l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)
+ / l1_entry_sectors;
+ extent = vmdk_add_extent(bs, bs->file, false,
+ le64_to_cpu(header.capacity),
+ le64_to_cpu(header.gd_offset) << 9,
+ le64_to_cpu(header.rgd_offset) << 9,
+ l1_size,
+ le32_to_cpu(header.num_gtes_per_gte),
+ le64_to_cpu(header.granularity));
+ if (extent->l1_entry_sectors <= 0) {
+ ret = -EINVAL;
+ goto fail;
}
- if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) {
- ret = -errno;
- goto fail_rgd;
+ /* try to open parent images, if exist */
+ ret = vmdk_parent_open(bs);
+ if (ret) {
+ goto fail;
}
- if (write(snp_fd, rgd_buf, gd_size) == -1) {
- ret = -errno;
- goto fail_rgd;
+ s->parent_cid = vmdk_read_cid(bs, 1);
+ ret = vmdk_init_tables(bs, extent);
+ if (ret) {
+ goto fail;
}
+ return 0;
+ fail:
+ vmdk_free_extents(bs);
+ return ret;
+}
- /* write GD */
- gd_buf = qemu_malloc(gd_size);
- if (lseek(p_fd, gd_offset, SEEK_SET) == -1) {
- ret = -errno;
- goto fail_gd;
+/* find an option value out of descriptor file */
+static int vmdk_parse_description(const char *desc, const char *opt_name,
+ char *buf, int buf_size)
+{
+ char *opt_pos, *opt_end;
+ const char *end = desc + strlen(desc);
+
+ opt_pos = strstr(desc, opt_name);
+ if (!opt_pos) {
+ return -1;
}
- if (read(p_fd, gd_buf, gd_size) != gd_size) {
- ret = -errno;
- goto fail_gd;
+ /* Skip "=\"" following opt_name */
+ opt_pos += strlen(opt_name) + 2;
+ if (opt_pos >= end) {
+ return -1;
}
- if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) {
- ret = -errno;
- goto fail_gd;
+ opt_end = opt_pos;
+ while (opt_end < end && *opt_end != '"') {
+ opt_end++;
}
- if (write(snp_fd, gd_buf, gd_size) == -1) {
- ret = -errno;
- goto fail_gd;
+ if (opt_end == end || buf_size < opt_end - opt_pos + 1) {
+ return -1;
}
- ret = 0;
-
-fail_gd:
- qemu_free(gd_buf);
-fail_rgd:
- qemu_free(rgd_buf);
-fail:
- close(p_fd);
- close(snp_fd);
- return ret;
+ pstrcpy(buf, opt_end - opt_pos + 1, opt_pos);
+ return 0;
}
-static int vmdk_parent_open(BlockDriverState *bs)
+static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
+ const char *desc_file_path)
{
- char *p_name;
- char desc[DESC_SIZE];
+ int ret;
+ char access[11];
+ char type[11];
+ char fname[512];
+ const char *p = desc;
+ int64_t sectors = 0;
+ int64_t flat_offset;
+
+ while (*p) {
+ /* parse extent line:
+ * RW [size in sectors] FLAT "file-name.vmdk" OFFSET
+ * or
+ * RW [size in sectors] SPARSE "file-name.vmdk"
+ */
+ flat_offset = -1;
+ ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64,
+ access, &sectors, type, fname, &flat_offset);
+ if (ret < 4 || strcmp(access, "RW")) {
+ goto next_line;
+ } else if (!strcmp(type, "FLAT")) {
+ if (ret != 5 || flat_offset < 0) {
+ return -EINVAL;
+ }
+ } else if (ret != 4) {
+ return -EINVAL;
+ }
- /* the descriptor offset = 0x200 */
- if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
- return -1;
+ /* trim the quotation marks around */
+ if (fname[0] == '"') {
+ memmove(fname, fname + 1, strlen(fname));
+ if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') {
+ return -EINVAL;
+ }
+ fname[strlen(fname) - 1] = '\0';
+ }
+ if (sectors <= 0 ||
+ (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) ||
+ (strcmp(access, "RW"))) {
+ goto next_line;
+ }
- if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) {
- char *end_name;
+ /* save to extents array */
+ if (!strcmp(type, "FLAT")) {
+ /* FLAT extent */
+ char extent_path[PATH_MAX];
+ BlockDriverState *extent_file;
+ VmdkExtent *extent;
+
+ path_combine(extent_path, sizeof(extent_path),
+ desc_file_path, fname);
+ ret = bdrv_file_open(&extent_file, extent_path, bs->open_flags);
+ if (ret) {
+ return ret;
+ }
+ extent = vmdk_add_extent(bs, extent_file, true, sectors,
+ 0, 0, 0, 0, sectors);
+ extent->flat_start_offset = flat_offset;
+ } else {
+ /* SPARSE extent, not supported for now */
+ fprintf(stderr,
+ "VMDK: Not supported extent type \"%s\""".\n", type);
+ return -ENOTSUP;
+ }
+next_line:
+ /* move to next line */
+ while (*p && *p != '\n') {
+ p++;
+ }
+ p++;
+ }
+ return 0;
+}
- p_name += sizeof("parentFileNameHint") + 1;
- if ((end_name = strchr(p_name,'\"')) == NULL)
- return -1;
- if ((end_name - p_name) > sizeof (bs->backing_file) - 1)
- return -1;
+static int vmdk_open_desc_file(BlockDriverState *bs, int flags)
+{
+ int ret;
+ char buf[2048];
+ char ct[128];
+ BDRVVmdkState *s = bs->opaque;
- pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
+ ret = bdrv_pread(bs->file, 0, buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+ buf[2047] = '\0';
+ if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
+ return -EINVAL;
+ }
+ if (strcmp(ct, "monolithicFlat")) {
+ fprintf(stderr,
+ "VMDK: Not supported image type \"%s\""".\n", ct);
+ return -ENOTSUP;
+ }
+ s->desc_offset = 0;
+ ret = vmdk_parse_extents(buf, bs, bs->file->filename);
+ if (ret) {
+ return ret;
}
+ /* try to open parent images, if exist */
+ if (vmdk_parent_open(bs)) {
+ qemu_free(s->extents);
+ return -EINVAL;
+ }
+ s->parent_cid = vmdk_read_cid(bs, 1);
return 0;
}
static int vmdk_open(BlockDriverState *bs, int flags)
{
- BDRVVmdkState *s = bs->opaque;
uint32_t magic;
- int l1_size, i;
- if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic))
- goto fail;
+ if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) {
+ return -EIO;
+ }
magic = be32_to_cpu(magic);
if (magic == VMDK3_MAGIC) {
- VMDK3Header header;
-
- if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
- goto fail;
- s->cluster_sectors = le32_to_cpu(header.granularity);
- s->l2_size = 1 << 9;
- s->l1_size = 1 << 6;
- bs->total_sectors = le32_to_cpu(header.disk_sectors);
- s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
- s->l1_backup_table_offset = 0;
- s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+ return vmdk_open_vmdk3(bs, flags);
} else if (magic == VMDK4_MAGIC) {
- VMDK4Header header;
-
- if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
- goto fail;
- bs->total_sectors = le64_to_cpu(header.capacity);
- s->cluster_sectors = le64_to_cpu(header.granularity);
- s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
- s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
- if (s->l1_entry_sectors <= 0)
- goto fail;
- s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
- / s->l1_entry_sectors;
- s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
- s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
-
- // try to open parent images, if exist
- if (vmdk_parent_open(bs) != 0)
- goto fail;
- // write the CID once after the image creation
- s->parent_cid = vmdk_read_cid(bs,1);
+ return vmdk_open_vmdk4(bs, flags);
} else {
- goto fail;
- }
-
- /* read the L1 table */
- l1_size = s->l1_size * sizeof(uint32_t);
- s->l1_table = qemu_malloc(l1_size);
- if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size)
- goto fail;
- for(i = 0; i < s->l1_size; i++) {
- le32_to_cpus(&s->l1_table[i]);
- }
-
- if (s->l1_backup_table_offset) {
- s->l1_backup_table = qemu_malloc(l1_size);
- if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size)
- goto fail;
- for(i = 0; i < s->l1_size; i++) {
- le32_to_cpus(&s->l1_backup_table[i]);
- }
+ return vmdk_open_desc_file(bs, flags);
}
-
- s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
- return 0;
- fail:
- qemu_free(s->l1_backup_table);
- qemu_free(s->l1_table);
- qemu_free(s->l2_cache);
- return -1;
}
-static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
- uint64_t offset, int allocate);
-
-static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
- uint64_t offset, int allocate)
+static int get_whole_cluster(BlockDriverState *bs,
+ VmdkExtent *extent,
+ uint64_t cluster_offset,
+ uint64_t offset,
+ bool allocate)
{
- BDRVVmdkState *s = bs->opaque;
- uint8_t whole_grain[s->cluster_sectors*512]; // 128 sectors * 512 bytes each = grain size 64KB
+ /* 128 sectors * 512 bytes each = grain size 64KB */
+ uint8_t whole_grain[extent->cluster_sectors * 512];
- // we will be here if it's first write on non-exist grain(cluster).
- // try to read from parent image, if exist
+ /* we will be here if it's first write on non-exist grain(cluster).
+ * try to read from parent image, if exist */
if (bs->backing_hd) {
int ret;
- if (!vmdk_is_cid_valid(bs))
+ if (!vmdk_is_cid_valid(bs)) {
return -1;
+ }
+ /* floor offset to cluster */
+ offset -= offset % (extent->cluster_sectors * 512);
ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain,
- s->cluster_sectors);
+ extent->cluster_sectors);
if (ret < 0) {
return -1;
}
- //Write grain only into the active image
- ret = bdrv_write(bs->file, cluster_offset, whole_grain,
- s->cluster_sectors);
+ /* Write grain only into the active image */
+ ret = bdrv_write(extent->file, cluster_offset, whole_grain,
+ extent->cluster_sectors);
if (ret < 0) {
return -1;
}
@@ -465,85 +625,112 @@ static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
return 0;
}
-static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data)
+static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data)
{
- BDRVVmdkState *s = bs->opaque;
-
/* update L2 table */
- if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
- &(m_data->offset), sizeof(m_data->offset)) < 0)
+ if (bdrv_pwrite_sync(
+ extent->file,
+ ((int64_t)m_data->l2_offset * 512)
+ + (m_data->l2_index * sizeof(m_data->offset)),
+ &(m_data->offset),
+ sizeof(m_data->offset)
+ ) < 0) {
return -1;
+ }
/* update backup L2 table */
- if (s->l1_backup_table_offset != 0) {
- m_data->l2_offset = s->l1_backup_table[m_data->l1_index];
- if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
- &(m_data->offset), sizeof(m_data->offset)) < 0)
+ if (extent->l1_backup_table_offset != 0) {
+ m_data->l2_offset = extent->l1_backup_table[m_data->l1_index];
+ if (bdrv_pwrite_sync(
+ extent->file,
+ ((int64_t)m_data->l2_offset * 512)
+ + (m_data->l2_index * sizeof(m_data->offset)),
+ &(m_data->offset), sizeof(m_data->offset)
+ ) < 0) {
return -1;
+ }
}
return 0;
}
-static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
- uint64_t offset, int allocate)
+static int get_cluster_offset(BlockDriverState *bs,
+ VmdkExtent *extent,
+ VmdkMetaData *m_data,
+ uint64_t offset,
+ int allocate,
+ uint64_t *cluster_offset)
{
- BDRVVmdkState *s = bs->opaque;
unsigned int l1_index, l2_offset, l2_index;
int min_index, i, j;
uint32_t min_count, *l2_table, tmp = 0;
- uint64_t cluster_offset;
- if (m_data)
+ if (m_data) {
m_data->valid = 0;
-
- l1_index = (offset >> 9) / s->l1_entry_sectors;
- if (l1_index >= s->l1_size)
- return 0;
- l2_offset = s->l1_table[l1_index];
- if (!l2_offset)
+ }
+ if (extent->flat) {
+ *cluster_offset = extent->flat_start_offset;
return 0;
- for(i = 0; i < L2_CACHE_SIZE; i++) {
- if (l2_offset == s->l2_cache_offsets[i]) {
+ }
+
+ l1_index = (offset >> 9) / extent->l1_entry_sectors;
+ if (l1_index >= extent->l1_size) {
+ return -1;
+ }
+ l2_offset = extent->l1_table[l1_index];
+ if (!l2_offset) {
+ return -1;
+ }
+ for (i = 0; i < L2_CACHE_SIZE; i++) {
+ if (l2_offset == extent->l2_cache_offsets[i]) {
/* increment the hit count */
- if (++s->l2_cache_counts[i] == 0xffffffff) {
- for(j = 0; j < L2_CACHE_SIZE; j++) {
- s->l2_cache_counts[j] >>= 1;
+ if (++extent->l2_cache_counts[i] == 0xffffffff) {
+ for (j = 0; j < L2_CACHE_SIZE; j++) {
+ extent->l2_cache_counts[j] >>= 1;
}
}
- l2_table = s->l2_cache + (i * s->l2_size);
+ l2_table = extent->l2_cache + (i * extent->l2_size);
goto found;
}
}
/* not found: load a new entry in the least used one */
min_index = 0;
min_count = 0xffffffff;
- for(i = 0; i < L2_CACHE_SIZE; i++) {
- if (s->l2_cache_counts[i] < min_count) {
- min_count = s->l2_cache_counts[i];
+ for (i = 0; i < L2_CACHE_SIZE; i++) {
+ if (extent->l2_cache_counts[i] < min_count) {
+ min_count = extent->l2_cache_counts[i];
min_index = i;
}
}
- l2_table = s->l2_cache + (min_index * s->l2_size);
- if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) !=
- s->l2_size * sizeof(uint32_t))
- return 0;
+ l2_table = extent->l2_cache + (min_index * extent->l2_size);
+ if (bdrv_pread(
+ extent->file,
+ (int64_t)l2_offset * 512,
+ l2_table,
+ extent->l2_size * sizeof(uint32_t)
+ ) != extent->l2_size * sizeof(uint32_t)) {
+ return -1;
+ }
- s->l2_cache_offsets[min_index] = l2_offset;
- s->l2_cache_counts[min_index] = 1;
+ extent->l2_cache_offsets[min_index] = l2_offset;
+ extent->l2_cache_counts[min_index] = 1;
found:
- l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
- cluster_offset = le32_to_cpu(l2_table[l2_index]);
+ l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
+ *cluster_offset = le32_to_cpu(l2_table[l2_index]);
- if (!cluster_offset) {
- if (!allocate)
- return 0;
+ if (!*cluster_offset) {
+ if (!allocate) {
+ return -1;
+ }
- // Avoid the L2 tables update for the images that have snapshots.
- cluster_offset = bdrv_getlength(bs->file);
- bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9));
+ /* Avoid the L2 tables update for the images that have snapshots. */
+ *cluster_offset = bdrv_getlength(extent->file);
+ bdrv_truncate(
+ extent->file,
+ *cluster_offset + (extent->cluster_sectors << 9)
+ );
- cluster_offset >>= 9;
- tmp = cpu_to_le32(cluster_offset);
+ *cluster_offset >>= 9;
+ tmp = cpu_to_le32(*cluster_offset);
l2_table[l2_index] = tmp;
/* First of all we write grain itself, to avoid race condition
@@ -551,8 +738,10 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
* This problem may occur because of insufficient space on host disk
* or inappropriate VM shutdown.
*/
- if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1)
- return 0;
+ if (get_whole_cluster(
+ bs, extent, *cluster_offset, offset, allocate) == -1) {
+ return -1;
+ }
if (m_data) {
m_data->offset = tmp;
@@ -562,53 +751,95 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
m_data->valid = 1;
}
}
- cluster_offset <<= 9;
- return cluster_offset;
+ *cluster_offset <<= 9;
+ return 0;
+}
+
+static VmdkExtent *find_extent(BDRVVmdkState *s,
+ int64_t sector_num, VmdkExtent *start_hint)
+{
+ VmdkExtent *extent = start_hint;
+
+ if (!extent) {
+ extent = &s->extents[0];
+ }
+ while (extent < &s->extents[s->num_extents]) {
+ if (sector_num < extent->end_sector) {
+ return extent;
+ }
+ extent++;
+ }
+ return NULL;
}
static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum)
{
BDRVVmdkState *s = bs->opaque;
- int index_in_cluster, n;
- uint64_t cluster_offset;
+ int64_t index_in_cluster, n, ret;
+ uint64_t offset;
+ VmdkExtent *extent;
- cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
- index_in_cluster = sector_num % s->cluster_sectors;
- n = s->cluster_sectors - index_in_cluster;
- if (n > nb_sectors)
+ extent = find_extent(s, sector_num, NULL);
+ if (!extent) {
+ return 0;
+ }
+ ret = get_cluster_offset(bs, extent, NULL,
+ sector_num * 512, 0, &offset);
+ /* get_cluster_offset returning 0 means success */
+ ret = !ret;
+
+ index_in_cluster = sector_num % extent->cluster_sectors;
+ n = extent->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors) {
n = nb_sectors;
+ }
*pnum = n;
- return (cluster_offset != 0);
+ return ret;
}
static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
BDRVVmdkState *s = bs->opaque;
- int index_in_cluster, n, ret;
+ int ret;
+ uint64_t n, index_in_cluster;
+ VmdkExtent *extent = NULL;
uint64_t cluster_offset;
while (nb_sectors > 0) {
- cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
- index_in_cluster = sector_num % s->cluster_sectors;
- n = s->cluster_sectors - index_in_cluster;
- if (n > nb_sectors)
+ extent = find_extent(s, sector_num, extent);
+ if (!extent) {
+ return -EIO;
+ }
+ ret = get_cluster_offset(
+ bs, extent, NULL,
+ sector_num << 9, 0, &cluster_offset);
+ index_in_cluster = sector_num % extent->cluster_sectors;
+ n = extent->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors) {
n = nb_sectors;
- if (!cluster_offset) {
- // try to read from parent image, if exist
+ }
+ if (ret) {
+ /* if not allocated, try to read from parent image, if exist */
if (bs->backing_hd) {
- if (!vmdk_is_cid_valid(bs))
- return -1;
+ if (!vmdk_is_cid_valid(bs)) {
+ return -EINVAL;
+ }
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
- if (ret < 0)
- return -1;
+ if (ret < 0) {
+ return ret;
+ }
} else {
memset(buf, 0, 512 * n);
}
} else {
- if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
- return -1;
+ ret = bdrv_pread(extent->file,
+ cluster_offset + index_in_cluster * 512,
+ buf, n * 512);
+ if (ret < 0) {
+ return ret;
+ }
}
nb_sectors -= n;
sector_num += n;
@@ -621,110 +852,101 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
BDRVVmdkState *s = bs->opaque;
- VmdkMetaData m_data;
- int index_in_cluster, n;
+ VmdkExtent *extent = NULL;
+ int n, ret;
+ int64_t index_in_cluster;
uint64_t cluster_offset;
- static int cid_update = 0;
+ VmdkMetaData m_data;
if (sector_num > bs->total_sectors) {
fprintf(stderr,
"(VMDK) Wrong offset: sector_num=0x%" PRIx64
" total_sectors=0x%" PRIx64 "\n",
sector_num, bs->total_sectors);
- return -1;
+ return -EIO;
}
while (nb_sectors > 0) {
- index_in_cluster = sector_num & (s->cluster_sectors - 1);
- n = s->cluster_sectors - index_in_cluster;
- if (n > nb_sectors)
+ extent = find_extent(s, sector_num, extent);
+ if (!extent) {
+ return -EIO;
+ }
+ ret = get_cluster_offset(
+ bs,
+ extent,
+ &m_data,
+ sector_num << 9, 1,
+ &cluster_offset);
+ if (ret) {
+ return -EINVAL;
+ }
+ index_in_cluster = sector_num % extent->cluster_sectors;
+ n = extent->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors) {
n = nb_sectors;
- cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1);
- if (!cluster_offset)
- return -1;
+ }
- if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
- return -1;
+ ret = bdrv_pwrite(extent->file,
+ cluster_offset + index_in_cluster * 512,
+ buf,
+ n * 512);
+ if (ret < 0) {
+ return ret;
+ }
if (m_data.valid) {
/* update L2 tables */
- if (vmdk_L2update(bs, &m_data) == -1)
- return -1;
+ if (vmdk_L2update(extent, &m_data) == -1) {
+ return -EIO;
+ }
}
nb_sectors -= n;
sector_num += n;
buf += n * 512;
- // update CID on the first write every time the virtual disk is opened
- if (!cid_update) {
+ /* update CID on the first write every time the virtual disk is
+ * opened */
+ if (!s->cid_updated) {
vmdk_write_cid(bs, time(NULL));
- cid_update++;
+ s->cid_updated = true;
}
}
return 0;
}
-static int vmdk_create(const char *filename, QEMUOptionParameter *options)
+
+static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat)
{
- int fd, i;
+ int ret, i;
+ int fd = 0;
VMDK4Header header;
uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
- static const char desc_template[] =
- "# Disk DescriptorFile\n"
- "version=1\n"
- "CID=%x\n"
- "parentCID=ffffffff\n"
- "createType=\"monolithicSparse\"\n"
- "\n"
- "# Extent description\n"
- "RW %" PRId64 " SPARSE \"%s\"\n"
- "\n"
- "# The Disk Data Base \n"
- "#DDB\n"
- "\n"
- "ddb.virtualHWVersion = \"%d\"\n"
- "ddb.geometry.cylinders = \"%" PRId64 "\"\n"
- "ddb.geometry.heads = \"16\"\n"
- "ddb.geometry.sectors = \"63\"\n"
- "ddb.adapterType = \"ide\"\n";
- char desc[1024];
- const char *real_filename, *temp_str;
- int64_t total_size = 0;
- const char *backing_file = NULL;
- int flags = 0;
- int ret;
- // Read out options
- while (options && options->name) {
- if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
- total_size = options->value.n / 512;
- } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
- backing_file = options->value.s;
- } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
- flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0;
- }
- options++;
+ fd = open(
+ filename,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ if (fd < 0) {
+ return -errno;
}
-
- /* XXX: add support for backing file */
- if (backing_file) {
- return vmdk_snapshot_create(filename, backing_file);
+ if (flat) {
+ ret = ftruncate(fd, filesize);
+ if (ret < 0) {
+ ret = -errno;
+ }
+ goto exit;
}
-
- fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
- 0644);
- if (fd < 0)
- return -errno;
magic = cpu_to_be32(VMDK4_MAGIC);
memset(&header, 0, sizeof(header));
header.version = 1;
header.flags = 3; /* ?? */
- header.capacity = total_size;
+ header.capacity = filesize / 512;
header.granularity = 128;
header.num_gtes_per_gte = 512;
- grains = (total_size + header.granularity - 1) / header.granularity;
+ grains = (filesize / 512 + header.granularity - 1) / header.granularity;
gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
- gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
+ gt_count =
+ (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
header.desc_offset = 1;
@@ -735,7 +957,6 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
((header.gd_offset + gd_size + (gt_size * gt_count) +
header.granularity - 1) / header.granularity) *
header.granularity;
-
/* swap endianness for all header fields */
header.version = cpu_to_le32(header.version);
header.flags = cpu_to_le32(header.flags);
@@ -793,27 +1014,255 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
}
}
- /* compose the descriptor */
- real_filename = filename;
- if ((temp_str = strrchr(real_filename, '\\')) != NULL)
- real_filename = temp_str + 1;
- if ((temp_str = strrchr(real_filename, '/')) != NULL)
- real_filename = temp_str + 1;
- if ((temp_str = strrchr(real_filename, ':')) != NULL)
- real_filename = temp_str + 1;
- snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL),
- total_size, real_filename,
- (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
- total_size / (int64_t)(63 * 16));
+ ret = 0;
+ exit:
+ close(fd);
+ return ret;
+}
+
+static int filename_decompose(const char *filename, char *path, char *prefix,
+ char *postfix, size_t buf_len)
+{
+ const char *p, *q;
+
+ if (filename == NULL || !strlen(filename)) {
+ fprintf(stderr, "Vmdk: no filename provided.\n");
+ return -1;
+ }
+ p = strrchr(filename, '/');
+ if (p == NULL) {
+ p = strrchr(filename, '\\');
+ }
+ if (p == NULL) {
+ p = strrchr(filename, ':');
+ }
+ if (p != NULL) {
+ p++;
+ if (p - filename >= buf_len) {
+ return -1;
+ }
+ pstrcpy(path, p - filename + 1, filename);
+ } else {
+ p = filename;
+ path[0] = '\0';
+ }
+ q = strrchr(p, '.');
+ if (q == NULL) {
+ pstrcpy(prefix, buf_len, p);
+ postfix[0] = '\0';
+ } else {
+ if (q - p >= buf_len) {
+ return -1;
+ }
+ pstrcpy(prefix, q - p + 1, p);
+ pstrcpy(postfix, buf_len, q);
+ }
+ return 0;
+}
+
+static int relative_path(char *dest, int dest_size,
+ const char *base, const char *target)
+{
+ int i = 0;
+ int n = 0;
+ const char *p, *q;
+#ifdef _WIN32
+ const char *sep = "\\";
+#else
+ const char *sep = "/";
+#endif
+
+ if (!(dest && base && target)) {
+ return -1;
+ }
+ if (path_is_absolute(target)) {
+ dest[dest_size - 1] = '\0';
+ strncpy(dest, target, dest_size - 1);
+ return 0;
+ }
+ while (base[i] == target[i]) {
+ i++;
+ }
+ p = &base[i];
+ q = &target[i];
+ while (*p) {
+ if (*p == *sep) {
+ n++;
+ }
+ p++;
+ }
+ dest[0] = '\0';
+ for (; n; n--) {
+ pstrcat(dest, dest_size, "..");
+ pstrcat(dest, dest_size, sep);
+ }
+ pstrcat(dest, dest_size, q);
+ return 0;
+}
+
+static int vmdk_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd, idx = 0;
+ char desc[BUF_SIZE];
+ int64_t total_size = 0, filesize;
+ const char *backing_file = NULL;
+ const char *fmt = NULL;
+ int flags = 0;
+ int ret = 0;
+ bool flat, split;
+ char ext_desc_lines[BUF_SIZE] = "";
+ char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX];
+ const int64_t split_size = 0x80000000; /* VMDK has constant split size */
+ const char *desc_extent_line;
+ char parent_desc_line[BUF_SIZE] = "";
+ uint32_t parent_cid = 0xffffffff;
+ const char desc_template[] =
+ "# Disk DescriptorFile\n"
+ "version=1\n"
+ "CID=%x\n"
+ "parentCID=%x\n"
+ "createType=\"%s\"\n"
+ "%s"
+ "\n"
+ "# Extent description\n"
+ "%s"
+ "\n"
+ "# The Disk Data Base\n"
+ "#DDB\n"
+ "\n"
+ "ddb.virtualHWVersion = \"%d\"\n"
+ "ddb.geometry.cylinders = \"%" PRId64 "\"\n"
+ "ddb.geometry.heads = \"16\"\n"
+ "ddb.geometry.sectors = \"63\"\n"
+ "ddb.adapterType = \"ide\"\n";
+
+ if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) {
+ return -EINVAL;
+ }
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ total_size = options->value.n;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+ backing_file = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
+ flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0;
+ } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) {
+ fmt = options->value.s;
+ }
+ options++;
+ }
+ if (!fmt) {
+ /* Default format to monolithicSparse */
+ fmt = "monolithicSparse";
+ } else if (strcmp(fmt, "monolithicFlat") &&
+ strcmp(fmt, "monolithicSparse") &&
+ strcmp(fmt, "twoGbMaxExtentSparse") &&
+ strcmp(fmt, "twoGbMaxExtentFlat")) {
+ fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt);
+ return -EINVAL;
+ }
+ split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
+ strcmp(fmt, "twoGbMaxExtentSparse"));
+ flat = !(strcmp(fmt, "monolithicFlat") &&
+ strcmp(fmt, "twoGbMaxExtentFlat"));
+ if (flat) {
+ desc_extent_line = "RW %lld FLAT \"%s\" 0\n";
+ } else {
+ desc_extent_line = "RW %lld SPARSE \"%s\"\n";
+ }
+ if (flat && backing_file) {
+ /* not supporting backing file for flat image */
+ return -ENOTSUP;
+ }
+ if (backing_file) {
+ char parent_filename[PATH_MAX];
+ BlockDriverState *bs = bdrv_new("");
+ ret = bdrv_open(bs, backing_file, 0, NULL);
+ if (ret != 0) {
+ bdrv_delete(bs);
+ return ret;
+ }
+ if (strcmp(bs->drv->format_name, "vmdk")) {
+ bdrv_delete(bs);
+ return -EINVAL;
+ }
+ filesize = bdrv_getlength(bs);
+ parent_cid = vmdk_read_cid(bs, 0);
+ bdrv_delete(bs);
+ relative_path(parent_filename, sizeof(parent_filename),
+ filename, backing_file);
+ snprintf(parent_desc_line, sizeof(parent_desc_line),
+ "parentFileNameHint=\"%s\"", parent_filename);
+ }
+
+ /* Create extents */
+ filesize = total_size;
+ while (filesize > 0) {
+ char desc_line[BUF_SIZE];
+ char ext_filename[PATH_MAX];
+ char desc_filename[PATH_MAX];
+ int64_t size = filesize;
- /* write the descriptor */
- lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
+ if (split && size > split_size) {
+ size = split_size;
+ }
+ if (split) {
+ snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s",
+ prefix, flat ? 'f' : 's', ++idx, postfix);
+ } else if (flat) {
+ snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s",
+ prefix, postfix);
+ } else {
+ snprintf(desc_filename, sizeof(desc_filename), "%s%s",
+ prefix, postfix);
+ }
+ snprintf(ext_filename, sizeof(ext_filename), "%s%s",
+ path, desc_filename);
+
+ if (vmdk_create_extent(ext_filename, size, flat)) {
+ return -EINVAL;
+ }
+ filesize -= size;
+
+ /* Format description line */
+ snprintf(desc_line, sizeof(desc_line),
+ desc_extent_line, size / 512, desc_filename);
+ pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line);
+ }
+ /* generate descriptor file */
+ snprintf(desc, sizeof(desc), desc_template,
+ (unsigned int)time(NULL),
+ parent_cid,
+ fmt,
+ parent_desc_line,
+ ext_desc_lines,
+ (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
+ total_size / (int64_t)(63 * 16 * 512));
+ if (split || flat) {
+ fd = open(
+ filename,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ } else {
+ fd = open(
+ filename,
+ O_WRONLY | O_BINARY | O_LARGEFILE,
+ 0644);
+ }
+ if (fd < 0) {
+ return -errno;
+ }
+ /* the descriptor offset = 0x200 */
+ if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) {
+ ret = -errno;
+ goto exit;
+ }
ret = qemu_write_full(fd, desc, strlen(desc));
if (ret != strlen(desc)) {
ret = -errno;
goto exit;
}
-
ret = 0;
exit:
close(fd);
@@ -822,17 +1271,47 @@ exit:
static void vmdk_close(BlockDriverState *bs)
{
- BDRVVmdkState *s = bs->opaque;
-
- qemu_free(s->l1_table);
- qemu_free(s->l2_cache);
+ vmdk_free_extents(bs);
}
static int vmdk_flush(BlockDriverState *bs)
{
- return bdrv_flush(bs->file);
+ int i, ret, err;
+ BDRVVmdkState *s = bs->opaque;
+
+ ret = bdrv_flush(bs->file);
+ for (i = 0; i < s->num_extents; i++) {
+ err = bdrv_flush(s->extents[i].file);
+ if (err < 0) {
+ ret = err;
+ }
+ }
+ return ret;
}
+static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs)
+{
+ int i;
+ int64_t ret = 0;
+ int64_t r;
+ BDRVVmdkState *s = bs->opaque;
+
+ ret = bdrv_get_allocated_file_size(bs->file);
+ if (ret < 0) {
+ return ret;
+ }
+ for (i = 0; i < s->num_extents; i++) {
+ if (s->extents[i].file == bs->file) {
+ continue;
+ }
+ r = bdrv_get_allocated_file_size(s->extents[i].file);
+ if (r < 0) {
+ return r;
+ }
+ ret += r;
+ }
+ return ret;
+}
static QEMUOptionParameter vmdk_create_options[] = {
{
@@ -850,20 +1329,28 @@ static QEMUOptionParameter vmdk_create_options[] = {
.type = OPT_FLAG,
.help = "VMDK version 6 image"
},
+ {
+ .name = BLOCK_OPT_SUBFMT,
+ .type = OPT_STRING,
+ .help =
+ "VMDK flat extent format, can be one of "
+ "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} "
+ },
{ NULL }
};
static BlockDriver bdrv_vmdk = {
- .format_name = "vmdk",
- .instance_size = sizeof(BDRVVmdkState),
- .bdrv_probe = vmdk_probe,
+ .format_name = "vmdk",
+ .instance_size = sizeof(BDRVVmdkState),
+ .bdrv_probe = vmdk_probe,
.bdrv_open = vmdk_open,
- .bdrv_read = vmdk_read,
- .bdrv_write = vmdk_write,
- .bdrv_close = vmdk_close,
- .bdrv_create = vmdk_create,
- .bdrv_flush = vmdk_flush,
- .bdrv_is_allocated = vmdk_is_allocated,
+ .bdrv_read = vmdk_read,
+ .bdrv_write = vmdk_write,
+ .bdrv_close = vmdk_close,
+ .bdrv_create = vmdk_create,
+ .bdrv_flush = vmdk_flush,
+ .bdrv_is_allocated = vmdk_is_allocated,
+ .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
.create_options = vmdk_create_options,
};
diff --git a/block_int.h b/block_int.h
index 1e265d274d..efb68038c4 100644
--- a/block_int.h
+++ b/block_int.h
@@ -39,6 +39,7 @@
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
#define BLOCK_OPT_TABLE_SIZE "table_size"
#define BLOCK_OPT_PREALLOC "preallocation"
+#define BLOCK_OPT_SUBFMT "subformat"
typedef struct AIOPool {
void (*cancel)(BlockDriverAIOCB *acb);
@@ -85,6 +86,7 @@ struct BlockDriver {
const char *protocol_name;
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
int64_t (*bdrv_getlength)(BlockDriverState *bs);
+ int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
diff --git a/blockdev.c b/blockdev.c
index a97a801e95..0b8d3a4f83 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -568,7 +568,7 @@ void do_commit(Monitor *mon, const QDict *qdict)
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *device = qdict_get_str(qdict, "device");
- const char *filename = qdict_get_try_str(qdict, "snapshot_file");
+ const char *filename = qdict_get_try_str(qdict, "snapshot-file");
const char *format = qdict_get_try_str(qdict, "format");
BlockDriverState *bs;
BlockDriver *drv, *old_drv, *proto_drv;
@@ -577,7 +577,7 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
char old_filename[1024];
if (!filename) {
- qerror_report(QERR_MISSING_PARAMETER, "snapshot_file");
+ qerror_report(QERR_MISSING_PARAMETER, "snapshot-file");
ret = -1;
goto out;
}
diff --git a/compiler.h b/compiler.h
new file mode 100644
index 0000000000..9af5dc6c91
--- /dev/null
+++ b/compiler.h
@@ -0,0 +1,34 @@
+/* public domain */
+
+#ifndef COMPILER_H
+#define COMPILER_H
+
+#include "config-host.h"
+
+#define QEMU_NORETURN __attribute__ ((__noreturn__))
+#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT
+#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define QEMU_WARN_UNUSED_RESULT
+#endif
+
+#define QEMU_BUILD_BUG_ON(x) \
+ typedef char qemu_build_bug_on__##__LINE__[(x)?-1:1];
+
+#if defined __GNUC__
+# if (__GNUC__ < 4) || \
+ defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4)
+ /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */
+# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2)))
+# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m)))
+# else
+ /* Use gnu_printf when supported (qemu uses standard format strings). */
+# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2)))
+# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m)))
+# endif
+#else
+#define GCC_ATTR /**/
+#define GCC_FMT_ATTR(n, m)
+#endif
+
+#endif /* COMPILER_H */
diff --git a/configure b/configure
index e57efb179c..6911c3be6a 100755
--- a/configure
+++ b/configure
@@ -177,6 +177,7 @@ spice=""
rbd=""
smartcard=""
smartcard_nss=""
+usb_redir=""
opengl=""
# parse CC options first
@@ -743,6 +744,10 @@ for opt do
;;
--enable-smartcard-nss) smartcard_nss="yes"
;;
+ --disable-usb-redir) usb_redir="no"
+ ;;
+ --enable-usb-redir) usb_redir="yes"
+ ;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
@@ -1018,6 +1023,8 @@ echo " --disable-smartcard disable smartcard support"
echo " --enable-smartcard enable smartcard support"
echo " --disable-smartcard-nss disable smartcard nss support"
echo " --enable-smartcard-nss enable smartcard nss support"
+echo " --disable-usb-redir disable usb network redirection support"
+echo " --enable-usb-redir enable usb network redirection support"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -1803,6 +1810,18 @@ EOF
fi
##########################################
+# glib support probe
+if $pkg_config --modversion gthread-2.0 gio-2.0 > /dev/null 2>&1 ; then
+ glib_cflags=`$pkg_config --cflags gthread-2.0 gio-2.0 2>/dev/null`
+ glib_libs=`$pkg_config --libs gthread-2.0 gio-2.0 2>/dev/null`
+ libs_softmmu="$glib_libs $libs_softmmu"
+ libs_tools="$glib_libs $libs_tools"
+else
+ echo "glib-2.0 required to compile QEMU"
+ exit 1
+fi
+
+##########################################
# pthread probe
PTHREADLIBS_LIST="-lpthread -lpthreadGC2"
@@ -2371,6 +2390,22 @@ if test "$smartcard" = "no" ; then
smartcard_nss="no"
fi
+# check for usbredirparser for usb network redirection support
+if test "$usb_redir" != "no" ; then
+ if $pkg_config libusbredirparser >/dev/null 2>&1 ; then
+ usb_redir="yes"
+ usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
+ usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)
+ QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags"
+ LIBS="$LIBS $usb_redir_libs"
+ else
+ if test "$usb_redir" = "yes"; then
+ feature_not_found "usb-redir"
+ fi
+ usb_redir="no"
+ fi
+fi
+
##########################################
##########################################
@@ -2520,6 +2555,7 @@ if test "$softmmu" = yes ; then
tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
tools="qemu-nbd\$(EXESUF) $tools"
+ tools="qemu-ga\$(EXESUF) $tools"
if [ "$check_utests" = "yes" ]; then
tools="check-qint check-qstring check-qdict check-qlist $tools"
tools="check-qfloat check-qjson $tools"
@@ -2617,6 +2653,7 @@ echo "spice support $spice"
echo "rbd support $rbd"
echo "xfsctl support $xfs"
echo "nss used $smartcard_nss"
+echo "usb net redir $usb_redir"
echo "OpenGL support $opengl"
if test $sdl_too_old = "yes"; then
@@ -2849,6 +2886,7 @@ if test "$bluez" = "yes" ; then
echo "CONFIG_BLUEZ=y" >> $config_host_mak
echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak
fi
+echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak
if test "$xen" = "yes" ; then
echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak
echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak
@@ -2910,6 +2948,10 @@ if test "$smartcard_nss" = "yes" ; then
echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak
fi
+if test "$usb_redir" = "yes" ; then
+ echo "CONFIG_USB_REDIR=y" >> $config_host_mak
+fi
+
if test "$opengl" = "yes" ; then
echo "CONFIG_OPENGL=y" >> $config_host_mak
fi
@@ -3473,6 +3515,8 @@ DIRS="tests tests/cris slirp audio block net pc-bios/optionrom"
DIRS="$DIRS pc-bios/spapr-rtas"
DIRS="$DIRS roms/seabios roms/vgabios"
DIRS="$DIRS fsdev ui"
+DIRS="$DIRS qapi"
+DIRS="$DIRS qga"
FILES="Makefile tests/Makefile"
FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
diff --git a/cpu-common.h b/cpu-common.h
index a5b80e1351..44b04b3839 100644
--- a/cpu-common.h
+++ b/cpu-common.h
@@ -134,28 +134,31 @@ void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size);
void qemu_flush_coalesced_mmio_buffer(void);
uint32_t ldub_phys(target_phys_addr_t addr);
-uint32_t lduw_phys(target_phys_addr_t addr);
uint32_t lduw_le_phys(target_phys_addr_t addr);
uint32_t lduw_be_phys(target_phys_addr_t addr);
-uint32_t ldl_phys(target_phys_addr_t addr);
uint32_t ldl_le_phys(target_phys_addr_t addr);
uint32_t ldl_be_phys(target_phys_addr_t addr);
-uint64_t ldq_phys(target_phys_addr_t addr);
uint64_t ldq_le_phys(target_phys_addr_t addr);
uint64_t ldq_be_phys(target_phys_addr_t addr);
-void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
-void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val);
void stb_phys(target_phys_addr_t addr, uint32_t val);
-void stw_phys(target_phys_addr_t addr, uint32_t val);
void stw_le_phys(target_phys_addr_t addr, uint32_t val);
void stw_be_phys(target_phys_addr_t addr, uint32_t val);
-void stl_phys(target_phys_addr_t addr, uint32_t val);
void stl_le_phys(target_phys_addr_t addr, uint32_t val);
void stl_be_phys(target_phys_addr_t addr, uint32_t val);
-void stq_phys(target_phys_addr_t addr, uint64_t val);
void stq_le_phys(target_phys_addr_t addr, uint64_t val);
void stq_be_phys(target_phys_addr_t addr, uint64_t val);
+#ifdef NEED_CPU_H
+uint32_t lduw_phys(target_phys_addr_t addr);
+uint32_t ldl_phys(target_phys_addr_t addr);
+uint64_t ldq_phys(target_phys_addr_t addr);
+void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
+void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val);
+void stw_phys(target_phys_addr_t addr, uint32_t val);
+void stl_phys(target_phys_addr_t addr, uint32_t val);
+void stq_phys(target_phys_addr_t addr, uint64_t val);
+#endif
+
void cpu_physical_memory_write_rom(target_phys_addr_t addr,
const uint8_t *buf, int len);
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
new file mode 100644
index 0000000000..b7befb5e48
--- /dev/null
+++ b/docs/qapi-code-gen.txt
@@ -0,0 +1,316 @@
+= How to use the QAPI code generator =
+
+* Note: as of this writing, QMP does not use QAPI. Eventually QMP
+commands will be converted to use QAPI internally. The following
+information describes QMP/QAPI as it will exist after the
+conversion.
+
+QAPI is a native C API within QEMU which provides management-level
+functionality to internal/external users. For external
+users/processes, this interface is made available by a JSON-based
+QEMU Monitor protocol that is provided by the QMP server.
+
+To map QMP-defined interfaces to the native C QAPI implementations,
+a JSON-based schema is used to define types and function
+signatures, and a set of scripts is used to generate types/signatures,
+and marshaling/dispatch code. The QEMU Guest Agent also uses these
+scripts, paired with a seperate schema, to generate
+marshaling/dispatch code for the guest agent server running in the
+guest.
+
+This document will describe how the schemas, scripts, and resulting
+code is used.
+
+
+== QMP/Guest agent schema ==
+
+This file defines the types, commands, and events used by QMP. It should
+fully describe the interface used by QMP.
+
+This file is designed to be loosely based on JSON although it's technically
+executable Python. While dictionaries are used, they are parsed as
+OrderedDicts so that ordering is preserved.
+
+There are two basic syntaxes used, type definitions and command definitions.
+
+The first syntax defines a type and is represented by a dictionary. There are
+two kinds of types that are supported: complex user-defined types, and enums.
+
+A complex type is a dictionary containing a single key who's value is a
+dictionary. This corresponds to a struct in C or an Object in JSON. An
+example of a complex type is:
+
+ { 'type': 'MyType',
+ 'data' { 'member1': 'str', 'member2': 'int', '*member3': 'str } }
+
+The use of '*' as a prefix to the name means the member is optional. Optional
+members should always be added to the end of the dictionary to preserve
+backwards compatibility.
+
+An enumeration type is a dictionary containing a single key who's value is a
+list of strings. An example enumeration is:
+
+ { 'enum': 'MyEnum', 'data': [ 'value1', 'value2', 'value3' ] }
+
+Generally speaking, complex types and enums should always use CamelCase for
+the type names.
+
+Commands are defined by using a list containing three members. The first
+member is the command name, the second member is a dictionary containing
+arguments, and the third member is the return type.
+
+An example command is:
+
+ { 'command': 'my-command',
+ 'data': { 'arg1': 'str', '*arg2': 'str' },
+ 'returns': 'str' ]
+
+Command names should be all lower case with words separated by a hyphen.
+
+
+== Code generation ==
+
+Schemas are fed into 3 scripts to generate all the code/files that, paired
+with the core QAPI libraries, comprise everything required to take JSON
+commands read in by a QMP/guest agent server, unmarshal the arguments into
+the underlying C types, call into the corresponding C function, and map the
+response back to a QMP/guest agent response to be returned to the user.
+
+As an example, we'll use the following schema, which describes a single
+complex user-defined type (which will produce a C struct, along with a list
+node structure that can be used to chain together a list of such types in
+case we want to accept/return a list of this type with a command), and a
+command which takes that type as a parameter and returns the same type:
+
+ mdroth@illuin:~/w/qemu2.git$ cat example-schema.json
+ { 'type': 'UserDefOne',
+ 'data': { 'integer': 'int', 'string': 'str' } }
+
+ { 'command': 'my-command',
+ 'data': {'arg1': 'UserDefOne'},
+ 'returns': 'UserDefOne' }
+ mdroth@illuin:~/w/qemu2.git$
+
+=== scripts/qapi-types.py ===
+
+Used to generate the C types defined by a schema. The following files are
+created:
+
+$(prefix)qapi-types.h - C types corresponding to types defined in
+ the schema you pass in
+$(prefix)qapi-types.c - Cleanup functions for the above C types
+
+The $(prefix) is an optional parameter used as a namespace to keep the
+generated code from one schema/code-generation separated from others so code
+can be generated/used from multiple schemas without clobbering previously
+created code.
+
+Example:
+
+ mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \
+ --output-dir="qapi-generated" --prefix="example-" < example-schema.json
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c
+ /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #include "qapi/qapi-dealloc-visitor.h"
+ #include "example-qapi-types.h"
+ #include "example-qapi-visit.h"
+
+ void qapi_free_UserDefOne(UserDefOne * obj)
+ {
+ QapiDeallocVisitor *md;
+ Visitor *v;
+
+ if (!obj) {
+ return;
+ }
+
+ md = qapi_dealloc_visitor_new();
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_UserDefOne(v, &obj, NULL, NULL);
+ qapi_dealloc_visitor_cleanup(md);
+ }
+
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h
+ /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+ #ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES
+ #define QAPI_GENERATED_EXAMPLE_QAPI_TYPES
+
+ #include "qapi/qapi-types-core.h"
+
+ typedef struct UserDefOne UserDefOne;
+
+ typedef struct UserDefOneList
+ {
+ UserDefOne *value;
+ struct UserDefOneList *next;
+ } UserDefOneList;
+
+ struct UserDefOne
+ {
+ int64_t integer;
+ char * string;
+ };
+
+ void qapi_free_UserDefOne(UserDefOne * obj);
+
+ #endif
+
+
+=== scripts/qapi-visit.py ===
+
+Used to generate the visitor functions used to walk through and convert
+a QObject (as provided by QMP) to a native C data structure and
+vice-versa, as well as the visitor function used to dealloc a complex
+schema-defined C type.
+
+The following files are generated:
+
+$(prefix)qapi-visit.c: visitor function for a particular C type, used
+ to automagically convert QObjects into the
+ corresponding C type and vice-versa, as well
+ as for deallocating memory for an existing C
+ type
+
+$(prefix)qapi-visit.h: declarations for previously mentioned visitor
+ functions
+
+Example:
+
+ mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \
+ --output-dir="qapi-generated" --prefix="example-" < example-schema.json
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c
+ /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #include "example-qapi-visit.h"
+
+ void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp)
+ {
+ visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp);
+ visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp);
+ visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp);
+ visit_end_struct(m, errp);
+ }
+
+ void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp)
+ {
+ GenericList *i;
+
+ visit_start_list(m, name, errp);
+
+ for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) {
+ UserDefOneList *native_i = (UserDefOneList *)i;
+ visit_type_UserDefOne(m, &native_i->value, NULL, errp);
+ }
+
+ visit_end_list(m, errp);
+ }
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h
+ /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT
+ #define QAPI_GENERATED_EXAMPLE_QAPI_VISIT
+
+ #include "qapi/qapi-visit-core.h"
+ #include "example-qapi-types.h"
+
+ void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp);
+ void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp);
+
+ #endif
+ mdroth@illuin:~/w/qemu2.git$
+
+
+=== scripts/qapi-commands.py ===
+
+Used to generate the marshaling/dispatch functions for the commands defined
+in the schema. The following files are generated:
+
+$(prefix)qmp-marshal.c: command marshal/dispatch functions for each
+ QMP command defined in the schema. Functions
+ generated by qapi-visit.py are used to
+ convert QObjects recieved from the wire into
+ function parameters, and uses the same
+ visitor functions to convert native C return
+ values to QObjects from transmission back
+ over the wire.
+
+$(prefix)qmp-commands.h: Function prototypes for the QMP commands
+ specified in the schema.
+
+Example:
+
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c
+ /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #include "qemu-objects.h"
+ #include "qapi/qmp-core.h"
+ #include "qapi/qapi-visit-core.h"
+ #include "qapi/qmp-output-visitor.h"
+ #include "qapi/qmp-input-visitor.h"
+ #include "qapi/qapi-dealloc-visitor.h"
+ #include "example-qapi-types.h"
+ #include "example-qapi-visit.h"
+
+ #include "example-qmp-commands.h"
+ static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp)
+ {
+ QapiDeallocVisitor *md = qapi_dealloc_visitor_new();
+ QmpOutputVisitor *mo = qmp_output_visitor_new();
+ Visitor *v;
+
+ v = qmp_output_get_visitor(mo);
+ visit_type_UserDefOne(v, &ret_in, "unused", errp);
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_UserDefOne(v, &ret_in, "unused", errp);
+ qapi_dealloc_visitor_cleanup(md);
+
+
+ *ret_out = qmp_output_get_qobject(mo);
+ }
+
+ static void qmp_marshal_input_my_command(QmpState *qmp__sess, QDict *args, QObject **ret, Error **errp)
+ {
+ UserDefOne * retval = NULL;
+ QmpInputVisitor *mi;
+ QapiDeallocVisitor *md;
+ Visitor *v;
+ UserDefOne * arg1 = NULL;
+
+ mi = qmp_input_visitor_new(QOBJECT(args));
+ v = qmp_input_get_visitor(mi);
+ visit_type_UserDefOne(v, &arg1, "arg1", errp);
+
+ if (error_is_set(errp)) {
+ goto out;
+ }
+ retval = qmp_my_command(arg1, errp);
+ qmp_marshal_output_my_command(retval, ret, errp);
+
+ out:
+ md = qapi_dealloc_visitor_new();
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_UserDefOne(v, &arg1, "arg1", errp);
+ qapi_dealloc_visitor_cleanup(md);
+ return;
+ }
+
+ static void qmp_init_marshal(void)
+ {
+ qmp_register_command("my-command", qmp_marshal_input_my_command);
+ }
+
+ qapi_init(qmp_init_marshal);
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h
+ /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS
+ #define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS
+
+ #include "example-qapi-types.h"
+ #include "error.h"
+
+ UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp);
+
+ #endif
+ mdroth@illuin:~/w/qemu2.git$
diff --git a/error.h b/error.h
index 0f92a6f570..6361f407fd 100644
--- a/error.h
+++ b/error.h
@@ -12,6 +12,7 @@
#ifndef ERROR_H
#define ERROR_H
+#include "compiler.h"
#include <stdbool.h>
/**
diff --git a/exec-all.h b/exec-all.h
index 69acf3b97d..9b8d62c239 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -323,7 +323,7 @@ static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong add
pd = env1->tlb_table[mmu_idx][page_index].addr_code & ~TARGET_PAGE_MASK;
if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SPARC)
- do_unassigned_access(addr, 0, 1, 0, 4);
+ cpu_unassigned_access(env1, addr, 0, 1, 0, 4);
#else
cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr);
#endif
diff --git a/exec.c b/exec.c
index 827790088b..2160ded401 100644
--- a/exec.c
+++ b/exec.c
@@ -3238,7 +3238,7 @@ static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr)
printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 0, 0, 0, 1);
+ cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 1);
#endif
return 0;
}
@@ -3249,7 +3249,7 @@ static uint32_t unassigned_mem_readw(void *opaque, target_phys_addr_t addr)
printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 0, 0, 0, 2);
+ cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 2);
#endif
return 0;
}
@@ -3260,7 +3260,7 @@ static uint32_t unassigned_mem_readl(void *opaque, target_phys_addr_t addr)
printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 0, 0, 0, 4);
+ cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 4);
#endif
return 0;
}
@@ -3271,7 +3271,7 @@ static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_
printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 1, 0, 0, 1);
+ cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 1);
#endif
}
@@ -3281,7 +3281,7 @@ static void unassigned_mem_writew(void *opaque, target_phys_addr_t addr, uint32_
printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 1, 0, 0, 2);
+ cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 2);
#endif
}
@@ -3291,7 +3291,7 @@ static void unassigned_mem_writel(void *opaque, target_phys_addr_t addr, uint32_
printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 1, 0, 0, 4);
+ cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 4);
#endif
}
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 6ad8806785..c857827618 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -840,7 +840,7 @@ ETEXI
{
.name = "snapshot_blkdev",
- .args_type = "device:B,snapshot_file:s?,format:s?",
+ .args_type = "device:B,snapshot-file:s?,format:s?",
.params = "device [new-image-file] [format]",
.help = "initiates a live snapshot\n\t\t\t"
"of device. If a new image file is specified, the\n\t\t\t"
diff --git a/hw/ds1225y.c b/hw/ds1225y.c
index b1c52321fe..662d7b5166 100644
--- a/hw/ds1225y.c
+++ b/hw/ds1225y.c
@@ -22,31 +22,24 @@
* THE SOFTWARE.
*/
-#include "hw.h"
-#include "mips.h"
-#include "nvram.h"
+#include "sysbus.h"
+#include "trace.h"
-//#define DEBUG_NVRAM
-
-typedef struct ds1225y_t
-{
+typedef struct {
+ DeviceState qdev;
uint32_t chip_size;
+ char *filename;
QEMUFile *file;
uint8_t *contents;
- uint8_t protection;
-} ds1225y_t;
-
+} NvRamState;
static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
{
- ds1225y_t *s = opaque;
+ NvRamState *s = opaque;
uint32_t val;
val = s->contents[addr];
-
-#ifdef DEBUG_NVRAM
- printf("nvram: read 0x%x at " TARGET_FMT_lx "\n", val, addr);
-#endif
+ trace_nvram_read(addr, val);
return val;
}
@@ -70,13 +63,12 @@ static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val)
{
- ds1225y_t *s = opaque;
+ NvRamState *s = opaque;
-#ifdef DEBUG_NVRAM
- printf("nvram: write 0x%x at " TARGET_FMT_lx "\n", val, addr);
-#endif
+ val &= 0xff;
+ trace_nvram_write(addr, s->contents[addr], val);
- s->contents[addr] = val & 0xff;
+ s->contents[addr] = val;
if (s->file) {
qemu_fseek(s->file, addr, SEEK_SET);
qemu_put_byte(s->file, (int)val);
@@ -98,34 +90,6 @@ static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t val)
nvram_writeb(opaque, addr + 3, (val >> 24) & 0xff);
}
-static void nvram_writeb_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
-{
- ds1225y_t *s = opaque;
-
- if (s->protection != 7) {
-#ifdef DEBUG_NVRAM
- printf("nvram: prevent write of 0x%x at " TARGET_FMT_lx "\n", val, addr);
-#endif
- return;
- }
-
- nvram_writeb(opaque, addr, val);
-}
-
-static void nvram_writew_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
-{
- nvram_writeb_protected(opaque, addr, val & 0xff);
- nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff);
-}
-
-static void nvram_writel_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
-{
- nvram_writeb_protected(opaque, addr, val & 0xff);
- nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff);
- nvram_writeb_protected(opaque, addr + 2, (val >> 16) & 0xff);
- nvram_writeb_protected(opaque, addr + 3, (val >> 24) & 0xff);
-}
-
static CPUReadMemoryFunc * const nvram_read[] = {
&nvram_readb,
&nvram_readw,
@@ -138,45 +102,83 @@ static CPUWriteMemoryFunc * const nvram_write[] = {
&nvram_writel,
};
-static CPUWriteMemoryFunc * const nvram_write_protected[] = {
- &nvram_writeb_protected,
- &nvram_writew_protected,
- &nvram_writel_protected,
+static int nvram_post_load(void *opaque, int version_id)
+{
+ NvRamState *s = opaque;
+
+ /* Close file, as filename may has changed in load/store process */
+ if (s->file) {
+ qemu_fclose(s->file);
+ }
+
+ /* Write back nvram contents */
+ s->file = qemu_fopen(s->filename, "wb");
+ if (s->file) {
+ /* Write back contents, as 'wb' mode cleaned the file */
+ qemu_put_buffer(s->file, s->contents, s->chip_size);
+ qemu_fflush(s->file);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_nvram = {
+ .name = "nvram",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = nvram_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_VARRAY_UINT32(contents, NvRamState, chip_size, 0,
+ vmstate_info_uint8, uint8_t),
+ VMSTATE_END_OF_LIST()
+ }
};
-/* Initialisation routine */
-void *ds1225y_init(target_phys_addr_t mem_base, const char *filename)
+typedef struct {
+ SysBusDevice busdev;
+ NvRamState nvram;
+} SysBusNvRamState;
+
+static int nvram_sysbus_initfn(SysBusDevice *dev)
{
- ds1225y_t *s;
- int mem_indexRW, mem_indexRP;
+ NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram;
QEMUFile *file;
+ int s_io;
- s = qemu_mallocz(sizeof(ds1225y_t));
- s->chip_size = 0x2000; /* Fixed for ds1225y chip: 8 KiB */
s->contents = qemu_mallocz(s->chip_size);
- s->protection = 7;
+
+ s_io = cpu_register_io_memory(nvram_read, nvram_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, s->chip_size, s_io);
/* Read current file */
- file = qemu_fopen(filename, "rb");
+ file = qemu_fopen(s->filename, "rb");
if (file) {
/* Read nvram contents */
qemu_get_buffer(file, s->contents, s->chip_size);
qemu_fclose(file);
}
- s->file = qemu_fopen(filename, "wb");
- if (s->file) {
- /* Write back contents, as 'wb' mode cleaned the file */
- qemu_put_buffer(s->file, s->contents, s->chip_size);
- qemu_fflush(s->file);
- }
+ nvram_post_load(s, 0);
+
+ return 0;
+}
+
+static SysBusDeviceInfo nvram_sysbus_info = {
+ .qdev.name = "ds1225y",
+ .qdev.size = sizeof(SysBusNvRamState),
+ .qdev.vmsd = &vmstate_nvram,
+ .init = nvram_sysbus_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000),
+ DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
- /* Read/write memory */
- mem_indexRW = cpu_register_io_memory(nvram_read, nvram_write, s,
- DEVICE_NATIVE_ENDIAN);
- cpu_register_physical_memory(mem_base, s->chip_size, mem_indexRW);
- /* Read/write protected memory */
- mem_indexRP = cpu_register_io_memory(nvram_read, nvram_write_protected, s,
- DEVICE_NATIVE_ENDIAN);
- cpu_register_physical_memory(mem_base + s->chip_size, s->chip_size, mem_indexRP);
- return s;
+static void nvram_register(void)
+{
+ sysbus_register_withprop(&nvram_sysbus_info);
}
+
+device_init(nvram_register)
diff --git a/hw/esp.c b/hw/esp.c
index aa50800a56..9ddd6373c8 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -244,7 +244,7 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7;
- s->current_req = scsi_req_new(s->current_dev, 0, lun);
+ s->current_req = scsi_req_new(s->current_dev, 0, lun, NULL);
datalen = scsi_req_enqueue(s->current_req, buf);
s->ti_size = datalen;
if (datalen != 0) {
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 940b43abfd..69eec1d2fe 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -661,7 +661,7 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
static void lsi_request_cancelled(SCSIRequest *req)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
- lsi_request *p;
+ lsi_request *p = req->hba_private;
if (s->current && req == s->current->req) {
scsi_req_unref(req);
@@ -670,7 +670,6 @@ static void lsi_request_cancelled(SCSIRequest *req)
return;
}
- p = lsi_find_by_tag(s, req->tag);
if (p) {
QTAILQ_REMOVE(&s->queue, p, next);
scsi_req_unref(req);
@@ -680,18 +679,12 @@ static void lsi_request_cancelled(SCSIRequest *req)
/* Record that data is available for a queued command. Returns zero if
the device was reselected, nonzero if the IO is deferred. */
-static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t len)
+static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
{
- lsi_request *p;
-
- p = lsi_find_by_tag(s, tag);
- if (!p) {
- BADF("IO with unknown tag %d\n", tag);
- return 1;
- }
+ lsi_request *p = req->hba_private;
if (p->pending) {
- BADF("Multiple IO pending for tag %d\n", tag);
+ BADF("Multiple IO pending for request %p\n", p);
}
p->pending = len;
/* Reselect if waiting for it, or if reselection triggers an IRQ
@@ -743,9 +736,9 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
- if (s->waiting == 1 || !s->current || req->tag != s->current->tag ||
+ if (s->waiting == 1 || !s->current || req->hba_private != s->current ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
- if (lsi_queue_tag(s, req->tag, len)) {
+ if (lsi_queue_req(s, req, len)) {
return;
}
}
@@ -789,7 +782,8 @@ static void lsi_do_command(LSIState *s)
assert(s->current == NULL);
s->current = qemu_mallocz(sizeof(lsi_request));
s->current->tag = s->select_tag;
- s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun);
+ s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun,
+ s->current);
n = scsi_req_enqueue(s->current->req, buf);
if (n) {
diff --git a/hw/mips.h b/hw/mips.h
index 73aa8f8b0e..cae5f4c804 100644
--- a/hw/mips.h
+++ b/hw/mips.h
@@ -8,10 +8,6 @@ PCIBus *gt64120_register(qemu_irq *pic);
/* bonito.c */
PCIBus *bonito_init(qemu_irq *pic);
-/* ds1225y.c */
-void *ds1225y_init(target_phys_addr_t mem_base, const char *filename);
-void ds1225y_set_protection(void *opaque, int protection);
-
/* g364fb.c */
int g364fb_mm_init(target_phys_addr_t vram_base,
target_phys_addr_t ctrl_base, int it_shift,
diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c
index a1003945fd..f6ab6dc455 100644
--- a/hw/mips_jazz.c
+++ b/hw/mips_jazz.c
@@ -37,6 +37,7 @@
#include "loader.h"
#include "mc146818rtc.h"
#include "blockdev.h"
+#include "sysbus.h"
enum jazz_model_e
{
@@ -115,6 +116,8 @@ void mips_jazz_init (ram_addr_t ram_size,
void* rc4030_opaque;
int s_rtc, s_dma_dummy;
NICInfo *nd;
+ DeviceState *dev;
+ SysBusDevice *sysbus;
ISADevice *pit;
DriveInfo *fds[MAX_FD];
qemu_irq esp_reset, dma_enable;
@@ -266,8 +269,11 @@ void mips_jazz_init (ram_addr_t ram_size,
/* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */
audio_init(i8259, NULL);
- /* NVRAM: Unprotected at 0x9000, Protected at 0xa000, Read only at 0xb000 */
- ds1225y_init(0x80009000, "nvram");
+ /* NVRAM */
+ dev = qdev_create(NULL, "ds1225y");
+ qdev_init_nofail(dev);
+ sysbus = sysbus_from_qdev(dev);
+ sysbus_mmio_map(sysbus, 0, 0x80009000);
/* LED indicator */
jazz_led_init(0x8000f000);
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index ad6a730be0..8b1a412210 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -131,7 +131,8 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
return res;
}
-SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun)
+SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag,
+ uint32_t lun, void *hba_private)
{
SCSIRequest *req;
@@ -141,14 +142,16 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
req->dev = d;
req->tag = tag;
req->lun = lun;
+ req->hba_private = hba_private;
req->status = -1;
trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
return req;
}
-SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun)
+SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
+ void *hba_private)
{
- return d->info->alloc_req(d, tag, lun);
+ return d->info->alloc_req(d, tag, lun, hba_private);
}
uint8_t *scsi_req_get_buf(SCSIRequest *req)
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index a8c7372d3e..05d14ab2fd 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -81,13 +81,13 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf);
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
- uint32_t lun)
+ uint32_t lun, void *hba_private)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *req;
SCSIDiskReq *r;
- req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun);
+ req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun, hba_private);
r = DO_UPCAST(SCSIDiskReq, req, req);
r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
return req;
@@ -398,7 +398,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
"buffer size %zd\n", req->cmd.xfer);
pages = buflen++;
outbuf[buflen++] = 0x00; // list of supported pages (this page)
- outbuf[buflen++] = 0x80; // unit serial number
+ if (s->serial)
+ outbuf[buflen++] = 0x80; // unit serial number
outbuf[buflen++] = 0x83; // device identification
if (s->drive_kind == SCSI_HD) {
outbuf[buflen++] = 0xb0; // block limits
@@ -409,8 +410,14 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
}
case 0x80: /* Device serial number, optional */
{
- int l = strlen(s->serial);
+ int l;
+ if (!s->serial) {
+ DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
+ return -1;
+ }
+
+ l = strlen(s->serial);
if (l > req->cmd.xfer)
l = req->cmd.xfer;
if (l > 20)
@@ -1007,7 +1014,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
command = buf[0];
outbuf = (uint8_t *)r->iov.iov_base;
- DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
+ DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", req->lun, req->tag, buf[0]);
if (scsi_req_parse(&r->req, buf) != 0) {
BADF("Unsupported command length, command %x\n", command);
@@ -1203,7 +1210,9 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind)
if (!s->serial) {
/* try to fall back to value set with legacy -drive serial=... */
dinfo = drive_get_by_blockdev(s->bs);
- s->serial = qemu_strdup(*dinfo->serial ? dinfo->serial : "0");
+ if (*dinfo->serial) {
+ s->serial = qemu_strdup(dinfo->serial);
+ }
}
if (!s->version) {
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 8e59c7ee89..90345a714f 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -96,11 +96,12 @@ static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
return size;
}
-static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
+ void *hba_private)
{
SCSIRequest *req;
- req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun);
+ req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun, hba_private);
return req;
}
diff --git a/hw/scsi.h b/hw/scsi.h
index c1dca35b86..6b15bbc2cd 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -43,6 +43,7 @@ struct SCSIRequest {
} cmd;
BlockDriverAIOCB *aiocb;
bool enqueued;
+ void *hba_private;
QTAILQ_ENTRY(SCSIRequest) next;
};
@@ -67,7 +68,8 @@ struct SCSIDeviceInfo {
DeviceInfo qdev;
scsi_qdev_initfn init;
void (*destroy)(SCSIDevice *s);
- SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun);
+ SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun,
+ void *hba_private);
void (*free_req)(SCSIRequest *req);
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
void (*read_data)(SCSIRequest *req);
@@ -138,8 +140,10 @@ extern const struct SCSISense sense_code_LUN_FAILURE;
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
-SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
-SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun);
+SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag,
+ uint32_t lun, void *hba_private);
+SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
+ void *hba_private);
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf);
void scsi_req_free(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 1c901ef6eb..646b1e3caf 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -121,7 +121,7 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
return NULL;
}
-static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
+static void vscsi_put_req(vscsi_req *req)
{
if (req->sreq != NULL) {
scsi_req_unref(req->sreq);
@@ -130,15 +130,6 @@ static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
req->active = 0;
}
-static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req)
-{
- uint32_t tag = req->tag;
- if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) {
- return NULL;
- }
- return &s->reqs[tag];
-}
-
static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun)
{
/* XXX Figure that one out properly ! This is crackpot */
@@ -454,7 +445,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
if (n) {
req->senselen = n;
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
- vscsi_put_req(s, req);
+ vscsi_put_req(req);
return;
}
@@ -483,7 +474,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
- vscsi_req *req = vscsi_find_req(s, sreq);
+ vscsi_req *req = sreq->hba_private;
uint8_t *buf;
int rc = 0;
@@ -531,7 +522,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
- vscsi_req *req = vscsi_find_req(s, sreq);
+ vscsi_req *req = sreq->hba_private;
int32_t res_in = 0, res_out = 0;
dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n",
@@ -563,15 +554,14 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
}
}
vscsi_send_rsp(s, req, 0, res_in, res_out);
- vscsi_put_req(s, req);
+ vscsi_put_req(req);
}
static void vscsi_request_cancelled(SCSIRequest *sreq)
{
- VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
- vscsi_req *req = vscsi_find_req(s, sreq);
+ vscsi_req *req = sreq->hba_private;
- vscsi_put_req(s, req);
+ vscsi_put_req(req);
}
static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
@@ -659,7 +649,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
}
req->lun = lun;
- req->sreq = scsi_req_new(sdev, req->qtag, lun);
+ req->sreq = scsi_req_new(sdev, req->qtag, lun, req);
n = scsi_req_enqueue(req->sreq, srp->cmd.cdb);
dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
@@ -858,7 +848,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
}
if (done) {
- vscsi_put_req(s, req);
+ vscsi_put_req(req);
}
}
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index d711b5c0be..b812da2a6a 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -531,18 +531,15 @@ static void usb_keyboard_process_keycode(USBHIDState *hs)
case 0xe0:
if (s->modifiers & (1 << 9)) {
s->modifiers ^= 3 << 8;
- usb_hid_changed(hs);
return;
}
case 0xe1 ... 0xe7:
if (keycode & (1 << 7)) {
s->modifiers &= ~(1 << (hid_code & 0x0f));
- usb_hid_changed(hs);
return;
}
case 0xe8 ... 0xef:
s->modifiers |= 1 << (hid_code & 0x0f);
- usb_hid_changed(hs);
return;
}
@@ -769,10 +766,12 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
}
break;
case GET_REPORT:
- if (s->kind == USB_MOUSE || s->kind == USB_TABLET)
+ if (s->kind == USB_MOUSE || s->kind == USB_TABLET) {
ret = usb_pointer_poll(s, data, length);
- else if (s->kind == USB_KEYBOARD)
+ } else if (s->kind == USB_KEYBOARD) {
ret = usb_keyboard_poll(s, data, length);
+ }
+ s->changed = s->n > 0;
break;
case SET_REPORT:
if (s->kind == USB_KEYBOARD)
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 86582cc723..bfea096893 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -216,10 +216,6 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
- if (req->tag != s->tag) {
- fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
- }
-
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
s->scsi_len = len;
s->scsi_buf = scsi_req_get_buf(req);
@@ -241,9 +237,6 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
- if (req->tag != s->tag) {
- fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
- }
DPRINTF("Command complete %d\n", status);
s->residue = s->data_len;
s->result = status != 0;
@@ -387,7 +380,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0;
s->scsi_len = 0;
- s->req = scsi_req_new(s->scsi_dev, s->tag, 0);
+ s->req = scsi_req_new(s->scsi_dev, s->tag, 0, NULL);
scsi_req_enqueue(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer
directions match. */
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index 2ef4c5b747..da74c57c62 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -730,6 +730,9 @@ out:
td->ctrl |= TD_CTRL_STALL;
td->ctrl &= ~TD_CTRL_ACTIVE;
s->status |= UHCI_STS_USBERR;
+ if (td->ctrl & TD_CTRL_IOC) {
+ *int_mask |= 0x01;
+ }
uhci_update_irq(s);
return 1;
@@ -737,6 +740,9 @@ out:
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
td->ctrl &= ~TD_CTRL_ACTIVE;
s->status |= UHCI_STS_USBERR;
+ if (td->ctrl & TD_CTRL_IOC) {
+ *int_mask |= 0x01;
+ }
uhci_update_irq(s);
/* frame interrupted */
return -1;
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 6997e02dcf..a32cc019b0 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -657,7 +657,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_
/* copy in packet. ugh */
len = iov_from_buf(sg, elem.in_num,
- buf + offset, size - offset);
+ buf + offset, 0, size - offset);
total += len;
offset += len;
/* If buffers can't be merged, at this point we
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 6d73386441..bdc760c36e 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -104,7 +104,7 @@ static size_t write_to_port(VirtIOSerialPort *port,
}
len = iov_from_buf(elem.in_sg, elem.in_num,
- buf + offset, size - offset);
+ buf + offset, 0, size - offset);
offset += len;
virtqueue_push(vq, &elem, len);
diff --git a/iov.c b/iov.c
index 588cd04288..1e027914d4 100644
--- a/iov.c
+++ b/iov.c
@@ -14,56 +14,61 @@
#include "iov.h"
-size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt,
- const void *buf, size_t size)
+size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt,
+ const void *buf, size_t iov_off, size_t size)
{
- size_t offset;
+ size_t iovec_off, buf_off;
unsigned int i;
- offset = 0;
- for (i = 0; offset < size && i < iovcnt; i++) {
- size_t len;
+ iovec_off = 0;
+ buf_off = 0;
+ for (i = 0; i < iov_cnt && size; i++) {
+ if (iov_off < (iovec_off + iov[i].iov_len)) {
+ size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off, size);
- len = MIN(iov[i].iov_len, size - offset);
+ memcpy(iov[i].iov_base + (iov_off - iovec_off), buf + buf_off, len);
- memcpy(iov[i].iov_base, buf + offset, len);
- offset += len;
+ buf_off += len;
+ iov_off += len;
+ size -= len;
+ }
+ iovec_off += iov[i].iov_len;
}
- return offset;
+ return buf_off;
}
-size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt,
- void *buf, size_t offset, size_t size)
+size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
+ void *buf, size_t iov_off, size_t size)
{
uint8_t *ptr;
- size_t iov_off, buf_off;
+ size_t iovec_off, buf_off;
unsigned int i;
ptr = buf;
- iov_off = 0;
+ iovec_off = 0;
buf_off = 0;
- for (i = 0; i < iovcnt && size; i++) {
- if (offset < (iov_off + iov[i].iov_len)) {
- size_t len = MIN((iov_off + iov[i].iov_len) - offset , size);
+ for (i = 0; i < iov_cnt && size; i++) {
+ if (iov_off < (iovec_off + iov[i].iov_len)) {
+ size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
- memcpy(ptr + buf_off, iov[i].iov_base + (offset - iov_off), len);
+ memcpy(ptr + buf_off, iov[i].iov_base + (iov_off - iovec_off), len);
buf_off += len;
- offset += len;
+ iov_off += len;
size -= len;
}
- iov_off += iov[i].iov_len;
+ iovec_off += iov[i].iov_len;
}
return buf_off;
}
-size_t iov_size(const struct iovec *iov, const unsigned int iovcnt)
+size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
{
size_t len;
unsigned int i;
len = 0;
- for (i = 0; i < iovcnt; i++) {
+ for (i = 0; i < iov_cnt; i++) {
len += iov[i].iov_len;
}
return len;
diff --git a/iov.h b/iov.h
index 60a85470bd..110f67ab53 100644
--- a/iov.h
+++ b/iov.h
@@ -12,8 +12,8 @@
#include "qemu-common.h"
-size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt,
- const void *buf, size_t size);
-size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt,
- void *buf, size_t offset, size_t size);
-size_t iov_size(const struct iovec *iov, const unsigned int iovcnt);
+size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt,
+ const void *buf, size_t iov_off, size_t size);
+size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
+ void *buf, size_t iov_off, size_t size);
+size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt);
diff --git a/module.h b/module.h
index 9263f1c7e2..ef667304c4 100644
--- a/module.h
+++ b/module.h
@@ -24,12 +24,14 @@ typedef enum {
MODULE_INIT_BLOCK,
MODULE_INIT_DEVICE,
MODULE_INIT_MACHINE,
+ MODULE_INIT_QAPI,
MODULE_INIT_MAX
} module_init_type;
#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
#define device_init(function) module_init(function, MODULE_INIT_DEVICE)
#define machine_init(function) module_init(function, MODULE_INIT_MACHINE)
+#define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
void register_module_init(void (*fn)(void), module_init_type type);
diff --git a/pc-bios/README b/pc-bios/README
index 40568f82aa..f74b2463f1 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -10,10 +10,9 @@
- OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable
firmware implementation. The goal is to implement a 100% IEEE
1275-1994 (referred to as Open Firmware) compliant firmware.
- The included image for PowerPC (for 32 and 64 bit PPC CPUs),
-
- PowerPC is built from OpenBIOS SVN revision 1044
- Sparc32 and Sparc64 are built from OpenBIOS SVN revision 1018.
+ The included image for PowerPC (for 32 and 64 bit PPC CPUs)
+ is built from OpenBIOS SVN revision 1044 and Sparc32 and Sparc64
+ images are built from OpenBIOS SVN revision 1045.
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index b2dc5c5e7b..ea9cc3214e 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index 70a223d573..7e746b4522 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/poison.h b/poison.h
index 2b182328a1..5354e7779c 100644
--- a/poison.h
+++ b/poison.h
@@ -37,6 +37,15 @@
#pragma GCC poison CPUState
#pragma GCC poison env
+#pragma GCC poison lduw_phys
+#pragma GCC poison ldl_phys
+#pragma GCC poison ldq_phys
+#pragma GCC poison stl_phys_notdirty
+#pragma GCC poison stq_phys_notdirty
+#pragma GCC poison stw_phys
+#pragma GCC poison stl_phys
+#pragma GCC poison stq_phys
+
#pragma GCC poison CPU_INTERRUPT_HARD
#pragma GCC poison CPU_INTERRUPT_EXITTB
#pragma GCC poison CPU_INTERRUPT_HALT
diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
new file mode 100644
index 0000000000..fde5971e87
--- /dev/null
+++ b/qapi-schema-guest.json
@@ -0,0 +1,217 @@
+# *-*- Mode: Python -*-*
+
+##
+# @guest-sync:
+#
+# Echo back a unique integer value
+#
+# This is used by clients talking to the guest agent over the
+# wire to ensure the stream is in sync and doesn't contain stale
+# data from previous client. All guest agent responses should be
+# ignored until the provided unique integer value is returned,
+# and it is up to the client to handle stale whole or
+# partially-delivered JSON text in such a way that this response
+# can be obtained.
+#
+# Such clients should also preceed this command
+# with a 0xFF byte to make such the guest agent flushes any
+# partially read JSON data from a previous session.
+#
+# @id: randomly generated 64-bit integer
+#
+# Returns: The unique integer id passed in by the client
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-sync'
+ 'data': { 'id': 'int' },
+ 'returns': 'int' }
+
+##
+# @guest-ping:
+#
+# Ping the guest agent, a non-error return implies success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-ping' }
+
+##
+# @guest-info:
+#
+# Get some information about the guest agent.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestAgentInfo', 'data': {'version': 'str'} }
+{ 'command': 'guest-info',
+ 'returns': 'GuestAgentInfo' }
+
+##
+# @guest-shutdown:
+#
+# Initiate guest-activated shutdown. Note: this is an asynchronous
+# shutdown request, with no guaruntee of successful shutdown. Errors
+# will be logged to guest's syslog.
+#
+# @mode: #optional "halt", "powerdown" (default), or "reboot"
+#
+# Returns: Nothing on success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' } }
+
+##
+# @guest-file-open:
+#
+# Open a file in the guest and retrieve a file handle for it
+#
+# @filepath: Full path to the file in the guest to open.
+#
+# @mode: #optional open mode, as per fopen(), "r" is the default.
+#
+# Returns: Guest file handle on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-open',
+ 'data': { 'path': 'str', '*mode': 'str' },
+ 'returns': 'int' }
+
+##
+# @guest-file-close:
+#
+# Close an open file in the guest
+#
+# @handle: filehandle returned by guest-file-open
+#
+# Returns: Nothing on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-close',
+ 'data': { 'handle': 'int' } }
+
+##
+# @guest-file-read:
+#
+# Read from an open file in the guest. Data will be base64-encoded
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @count: #optional maximum number of bytes to read (default is 4KB)
+#
+# Returns: GuestFileRead on success. Note: count is number of bytes read
+# *before* base64 encoding bytes read.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileRead',
+ 'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
+
+{ 'command': 'guest-file-read',
+ 'data': { 'handle': 'int', '*count': 'int' },
+ 'returns': 'GuestFileRead' }
+
+##
+# @guest-file-write:
+#
+# Write to an open file in the guest.
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @buf-b64: base64-encoded string representing data to be written
+#
+# @count: #optional bytes to write (actual bytes, after base64-decode),
+# default is all content in buf-b64 buffer after base64 decoding
+#
+# Returns: GuestFileWrite on success. Note: count is the number of bytes
+# base64-decoded bytes written
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileWrite',
+ 'data': { 'count': 'int', 'eof': 'bool' } }
+{ 'command': 'guest-file-write',
+ 'data': { 'handle': 'int', 'buf-b64': 'str', '*count': 'int' },
+ 'returns': 'GuestFileWrite' }
+
+##
+# @guest-file-seek:
+#
+# Seek to a position in the file, as with fseek(), and return the
+# current file position afterward. Also encapsulates ftell()'s
+# functionality, just Set offset=0, whence=SEEK_CUR.
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @offset: bytes to skip over in the file stream
+#
+# @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek()
+#
+# Returns: GuestFileSeek on success.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileSeek',
+ 'data': { 'position': 'int', 'eof': 'bool' } }
+
+{ 'command': 'guest-file-seek',
+ 'data': { 'handle': 'int', 'offset': 'int', 'whence': 'int' },
+ 'returns': 'GuestFileSeek' }
+
+##
+# @guest-file-flush:
+#
+# Write file changes bufferred in userspace to disk/kernel buffers
+#
+# @handle: filehandle returned by guest-file-open
+#
+# Returns: Nothing on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-flush',
+ 'data': { 'handle': 'int' } }
+
+##
+# @guest-fsfreeze-status:
+#
+# Get guest fsfreeze state. error state indicates failure to thaw 1 or more
+# previously frozen filesystems, or failure to open a previously cached
+# filesytem (filesystem unmounted/directory changes, etc).
+#
+# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below)
+#
+# Since: 0.15.0
+##
+{ 'enum': 'GuestFsfreezeStatus',
+ 'data': [ 'thawed', 'frozen', 'error' ] }
+{ 'command': 'guest-fsfreeze-status',
+ 'returns': 'GuestFsfreezeStatus' }
+
+##
+# @guest-fsfreeze-freeze:
+#
+# Sync and freeze all non-network guest filesystems
+#
+# Returns: Number of file systems frozen on success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-fsfreeze-freeze',
+ 'returns': 'int' }
+
+##
+# @guest-fsfreeze-thaw:
+#
+# Unfreeze frozen guest fileystems
+#
+# Returns: Number of file systems thawed
+# If error, -1 (unknown error) or -errno
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-fsfreeze-thaw',
+ 'returns': 'int' }
diff --git a/qapi-schema-test.json b/qapi-schema-test.json
new file mode 100644
index 0000000000..3acedad7ee
--- /dev/null
+++ b/qapi-schema-test.json
@@ -0,0 +1,22 @@
+# *-*- Mode: Python -*-*
+
+# for testing enums
+{ 'enum': 'EnumOne',
+ 'data': [ 'value1', 'value2', 'value3' ] }
+{ 'type': 'NestedEnumsOne',
+ 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } }
+
+# for testing nested structs
+{ 'type': 'UserDefOne',
+ 'data': { 'integer': 'int', 'string': 'str' } }
+
+{ 'type': 'UserDefTwo',
+ 'data': { 'string': 'str',
+ 'dict': { 'string': 'str',
+ 'dict': { 'userdef': 'UserDefOne', 'string': 'str' },
+ '*dict2': { 'userdef': 'UserDefOne', 'string': 'str' } } } }
+
+# testing commands
+{ 'command': 'user_def_cmd', 'data': {} }
+{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
+{ 'command': 'user_def_cmd2', 'data': {'ud1a': 'UserDefOne', 'ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' }
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
new file mode 100644
index 0000000000..8cde4dd0d4
--- /dev/null
+++ b/qapi/qapi-dealloc-visitor.c
@@ -0,0 +1,147 @@
+/*
+ * Dealloc Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi-dealloc-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-objects.h"
+
+typedef struct StackEntry
+{
+ void *value;
+ QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QapiDeallocVisitor
+{
+ Visitor visitor;
+ QTAILQ_HEAD(, StackEntry) stack;
+};
+
+static QapiDeallocVisitor *to_qov(Visitor *v)
+{
+ return container_of(v, QapiDeallocVisitor, visitor);
+}
+
+static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value)
+{
+ StackEntry *e = qemu_mallocz(sizeof(*e));
+
+ e->value = value;
+ QTAILQ_INSERT_HEAD(&qov->stack, e, node);
+}
+
+static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
+{
+ StackEntry *e = QTAILQ_FIRST(&qov->stack);
+ QObject *value;
+ QTAILQ_REMOVE(&qov->stack, e, node);
+ value = e->value;
+ qemu_free(e);
+ return value;
+}
+
+static void qapi_dealloc_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t unused,
+ Error **errp)
+{
+ QapiDeallocVisitor *qov = to_qov(v);
+ qapi_dealloc_push(qov, obj);
+}
+
+static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
+{
+ QapiDeallocVisitor *qov = to_qov(v);
+ void **obj = qapi_dealloc_pop(qov);
+ if (obj) {
+ qemu_free(*obj);
+ }
+}
+
+static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp)
+{
+}
+
+static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ GenericList *retval = *list;
+ qemu_free(retval->value);
+ *list = retval->next;
+ return retval;
+}
+
+static void qapi_dealloc_end_list(Visitor *v, Error **errp)
+{
+}
+
+static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ if (obj) {
+ qemu_free(*obj);
+ }
+}
+
+static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+}
+
+static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+}
+
+static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
+ Error **errp)
+{
+}
+
+static void qapi_dealloc_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name,
+ Error **errp)
+{
+}
+
+Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
+{
+ return &v->visitor;
+}
+
+void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v)
+{
+ qemu_free(v);
+}
+
+QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
+{
+ QapiDeallocVisitor *v;
+
+ v = qemu_mallocz(sizeof(*v));
+
+ v->visitor.start_struct = qapi_dealloc_start_struct;
+ v->visitor.end_struct = qapi_dealloc_end_struct;
+ v->visitor.start_list = qapi_dealloc_start_list;
+ v->visitor.next_list = qapi_dealloc_next_list;
+ v->visitor.end_list = qapi_dealloc_end_list;
+ v->visitor.type_enum = qapi_dealloc_type_enum;
+ v->visitor.type_int = qapi_dealloc_type_int;
+ v->visitor.type_bool = qapi_dealloc_type_bool;
+ v->visitor.type_str = qapi_dealloc_type_str;
+ v->visitor.type_number = qapi_dealloc_type_number;
+
+ QTAILQ_INIT(&v->stack);
+
+ return v;
+}
diff --git a/qapi/qapi-dealloc-visitor.h b/qapi/qapi-dealloc-visitor.h
new file mode 100644
index 0000000000..5842bc79bd
--- /dev/null
+++ b/qapi/qapi-dealloc-visitor.h
@@ -0,0 +1,26 @@
+/*
+ * Dealloc Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QAPI_DEALLOC_VISITOR_H
+#define QAPI_DEALLOC_VISITOR_H
+
+#include "qapi-visit-core.h"
+
+typedef struct QapiDeallocVisitor QapiDeallocVisitor;
+
+QapiDeallocVisitor *qapi_dealloc_visitor_new(void);
+void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *d);
+
+Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v);
+
+#endif
diff --git a/qapi/qapi-types-core.h b/qapi/qapi-types-core.h
new file mode 100644
index 0000000000..a79bc2b3c3
--- /dev/null
+++ b/qapi/qapi-types-core.h
@@ -0,0 +1,20 @@
+/*
+ * Core Definitions for QAPI-generated Types
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QAPI_TYPES_CORE_H
+#define QAPI_TYPES_CORE_H
+
+#include "qemu-common.h"
+#include "error.h"
+
+#endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
new file mode 100644
index 0000000000..ddef3eda12
--- /dev/null
+++ b/qapi/qapi-visit-core.c
@@ -0,0 +1,118 @@
+/*
+ * Core Definitions for QAPI Visitor Classes
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qapi-visit-core.h"
+
+void visit_start_handle(Visitor *v, void **obj, const char *kind,
+ const char *name, Error **errp)
+{
+ if (!error_is_set(errp) && v->start_handle) {
+ v->start_handle(v, obj, kind, name, errp);
+ }
+}
+
+void visit_end_handle(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp) && v->end_handle) {
+ v->end_handle(v, errp);
+ }
+}
+
+void visit_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->start_struct(v, obj, kind, name, size, errp);
+ }
+}
+
+void visit_end_struct(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->end_struct(v, errp);
+ }
+}
+
+void visit_start_list(Visitor *v, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->start_list(v, name, errp);
+ }
+}
+
+GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ return v->next_list(v, list, errp);
+ }
+
+ return 0;
+}
+
+void visit_end_list(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->end_list(v, errp);
+ }
+}
+
+void visit_start_optional(Visitor *v, bool *present, const char *name,
+ Error **errp)
+{
+ if (!error_is_set(errp) && v->start_optional) {
+ v->start_optional(v, present, name, errp);
+ }
+}
+
+void visit_end_optional(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp) && v->end_optional) {
+ v->end_optional(v, errp);
+ }
+}
+
+void visit_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_enum(v, obj, strings, kind, name, errp);
+ }
+}
+
+void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_int(v, obj, name, errp);
+ }
+}
+
+void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_bool(v, obj, name, errp);
+ }
+}
+
+void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_str(v, obj, name, errp);
+ }
+}
+
+void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_number(v, obj, name, errp);
+ }
+}
diff --git a/qapi/qapi-visit-core.h b/qapi/qapi-visit-core.h
new file mode 100644
index 0000000000..e850746b75
--- /dev/null
+++ b/qapi/qapi-visit-core.h
@@ -0,0 +1,76 @@
+/*
+ * Core Definitions for QAPI Visitor Classes
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#ifndef QAPI_VISITOR_CORE_H
+#define QAPI_VISITOR_CORE_H
+
+#include "qapi/qapi-types-core.h"
+#include <stdlib.h>
+
+typedef struct GenericList
+{
+ void *value;
+ struct GenericList *next;
+} GenericList;
+
+typedef struct Visitor Visitor;
+
+struct Visitor
+{
+ /* Must be set */
+ void (*start_struct)(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp);
+ void (*end_struct)(Visitor *v, Error **errp);
+
+ void (*start_list)(Visitor *v, const char *name, Error **errp);
+ GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
+ void (*end_list)(Visitor *v, Error **errp);
+
+ void (*type_enum)(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name, Error **errp);
+
+ void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
+ void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
+ void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
+ void (*type_number)(Visitor *v, double *obj, const char *name,
+ Error **errp);
+
+ /* May be NULL */
+ void (*start_optional)(Visitor *v, bool *present, const char *name,
+ Error **errp);
+ void (*end_optional)(Visitor *v, Error **errp);
+
+ void (*start_handle)(Visitor *v, void **obj, const char *kind,
+ const char *name, Error **errp);
+ void (*end_handle)(Visitor *v, Error **errp);
+};
+
+void visit_start_handle(Visitor *v, void **obj, const char *kind,
+ const char *name, Error **errp);
+void visit_end_handle(Visitor *v, Error **errp);
+void visit_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp);
+void visit_end_struct(Visitor *v, Error **errp);
+void visit_start_list(Visitor *v, const char *name, Error **errp);
+GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
+void visit_end_list(Visitor *v, Error **errp);
+void visit_start_optional(Visitor *v, bool *present, const char *name,
+ Error **errp);
+void visit_end_optional(Visitor *v, Error **errp);
+void visit_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name, Error **errp);
+void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp);
+void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
+void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
+void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
+
+#endif
diff --git a/qapi/qmp-core.h b/qapi/qmp-core.h
new file mode 100644
index 0000000000..f1c26e4b2e
--- /dev/null
+++ b/qapi/qmp-core.h
@@ -0,0 +1,41 @@
+/*
+ * Core Definitions for QAPI/QMP Dispatch
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QMP_CORE_H
+#define QMP_CORE_H
+
+#include "qobject.h"
+#include "qdict.h"
+#include "error.h"
+
+typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);
+
+typedef enum QmpCommandType
+{
+ QCT_NORMAL,
+} QmpCommandType;
+
+typedef struct QmpCommand
+{
+ const char *name;
+ QmpCommandType type;
+ QmpCommandFunc *fn;
+ QTAILQ_ENTRY(QmpCommand) node;
+} QmpCommand;
+
+void qmp_register_command(const char *name, QmpCommandFunc *fn);
+QmpCommand *qmp_find_command(const char *name);
+QObject *qmp_dispatch(QObject *request);
+
+#endif
+
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
new file mode 100644
index 0000000000..558469325c
--- /dev/null
+++ b/qapi/qmp-dispatch.c
@@ -0,0 +1,124 @@
+/*
+ * Core Definitions for QAPI/QMP Dispatch
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-objects.h"
+#include "qapi/qmp-core.h"
+#include "json-parser.h"
+#include "error.h"
+#include "error_int.h"
+#include "qerror.h"
+
+static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
+{
+ const QDictEntry *ent;
+ const char *arg_name;
+ const QObject *arg_obj;
+ bool has_exec_key = false;
+ QDict *dict = NULL;
+
+ if (qobject_type(request) != QTYPE_QDICT) {
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT,
+ "request is not a dictionary");
+ return NULL;
+ }
+
+ dict = qobject_to_qdict(request);
+
+ for (ent = qdict_first(dict); ent;
+ ent = qdict_next(dict, ent)) {
+ arg_name = qdict_entry_key(ent);
+ arg_obj = qdict_entry_value(ent);
+
+ if (!strcmp(arg_name, "execute")) {
+ if (qobject_type(arg_obj) != QTYPE_QSTRING) {
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
+ "string");
+ return NULL;
+ }
+ has_exec_key = true;
+ } else if (strcmp(arg_name, "arguments")) {
+ error_set(errp, QERR_QMP_EXTRA_MEMBER, arg_name);
+ return NULL;
+ }
+ }
+
+ if (!has_exec_key) {
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute");
+ return NULL;
+ }
+
+ return dict;
+}
+
+static QObject *do_qmp_dispatch(QObject *request, Error **errp)
+{
+ const char *command;
+ QDict *args, *dict;
+ QmpCommand *cmd;
+ QObject *ret = NULL;
+
+
+ dict = qmp_dispatch_check_obj(request, errp);
+ if (!dict || error_is_set(errp)) {
+ return NULL;
+ }
+
+ command = qdict_get_str(dict, "execute");
+ cmd = qmp_find_command(command);
+ if (cmd == NULL) {
+ error_set(errp, QERR_COMMAND_NOT_FOUND, command);
+ return NULL;
+ }
+
+ if (!qdict_haskey(dict, "arguments")) {
+ args = qdict_new();
+ } else {
+ args = qdict_get_qdict(dict, "arguments");
+ QINCREF(args);
+ }
+
+ switch (cmd->type) {
+ case QCT_NORMAL:
+ cmd->fn(args, &ret, errp);
+ if (!error_is_set(errp) && ret == NULL) {
+ ret = QOBJECT(qdict_new());
+ }
+ break;
+ }
+
+ QDECREF(args);
+
+ return ret;
+}
+
+QObject *qmp_dispatch(QObject *request)
+{
+ Error *err = NULL;
+ QObject *ret;
+ QDict *rsp;
+
+ ret = do_qmp_dispatch(request, &err);
+
+ rsp = qdict_new();
+ if (err) {
+ qdict_put_obj(rsp, "error", error_get_qobject(err));
+ error_free(err);
+ } else if (ret) {
+ qdict_put_obj(rsp, "return", ret);
+ } else {
+ QDECREF(rsp);
+ return NULL;
+ }
+
+ return QOBJECT(rsp);
+}
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
new file mode 100644
index 0000000000..6a1adc9fce
--- /dev/null
+++ b/qapi/qmp-input-visitor.c
@@ -0,0 +1,301 @@
+/*
+ * Input Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qmp-input-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "qerror.h"
+
+#define QIV_STACK_SIZE 1024
+
+typedef struct StackObject
+{
+ const QObject *obj;
+ const QListEntry *entry;
+} StackObject;
+
+struct QmpInputVisitor
+{
+ Visitor visitor;
+ QObject *obj;
+ StackObject stack[QIV_STACK_SIZE];
+ int nb_stack;
+};
+
+static QmpInputVisitor *to_qiv(Visitor *v)
+{
+ return container_of(v, QmpInputVisitor, visitor);
+}
+
+static const QObject *qmp_input_get_object(QmpInputVisitor *qiv,
+ const char *name)
+{
+ const QObject *qobj;
+
+ if (qiv->nb_stack == 0) {
+ qobj = qiv->obj;
+ } else {
+ qobj = qiv->stack[qiv->nb_stack - 1].obj;
+ }
+
+ if (name && qobject_type(qobj) == QTYPE_QDICT) {
+ return qdict_get(qobject_to_qdict(qobj), name);
+ } else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) {
+ return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
+ }
+
+ return qobj;
+}
+
+static void qmp_input_push(QmpInputVisitor *qiv, const QObject *obj, Error **errp)
+{
+ qiv->stack[qiv->nb_stack].obj = obj;
+ if (qobject_type(obj) == QTYPE_QLIST) {
+ qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj));
+ }
+ qiv->nb_stack++;
+
+ if (qiv->nb_stack >= QIV_STACK_SIZE) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return;
+ }
+}
+
+static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
+{
+ qiv->nb_stack--;
+ if (qiv->nb_stack < 0) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return;
+ }
+}
+
+static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "QDict");
+ return;
+ }
+
+ qmp_input_push(qiv, qobj, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (obj) {
+ *obj = qemu_mallocz(size);
+ }
+}
+
+static void qmp_input_end_struct(Visitor *v, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+
+ qmp_input_pop(qiv, errp);
+}
+
+static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "list");
+ return;
+ }
+
+ qmp_input_push(qiv, qobj, errp);
+}
+
+static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ GenericList *entry;
+ StackObject *so = &qiv->stack[qiv->nb_stack - 1];
+
+ if (so->entry == NULL) {
+ return NULL;
+ }
+
+ entry = qemu_mallocz(sizeof(*entry));
+ if (*list) {
+ so->entry = qlist_next(so->entry);
+ if (so->entry == NULL) {
+ qemu_free(entry);
+ return NULL;
+ }
+ (*list)->next = entry;
+ }
+ *list = entry;
+
+
+ return entry;
+}
+
+static void qmp_input_end_list(Visitor *v, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+
+ qmp_input_pop(qiv, errp);
+}
+
+static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QINT) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "integer");
+ return;
+ }
+
+ *obj = qint_get_int(qobject_to_qint(qobj));
+}
+
+static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "boolean");
+ return;
+ }
+
+ *obj = qbool_get_int(qobject_to_qbool(qobj));
+}
+
+static void qmp_input_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "string");
+ return;
+ }
+
+ *obj = qemu_strdup(qstring_get_str(qobject_to_qstring(qobj)));
+}
+
+static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QFLOAT) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "double");
+ return;
+ }
+
+ *obj = qfloat_get_double(qobject_to_qfloat(qobj));
+}
+
+static void qmp_input_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name,
+ Error **errp)
+{
+ int64_t value = 0;
+ char *enum_str;
+
+ assert(strings);
+
+ qmp_input_type_str(v, &enum_str, name, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ while (strings[value] != NULL) {
+ if (strcmp(strings[value], enum_str) == 0) {
+ break;
+ }
+ value++;
+ }
+
+ if (strings[value] == NULL) {
+ error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null");
+ return;
+ }
+
+ *obj = value;
+}
+
+static void qmp_input_start_optional(Visitor *v, bool *present,
+ const char *name, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj) {
+ *present = false;
+ return;
+ }
+
+ *present = true;
+}
+
+static void qmp_input_end_optional(Visitor *v, Error **errp)
+{
+}
+
+Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
+{
+ return &v->visitor;
+}
+
+void qmp_input_visitor_cleanup(QmpInputVisitor *v)
+{
+ qobject_decref(v->obj);
+ qemu_free(v);
+}
+
+QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
+{
+ QmpInputVisitor *v;
+
+ v = qemu_mallocz(sizeof(*v));
+
+ v->visitor.start_struct = qmp_input_start_struct;
+ v->visitor.end_struct = qmp_input_end_struct;
+ v->visitor.start_list = qmp_input_start_list;
+ v->visitor.next_list = qmp_input_next_list;
+ v->visitor.end_list = qmp_input_end_list;
+ v->visitor.type_enum = qmp_input_type_enum;
+ v->visitor.type_int = qmp_input_type_int;
+ v->visitor.type_bool = qmp_input_type_bool;
+ v->visitor.type_str = qmp_input_type_str;
+ v->visitor.type_number = qmp_input_type_number;
+ v->visitor.start_optional = qmp_input_start_optional;
+ v->visitor.end_optional = qmp_input_end_optional;
+
+ v->obj = obj;
+ qobject_incref(v->obj);
+
+ return v;
+}
diff --git a/qapi/qmp-input-visitor.h b/qapi/qmp-input-visitor.h
new file mode 100644
index 0000000000..3f798f0335
--- /dev/null
+++ b/qapi/qmp-input-visitor.h
@@ -0,0 +1,27 @@
+/*
+ * Input Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QMP_INPUT_VISITOR_H
+#define QMP_INPUT_VISITOR_H
+
+#include "qapi-visit-core.h"
+#include "qobject.h"
+
+typedef struct QmpInputVisitor QmpInputVisitor;
+
+QmpInputVisitor *qmp_input_visitor_new(QObject *obj);
+void qmp_input_visitor_cleanup(QmpInputVisitor *v);
+
+Visitor *qmp_input_get_visitor(QmpInputVisitor *v);
+
+#endif
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
new file mode 100644
index 0000000000..c398cac4f0
--- /dev/null
+++ b/qapi/qmp-output-visitor.c
@@ -0,0 +1,239 @@
+/*
+ * Core Definitions for QAPI/QMP Command Registry
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qmp-output-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "qerror.h"
+
+typedef struct QStackEntry
+{
+ QObject *value;
+ QTAILQ_ENTRY(QStackEntry) node;
+} QStackEntry;
+
+typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
+
+struct QmpOutputVisitor
+{
+ Visitor visitor;
+ QStack stack;
+};
+
+#define qmp_output_add(qov, name, value) \
+ qmp_output_add_obj(qov, name, QOBJECT(value))
+#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value))
+
+static QmpOutputVisitor *to_qov(Visitor *v)
+{
+ return container_of(v, QmpOutputVisitor, visitor);
+}
+
+static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
+{
+ QStackEntry *e = qemu_mallocz(sizeof(*e));
+
+ e->value = value;
+ QTAILQ_INSERT_HEAD(&qov->stack, e, node);
+}
+
+static QObject *qmp_output_pop(QmpOutputVisitor *qov)
+{
+ QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+ QObject *value;
+ QTAILQ_REMOVE(&qov->stack, e, node);
+ value = e->value;
+ qemu_free(e);
+ return value;
+}
+
+static QObject *qmp_output_first(QmpOutputVisitor *qov)
+{
+ QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
+ return e->value;
+}
+
+static QObject *qmp_output_last(QmpOutputVisitor *qov)
+{
+ QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+ return e->value;
+}
+
+static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
+ QObject *value)
+{
+ QObject *cur;
+
+ if (QTAILQ_EMPTY(&qov->stack)) {
+ qmp_output_push_obj(qov, value);
+ return;
+ }
+
+ cur = qmp_output_last(qov);
+
+ switch (qobject_type(cur)) {
+ case QTYPE_QDICT:
+ qdict_put_obj(qobject_to_qdict(cur), name, value);
+ break;
+ case QTYPE_QLIST:
+ qlist_append_obj(qobject_to_qlist(cur), value);
+ break;
+ default:
+ qobject_decref(qmp_output_pop(qov));
+ qmp_output_push_obj(qov, value);
+ break;
+ }
+}
+
+static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t unused,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ QDict *dict = qdict_new();
+
+ qmp_output_add(qov, name, dict);
+ qmp_output_push(qov, dict);
+}
+
+static void qmp_output_end_struct(Visitor *v, Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_pop(qov);
+}
+
+static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ QList *list = qlist_new();
+
+ qmp_output_add(qov, name, list);
+ qmp_output_push(qov, list);
+}
+
+static GenericList *qmp_output_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ GenericList *retval = *list;
+ *list = retval->next;
+ return retval;
+}
+
+static void qmp_output_end_list(Visitor *v, Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_pop(qov);
+}
+
+static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_add(qov, name, qint_from_int(*obj));
+}
+
+static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_add(qov, name, qbool_from_int(*obj));
+}
+
+static void qmp_output_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ if (*obj) {
+ qmp_output_add(qov, name, qstring_from_str(*obj));
+ } else {
+ qmp_output_add(qov, name, qstring_from_str(""));
+ }
+}
+
+static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_add(qov, name, qfloat_from_double(*obj));
+}
+
+static void qmp_output_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name,
+ Error **errp)
+{
+ int i = 0;
+ int value = *obj;
+ char *enum_str;
+
+ assert(strings);
+ while (strings[i++] != NULL);
+ if (value >= i - 1) {
+ error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null");
+ return;
+ }
+
+ enum_str = (char *)strings[value];
+ qmp_output_type_str(v, &enum_str, name, errp);
+}
+
+QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
+{
+ QObject *obj = qmp_output_first(qov);
+ if (obj) {
+ qobject_incref(obj);
+ }
+ return obj;
+}
+
+Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
+{
+ return &v->visitor;
+}
+
+void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
+{
+ QStackEntry *e, *tmp;
+
+ QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
+ QTAILQ_REMOVE(&v->stack, e, node);
+ if (e->value) {
+ qobject_decref(e->value);
+ }
+ qemu_free(e);
+ }
+
+ qemu_free(v);
+}
+
+QmpOutputVisitor *qmp_output_visitor_new(void)
+{
+ QmpOutputVisitor *v;
+
+ v = qemu_mallocz(sizeof(*v));
+
+ v->visitor.start_struct = qmp_output_start_struct;
+ v->visitor.end_struct = qmp_output_end_struct;
+ v->visitor.start_list = qmp_output_start_list;
+ v->visitor.next_list = qmp_output_next_list;
+ v->visitor.end_list = qmp_output_end_list;
+ v->visitor.type_enum = qmp_output_type_enum;
+ v->visitor.type_int = qmp_output_type_int;
+ v->visitor.type_bool = qmp_output_type_bool;
+ v->visitor.type_str = qmp_output_type_str;
+ v->visitor.type_number = qmp_output_type_number;
+
+ QTAILQ_INIT(&v->stack);
+
+ return v;
+}
diff --git a/qapi/qmp-output-visitor.h b/qapi/qmp-output-visitor.h
new file mode 100644
index 0000000000..4a649c2504
--- /dev/null
+++ b/qapi/qmp-output-visitor.h
@@ -0,0 +1,28 @@
+/*
+ * Output Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QMP_OUTPUT_VISITOR_H
+#define QMP_OUTPUT_VISITOR_H
+
+#include "qapi-visit-core.h"
+#include "qobject.h"
+
+typedef struct QmpOutputVisitor QmpOutputVisitor;
+
+QmpOutputVisitor *qmp_output_visitor_new(void);
+void qmp_output_visitor_cleanup(QmpOutputVisitor *v);
+
+QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
+Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
+
+#endif
diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c
new file mode 100644
index 0000000000..3fe8866d44
--- /dev/null
+++ b/qapi/qmp-registry.c
@@ -0,0 +1,40 @@
+/*
+ * Core Definitions for QAPI/QMP Dispatch
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Michael Roth <mdroth@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qmp-core.h"
+
+static QTAILQ_HEAD(, QmpCommand) qmp_commands =
+ QTAILQ_HEAD_INITIALIZER(qmp_commands);
+
+void qmp_register_command(const char *name, QmpCommandFunc *fn)
+{
+ QmpCommand *cmd = qemu_mallocz(sizeof(*cmd));
+
+ cmd->name = name;
+ cmd->type = QCT_NORMAL;
+ cmd->fn = fn;
+ QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node);
+}
+
+QmpCommand *qmp_find_command(const char *name)
+{
+ QmpCommand *i;
+
+ QTAILQ_FOREACH(i, &qmp_commands, node) {
+ if (strcmp(i->name, name) == 0) {
+ return i;
+ }
+ }
+ return NULL;
+}
diff --git a/qemu-common.h b/qemu-common.h
index c2b79bd60e..ba55719700 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -2,16 +2,9 @@
#ifndef QEMU_COMMON_H
#define QEMU_COMMON_H
+#include "compiler.h"
#include "config-host.h"
-#define QEMU_NORETURN __attribute__ ((__noreturn__))
-#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT
-#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-#else
-#define QEMU_WARN_UNUSED_RESULT
-#endif
-
-#define QEMU_BUILD_BUG_ON(x) typedef char __build_bug_on__##__LINE__[(x)?-1:1];
#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
typedef struct QEMUTimer QEMUTimer;
@@ -82,22 +75,6 @@ struct iovec {
#include <sys/uio.h>
#endif
-#if defined __GNUC__
-# if (__GNUC__ < 4) || \
- defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4)
- /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */
-# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2)))
-# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m)))
-# else
- /* Use gnu_printf when supported (qemu uses standard format strings). */
-# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2)))
-# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m)))
-# endif
-#else
-#define GCC_ATTR /**/
-#define GCC_FMT_ATTR(n, m)
-#endif
-
typedef int (*fprintf_function)(FILE *f, const char *fmt, ...)
GCC_FMT_ATTR(2, 3);
diff --git a/qemu-config.c b/qemu-config.c
index c63741c6b1..93d20c6cf8 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -23,6 +23,7 @@ static QemuOptsList qemu_drive_opts = {
},{
.name = "index",
.type = QEMU_OPT_NUMBER,
+ .help = "index number",
},{
.name = "cyls",
.type = QEMU_OPT_NUMBER,
@@ -46,6 +47,7 @@ static QemuOptsList qemu_drive_opts = {
},{
.name = "snapshot",
.type = QEMU_OPT_BOOL,
+ .help = "enable/disable snapshot mode",
},{
.name = "file",
.type = QEMU_OPT_STRING,
@@ -65,12 +67,15 @@ static QemuOptsList qemu_drive_opts = {
},{
.name = "serial",
.type = QEMU_OPT_STRING,
+ .help = "disk serial number",
},{
.name = "rerror",
.type = QEMU_OPT_STRING,
+ .help = "read error action",
},{
.name = "werror",
.type = QEMU_OPT_STRING,
+ .help = "write error action",
},{
.name = "addr",
.type = QEMU_OPT_STRING,
@@ -78,6 +83,7 @@ static QemuOptsList qemu_drive_opts = {
},{
.name = "readonly",
.type = QEMU_OPT_BOOL,
+ .help = "open drive file as read-only",
},
{ /* end of list */ }
},
diff --git a/qemu-ga.c b/qemu-ga.c
new file mode 100644
index 0000000000..6e2f61fe3c
--- /dev/null
+++ b/qemu-ga.c
@@ -0,0 +1,654 @@
+/*
+ * QEMU Guest Agent
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.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 <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <getopt.h>
+#include <termios.h>
+#include <syslog.h>
+#include "qemu_socket.h"
+#include "json-streamer.h"
+#include "json-parser.h"
+#include "qint.h"
+#include "qjson.h"
+#include "qga/guest-agent-core.h"
+#include "module.h"
+#include "signal.h"
+#include "qerror.h"
+#include "error_int.h"
+
+#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
+#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
+#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
+#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
+
+struct GAState {
+ JSONMessageParser parser;
+ GMainLoop *main_loop;
+ GSocket *conn_sock;
+ GIOChannel *conn_channel;
+ GSocket *listen_sock;
+ GIOChannel *listen_channel;
+ const char *path;
+ const char *method;
+ bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
+ GACommandState *command_state;
+ GLogLevelFlags log_level;
+ FILE *log_file;
+ bool logging_enabled;
+};
+
+static struct GAState *ga_state;
+
+static void quit_handler(int sig)
+{
+ g_debug("recieved signal num %d, quitting", sig);
+
+ if (g_main_loop_is_running(ga_state->main_loop)) {
+ g_main_loop_quit(ga_state->main_loop);
+ }
+}
+
+static void register_signal_handlers(void)
+{
+ struct sigaction sigact;
+ int ret;
+
+ memset(&sigact, 0, sizeof(struct sigaction));
+ sigact.sa_handler = quit_handler;
+
+ ret = sigaction(SIGINT, &sigact, NULL);
+ if (ret == -1) {
+ g_error("error configuring signal handler: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ ret = sigaction(SIGTERM, &sigact, NULL);
+ if (ret == -1) {
+ g_error("error configuring signal handler: %s", strerror(errno));
+ }
+}
+
+static void usage(const char *cmd)
+{
+ printf(
+"Usage: %s -c <channel_opts>\n"
+"QEMU Guest Agent %s\n"
+"\n"
+" -m, --method transport method: one of unix-listen, virtio-serial, or\n"
+" isa-serial (virtio-serial is the default)\n"
+" -p, --path device/socket path (%s is the default for virtio-serial)\n"
+" -l, --logfile set logfile path, logs to stderr by default\n"
+" -f, --pidfile specify pidfile (default is %s)\n"
+" -v, --verbose log extra debugging information\n"
+" -V, --version print version information and exit\n"
+" -d, --daemonize become a daemon\n"
+" -h, --help display this help and exit\n"
+"\n"
+"Report bugs to <mdroth@linux.vnet.ibm.com>\n"
+ , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
+}
+
+static void conn_channel_close(GAState *s);
+
+static const char *ga_log_level_str(GLogLevelFlags level)
+{
+ switch (level & G_LOG_LEVEL_MASK) {
+ case G_LOG_LEVEL_ERROR:
+ return "error";
+ case G_LOG_LEVEL_CRITICAL:
+ return "critical";
+ case G_LOG_LEVEL_WARNING:
+ return "warning";
+ case G_LOG_LEVEL_MESSAGE:
+ return "message";
+ case G_LOG_LEVEL_INFO:
+ return "info";
+ case G_LOG_LEVEL_DEBUG:
+ return "debug";
+ default:
+ return "user";
+ }
+}
+
+bool ga_logging_enabled(GAState *s)
+{
+ return s->logging_enabled;
+}
+
+void ga_disable_logging(GAState *s)
+{
+ s->logging_enabled = false;
+}
+
+void ga_enable_logging(GAState *s)
+{
+ s->logging_enabled = true;
+}
+
+static void ga_log(const gchar *domain, GLogLevelFlags level,
+ const gchar *msg, gpointer opaque)
+{
+ GAState *s = opaque;
+ GTimeVal time;
+ const char *level_str = ga_log_level_str(level);
+
+ if (!ga_logging_enabled(s)) {
+ return;
+ }
+
+ level &= G_LOG_LEVEL_MASK;
+ if (g_strcmp0(domain, "syslog") == 0) {
+ syslog(LOG_INFO, "%s: %s", level_str, msg);
+ } else if (level & s->log_level) {
+ g_get_current_time(&time);
+ fprintf(s->log_file,
+ "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
+ fflush(s->log_file);
+ }
+}
+
+static void become_daemon(const char *pidfile)
+{
+ pid_t pid, sid;
+ int pidfd;
+ char *pidstr = NULL;
+
+ pid = fork();
+ if (pid < 0) {
+ exit(EXIT_FAILURE);
+ }
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
+ if (pidfd == -1) {
+ g_critical("Cannot create pid file, %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (asprintf(&pidstr, "%d", getpid()) == -1) {
+ g_critical("Cannot allocate memory");
+ goto fail;
+ }
+ if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
+ free(pidstr);
+ g_critical("Failed to write pid file");
+ goto fail;
+ }
+
+ umask(0);
+ sid = setsid();
+ if (sid < 0) {
+ goto fail;
+ }
+ if ((chdir("/")) < 0) {
+ goto fail;
+ }
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ free(pidstr);
+ return;
+
+fail:
+ unlink(pidfile);
+ g_critical("failed to daemonize");
+ exit(EXIT_FAILURE);
+}
+
+static int conn_channel_send_buf(GIOChannel *channel, const char *buf,
+ gsize count)
+{
+ GError *err = NULL;
+ gsize written = 0;
+ GIOStatus status;
+
+ while (count) {
+ status = g_io_channel_write_chars(channel, buf, count, &written, &err);
+ g_debug("sending data, count: %d", (int)count);
+ if (err != NULL) {
+ g_warning("error sending newline: %s", err->message);
+ return err->code;
+ }
+ if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
+ return -EPIPE;
+ }
+
+ if (status == G_IO_STATUS_NORMAL) {
+ count -= written;
+ }
+ }
+
+ return 0;
+}
+
+static int conn_channel_send_payload(GIOChannel *channel, QObject *payload)
+{
+ int ret = 0;
+ const char *buf;
+ QString *payload_qstr;
+ GError *err = NULL;
+
+ g_assert(payload && channel);
+
+ payload_qstr = qobject_to_json(payload);
+ if (!payload_qstr) {
+ return -EINVAL;
+ }
+
+ qstring_append_chr(payload_qstr, '\n');
+ buf = qstring_get_str(payload_qstr);
+ ret = conn_channel_send_buf(channel, buf, strlen(buf));
+ if (ret) {
+ goto out_free;
+ }
+
+ g_io_channel_flush(channel, &err);
+ if (err != NULL) {
+ g_warning("error flushing payload: %s", err->message);
+ ret = err->code;
+ goto out_free;
+ }
+
+out_free:
+ QDECREF(payload_qstr);
+ if (err) {
+ g_error_free(err);
+ }
+ return ret;
+}
+
+static void process_command(GAState *s, QDict *req)
+{
+ QObject *rsp = NULL;
+ int ret;
+
+ g_assert(req);
+ g_debug("processing command");
+ rsp = qmp_dispatch(QOBJECT(req));
+ if (rsp) {
+ ret = conn_channel_send_payload(s->conn_channel, rsp);
+ if (ret) {
+ g_warning("error sending payload: %s", strerror(ret));
+ }
+ qobject_decref(rsp);
+ } else {
+ g_warning("error getting response");
+ }
+}
+
+/* handle requests/control events coming in over the channel */
+static void process_event(JSONMessageParser *parser, QList *tokens)
+{
+ GAState *s = container_of(parser, GAState, parser);
+ QObject *obj;
+ QDict *qdict;
+ Error *err = NULL;
+ int ret;
+
+ g_assert(s && parser);
+
+ g_debug("process_event: called");
+ obj = json_parser_parse_err(tokens, NULL, &err);
+ if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
+ qobject_decref(obj);
+ qdict = qdict_new();
+ if (!err) {
+ g_warning("failed to parse event: unknown error");
+ error_set(&err, QERR_JSON_PARSING);
+ } else {
+ g_warning("failed to parse event: %s", error_get_pretty(err));
+ }
+ qdict_put_obj(qdict, "error", error_get_qobject(err));
+ error_free(err);
+ } else {
+ qdict = qobject_to_qdict(obj);
+ }
+
+ g_assert(qdict);
+
+ /* handle host->guest commands */
+ if (qdict_haskey(qdict, "execute")) {
+ process_command(s, qdict);
+ } else {
+ if (!qdict_haskey(qdict, "error")) {
+ QDECREF(qdict);
+ qdict = qdict_new();
+ g_warning("unrecognized payload format");
+ error_set(&err, QERR_UNSUPPORTED);
+ qdict_put_obj(qdict, "error", error_get_qobject(err));
+ error_free(err);
+ }
+ ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
+ if (ret) {
+ g_warning("error sending payload: %s", strerror(ret));
+ }
+ }
+
+ QDECREF(qdict);
+}
+
+static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
+ gpointer data)
+{
+ GAState *s = data;
+ gchar buf[1024];
+ gsize count;
+ GError *err = NULL;
+ memset(buf, 0, 1024);
+ GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
+ &count, &err);
+ if (err != NULL) {
+ g_warning("error reading channel: %s", err->message);
+ conn_channel_close(s);
+ g_error_free(err);
+ return false;
+ }
+ switch (status) {
+ case G_IO_STATUS_ERROR:
+ g_warning("problem");
+ return false;
+ case G_IO_STATUS_NORMAL:
+ g_debug("read data, count: %d, data: %s", (int)count, buf);
+ json_message_parser_feed(&s->parser, (char *)buf, (int)count);
+ case G_IO_STATUS_AGAIN:
+ /* virtio causes us to spin here when no process is attached to
+ * host-side chardev. sleep a bit to mitigate this
+ */
+ if (s->virtio) {
+ usleep(100*1000);
+ }
+ return true;
+ case G_IO_STATUS_EOF:
+ g_debug("received EOF");
+ conn_channel_close(s);
+ if (s->virtio) {
+ return true;
+ }
+ return false;
+ default:
+ g_warning("unknown channel read status, closing");
+ conn_channel_close(s);
+ return false;
+ }
+ return true;
+}
+
+static int conn_channel_add(GAState *s, int fd)
+{
+ GIOChannel *conn_channel;
+ GError *err = NULL;
+
+ g_assert(s && !s->conn_channel);
+ conn_channel = g_io_channel_unix_new(fd);
+ g_assert(conn_channel);
+ g_io_channel_set_encoding(conn_channel, NULL, &err);
+ if (err != NULL) {
+ g_warning("error setting channel encoding to binary");
+ g_error_free(err);
+ return -1;
+ }
+ g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
+ conn_channel_read, s);
+ s->conn_channel = conn_channel;
+ return 0;
+}
+
+static gboolean listen_channel_accept(GIOChannel *channel,
+ GIOCondition condition, gpointer data)
+{
+ GAState *s = data;
+ GError *err = NULL;
+ g_assert(channel != NULL);
+ int ret;
+ bool accepted = false;
+
+ s->conn_sock = g_socket_accept(s->listen_sock, NULL, &err);
+ if (err != NULL) {
+ g_warning("error converting fd to gsocket: %s", err->message);
+ g_error_free(err);
+ goto out;
+ }
+ ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
+ if (ret) {
+ g_warning("error setting up connection");
+ goto out;
+ }
+ accepted = true;
+
+out:
+ /* only accept 1 connection at a time */
+ return !accepted;
+}
+
+/* start polling for readable events on listen fd, new==true
+ * indicates we should use the existing s->listen_channel
+ */
+static int listen_channel_add(GAState *s, int listen_fd, bool new)
+{
+ GError *err = NULL;
+
+ if (new) {
+ s->listen_channel = g_io_channel_unix_new(listen_fd);
+ if (s->listen_sock) {
+ g_object_unref(s->listen_sock);
+ }
+ s->listen_sock = g_socket_new_from_fd(listen_fd, &err);
+ if (err != NULL) {
+ g_warning("error converting fd to gsocket: %s", err->message);
+ g_error_free(err);
+ return -1;
+ }
+ }
+ g_io_add_watch(s->listen_channel, G_IO_IN,
+ listen_channel_accept, s);
+ return 0;
+}
+
+/* cleanup state for closed connection/session, start accepting new
+ * connections if we're in listening mode
+ */
+static void conn_channel_close(GAState *s)
+{
+ if (strcmp(s->method, "unix-listen") == 0) {
+ g_io_channel_shutdown(s->conn_channel, true, NULL);
+ g_object_unref(s->conn_sock);
+ s->conn_sock = NULL;
+ listen_channel_add(s, 0, false);
+ } else if (strcmp(s->method, "virtio-serial") == 0) {
+ /* we spin on EOF for virtio-serial, so back off a bit. also,
+ * dont close the connection in this case, it'll resume normal
+ * operation when another process connects to host chardev
+ */
+ usleep(100*1000);
+ goto out_noclose;
+ }
+ g_io_channel_unref(s->conn_channel);
+ s->conn_channel = NULL;
+out_noclose:
+ return;
+}
+
+static void init_guest_agent(GAState *s)
+{
+ struct termios tio;
+ int ret, fd;
+
+ if (s->method == NULL) {
+ /* try virtio-serial as our default */
+ s->method = "virtio-serial";
+ }
+
+ if (s->path == NULL) {
+ if (strcmp(s->method, "virtio-serial") != 0) {
+ g_critical("must specify a path for this channel");
+ exit(EXIT_FAILURE);
+ }
+ /* try the default path for the virtio-serial port */
+ s->path = QGA_VIRTIO_PATH_DEFAULT;
+ }
+
+ if (strcmp(s->method, "virtio-serial") == 0) {
+ s->virtio = true;
+ fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
+ if (fd == -1) {
+ g_critical("error opening channel: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ ret = conn_channel_add(s, fd);
+ if (ret) {
+ g_critical("error adding channel to main loop");
+ exit(EXIT_FAILURE);
+ }
+ } else if (strcmp(s->method, "isa-serial") == 0) {
+ fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
+ if (fd == -1) {
+ g_critical("error opening channel: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ tcgetattr(fd, &tio);
+ /* set up serial port for non-canonical, dumb byte streaming */
+ tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
+ INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
+ IMAXBEL);
+ tio.c_oflag = 0;
+ tio.c_lflag = 0;
+ tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
+ /* 1 available byte min or reads will block (we'll set non-blocking
+ * elsewhere, else we have to deal with read()=0 instead)
+ */
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ /* flush everything waiting for read/xmit, it's garbage at this point */
+ tcflush(fd, TCIFLUSH);
+ tcsetattr(fd, TCSANOW, &tio);
+ ret = conn_channel_add(s, fd);
+ if (ret) {
+ g_error("error adding channel to main loop");
+ }
+ } else if (strcmp(s->method, "unix-listen") == 0) {
+ fd = unix_listen(s->path, NULL, strlen(s->path));
+ if (fd == -1) {
+ g_critical("error opening path: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ ret = listen_channel_add(s, fd, true);
+ if (ret) {
+ g_critical("error binding/listening to specified socket");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ g_critical("unsupported channel method/type: %s", s->method);
+ exit(EXIT_FAILURE);
+ }
+
+ json_message_parser_init(&s->parser, process_event);
+ s->main_loop = g_main_loop_new(NULL, false);
+}
+
+int main(int argc, char **argv)
+{
+ const char *sopt = "hVvdm:p:l:f:";
+ const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
+ const struct option lopt[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "logfile", 0, NULL, 'l' },
+ { "pidfile", 0, NULL, 'f' },
+ { "verbose", 0, NULL, 'v' },
+ { "method", 0, NULL, 'm' },
+ { "path", 0, NULL, 'p' },
+ { "daemonize", 0, NULL, 'd' },
+ { NULL, 0, NULL, 0 }
+ };
+ int opt_ind = 0, ch, daemonize = 0;
+ GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
+ FILE *log_file = stderr;
+ GAState *s;
+
+ while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
+ switch (ch) {
+ case 'm':
+ method = optarg;
+ break;
+ case 'p':
+ path = optarg;
+ break;
+ case 'l':
+ log_file = fopen(optarg, "a");
+ if (!log_file) {
+ g_critical("unable to open specified log file: %s",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'f':
+ pidfile = optarg;
+ break;
+ case 'v':
+ /* enable all log levels */
+ log_level = G_LOG_LEVEL_MASK;
+ break;
+ case 'V':
+ printf("QEMU Guest Agent %s\n", QGA_VERSION);
+ return 0;
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ case '?':
+ g_print("Unknown option, try '%s --help' for more information.\n",
+ argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (daemonize) {
+ g_debug("starting daemon");
+ become_daemon(pidfile);
+ }
+
+ g_type_init();
+ g_thread_init(NULL);
+
+ s = qemu_mallocz(sizeof(GAState));
+ s->conn_channel = NULL;
+ s->path = path;
+ s->method = method;
+ s->log_file = log_file;
+ s->log_level = log_level;
+ g_log_set_default_handler(ga_log, s);
+ g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
+ s->logging_enabled = true;
+ s->command_state = ga_command_state_new();
+ ga_command_state_init(s, s->command_state);
+ ga_command_state_init_all(s->command_state);
+ ga_state = s;
+
+ module_call_init(MODULE_INIT_QAPI);
+ init_guest_agent(ga_state);
+ register_signal_handlers();
+
+ g_main_loop_run(ga_state->main_loop);
+
+ ga_command_state_cleanup_all(ga_state->command_state);
+ unlink(pidfile);
+
+ return 0;
+}
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 2b70618c70..1299e83ef2 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -30,7 +30,7 @@ ETEXI
DEF("convert", img_convert,
"convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename")
STEXI
-@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
+@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI
DEF("info", img_info,
@@ -48,7 +48,7 @@ ETEXI
DEF("rebase", img_rebase,
"rebase [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
STEXI
-@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+@item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
ETEXI
DEF("resize", img_resize,
diff --git a/qemu-img.c b/qemu-img.c
index 54137a4e92..b205e98dd2 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1024,35 +1024,6 @@ out:
return 0;
}
-#ifdef _WIN32
-static int64_t get_allocated_file_size(const char *filename)
-{
- typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
- get_compressed_t get_compressed;
- struct _stati64 st;
-
- /* WinNT support GetCompressedFileSize to determine allocate size */
- get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
- if (get_compressed) {
- DWORD high, low;
- low = get_compressed(filename, &high);
- if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR)
- return (((int64_t) high) << 32) + low;
- }
-
- if (_stati64(filename, &st) < 0)
- return -1;
- return st.st_size;
-}
-#else
-static int64_t get_allocated_file_size(const char *filename)
-{
- struct stat st;
- if (stat(filename, &st) < 0)
- return -1;
- return (int64_t)st.st_blocks * 512;
-}
-#endif
static void dump_snapshots(BlockDriverState *bs)
{
@@ -1112,7 +1083,7 @@ static int img_info(int argc, char **argv)
bdrv_get_format(bs, fmt_name, sizeof(fmt_name));
bdrv_get_geometry(bs, &total_sectors);
get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
- allocated_size = get_allocated_file_size(filename);
+ allocated_size = bdrv_get_allocated_file_size(bs);
if (allocated_size < 0) {
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
} else {
diff --git a/qemu-img.texi b/qemu-img.texi
index 526474c112..495a1b6695 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -38,6 +38,8 @@ by the used format or see the format descriptions below for details.
indicates that target image must be compressed (qcow format only)
@item -h
with or without a command shows help and lists the supported formats
+@item -p
+display progress bar (convert and rebase commands only)
@end table
Parameters to snapshot subcommand:
@@ -84,7 +86,7 @@ it doesn't need to be specified separately in this case.
Commit the changes recorded in @var{filename} in its base image.
-@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
+@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
@@ -114,7 +116,7 @@ they are displayed too.
List, apply, create or delete snapshots in image @var{filename}.
-@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+@item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
Changes the backing file of an image. Only the formats @code{qcow2} and
@code{qed} support changing the backing file.
diff --git a/qemu-io.c b/qemu-io.c
index dd4ebf537a..a553d0c98d 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -20,7 +20,7 @@
#define VERSION "0.0.1"
-#define CMD_NOFILE_OK 0x01
+#define CMD_NOFILE_OK 0x01
char *progname;
static BlockDriverState *bs;
@@ -35,16 +35,16 @@ static int misalign;
*/
static int parse_pattern(const char *arg)
{
- char *endptr = NULL;
- long pattern;
+ char *endptr = NULL;
+ long pattern;
- pattern = strtol(arg, &endptr, 0);
- if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') {
- printf("%s is not a valid pattern byte\n", arg);
- return -1;
- }
+ pattern = strtol(arg, &endptr, 0);
+ if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') {
+ printf("%s is not a valid pattern byte\n", arg);
+ return -1;
+ }
- return pattern;
+ return pattern;
}
/*
@@ -54,70 +54,73 @@ static int parse_pattern(const char *arg)
* that is specified on the command line.
*/
-#define MISALIGN_OFFSET 16
+#define MISALIGN_OFFSET 16
static void *qemu_io_alloc(size_t len, int pattern)
{
- void *buf;
-
- if (misalign)
- len += MISALIGN_OFFSET;
- buf = qemu_blockalign(bs, len);
- memset(buf, pattern, len);
- if (misalign)
- buf += MISALIGN_OFFSET;
- return buf;
+ void *buf;
+
+ if (misalign) {
+ len += MISALIGN_OFFSET;
+ }
+ buf = qemu_blockalign(bs, len);
+ memset(buf, pattern, len);
+ if (misalign) {
+ buf += MISALIGN_OFFSET;
+ }
+ return buf;
}
static void qemu_io_free(void *p)
{
- if (misalign)
- p -= MISALIGN_OFFSET;
- qemu_vfree(p);
+ if (misalign) {
+ p -= MISALIGN_OFFSET;
+ }
+ qemu_vfree(p);
}
-static void
-dump_buffer(const void *buffer, int64_t offset, int len)
+static void dump_buffer(const void *buffer, int64_t offset, int len)
{
- int i, j;
- const uint8_t *p;
-
- for (i = 0, p = buffer; i < len; i += 16) {
- const uint8_t *s = p;
-
- printf("%08" PRIx64 ": ", offset + i);
- for (j = 0; j < 16 && i + j < len; j++, p++)
- printf("%02x ", *p);
- printf(" ");
- for (j = 0; j < 16 && i + j < len; j++, s++) {
- if (isalnum(*s))
- printf("%c", *s);
- else
- printf(".");
- }
- printf("\n");
- }
+ int i, j;
+ const uint8_t *p;
+
+ for (i = 0, p = buffer; i < len; i += 16) {
+ const uint8_t *s = p;
+
+ printf("%08" PRIx64 ": ", offset + i);
+ for (j = 0; j < 16 && i + j < len; j++, p++) {
+ printf("%02x ", *p);
+ }
+ printf(" ");
+ for (j = 0; j < 16 && i + j < len; j++, s++) {
+ if (isalnum(*s)) {
+ printf("%c", *s);
+ } else {
+ printf(".");
+ }
+ }
+ printf("\n");
+ }
}
-static void
-print_report(const char *op, struct timeval *t, int64_t offset,
- int count, int total, int cnt, int Cflag)
+static void print_report(const char *op, struct timeval *t, int64_t offset,
+ int count, int total, int cnt, int Cflag)
{
- char s1[64], s2[64], ts[64];
-
- timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0);
- if (!Cflag) {
- cvtstr((double)total, s1, sizeof(s1));
- cvtstr(tdiv((double)total, *t), s2, sizeof(s2));
- printf("%s %d/%d bytes at offset %" PRId64 "\n",
- op, total, count, offset);
- printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
- s1, cnt, ts, s2, tdiv((double)cnt, *t));
- } else {/* bytes,ops,time,bytes/sec,ops/sec */
- printf("%d,%d,%s,%.3f,%.3f\n",
- total, cnt, ts,
- tdiv((double)total, *t),
- tdiv((double)cnt, *t));
- }
+ char s1[64], s2[64], ts[64];
+
+ timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0);
+ if (!Cflag) {
+ cvtstr((double)total, s1, sizeof(s1));
+ cvtstr(tdiv((double)total, *t), s2, sizeof(s2));
+ printf("%s %d/%d bytes at offset %" PRId64 "\n",
+ op, total, count, offset);
+ printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
+ s1, cnt, ts, s2, tdiv((double)cnt, *t));
+ } else {/* bytes,ops,time,bytes/sec,ops/sec */
+ printf("%d,%d,%s,%.3f,%.3f\n",
+ total, cnt, ts,
+ tdiv((double)total, *t),
+ tdiv((double)cnt, *t));
+ }
}
/*
@@ -127,192 +130,200 @@ print_report(const char *op, struct timeval *t, int64_t offset,
static void *
create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern)
{
- size_t *sizes = calloc(nr_iov, sizeof(size_t));
- size_t count = 0;
- void *buf = NULL;
- void *p;
- int i;
-
- for (i = 0; i < nr_iov; i++) {
- char *arg = argv[i];
- int64_t len;
-
- len = cvtnum(arg);
- if (len < 0) {
- printf("non-numeric length argument -- %s\n", arg);
- goto fail;
- }
-
- /* should be SIZE_T_MAX, but that doesn't exist */
- if (len > INT_MAX) {
- printf("too large length argument -- %s\n", arg);
- goto fail;
- }
-
- if (len & 0x1ff) {
- printf("length argument %" PRId64
- " is not sector aligned\n", len);
- goto fail;
- }
-
- sizes[i] = len;
- count += len;
- }
-
- qemu_iovec_init(qiov, nr_iov);
-
- buf = p = qemu_io_alloc(count, pattern);
-
- for (i = 0; i < nr_iov; i++) {
- qemu_iovec_add(qiov, p, sizes[i]);
- p += sizes[i];
- }
+ size_t *sizes = calloc(nr_iov, sizeof(size_t));
+ size_t count = 0;
+ void *buf = NULL;
+ void *p;
+ int i;
+
+ for (i = 0; i < nr_iov; i++) {
+ char *arg = argv[i];
+ int64_t len;
+
+ len = cvtnum(arg);
+ if (len < 0) {
+ printf("non-numeric length argument -- %s\n", arg);
+ goto fail;
+ }
+
+ /* should be SIZE_T_MAX, but that doesn't exist */
+ if (len > INT_MAX) {
+ printf("too large length argument -- %s\n", arg);
+ goto fail;
+ }
+
+ if (len & 0x1ff) {
+ printf("length argument %" PRId64
+ " is not sector aligned\n", len);
+ goto fail;
+ }
+
+ sizes[i] = len;
+ count += len;
+ }
+
+ qemu_iovec_init(qiov, nr_iov);
+
+ buf = p = qemu_io_alloc(count, pattern);
+
+ for (i = 0; i < nr_iov; i++) {
+ qemu_iovec_add(qiov, p, sizes[i]);
+ p += sizes[i];
+ }
fail:
- free(sizes);
- return buf;
+ free(sizes);
+ return buf;
}
static int do_read(char *buf, int64_t offset, int count, int *total)
{
- int ret;
+ int ret;
- ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9);
- if (ret < 0)
- return ret;
- *total = count;
- return 1;
+ ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9);
+ if (ret < 0) {
+ return ret;
+ }
+ *total = count;
+ return 1;
}
static int do_write(char *buf, int64_t offset, int count, int *total)
{
- int ret;
+ int ret;
- ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9);
- if (ret < 0)
- return ret;
- *total = count;
- return 1;
+ ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9);
+ if (ret < 0) {
+ return ret;
+ }
+ *total = count;
+ return 1;
}
static int do_pread(char *buf, int64_t offset, int count, int *total)
{
- *total = bdrv_pread(bs, offset, (uint8_t *)buf, count);
- if (*total < 0)
- return *total;
- return 1;
+ *total = bdrv_pread(bs, offset, (uint8_t *)buf, count);
+ if (*total < 0) {
+ return *total;
+ }
+ return 1;
}
static int do_pwrite(char *buf, int64_t offset, int count, int *total)
{
- *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count);
- if (*total < 0)
- return *total;
- return 1;
+ *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count);
+ if (*total < 0) {
+ return *total;
+ }
+ return 1;
}
static int do_load_vmstate(char *buf, int64_t offset, int count, int *total)
{
- *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count);
- if (*total < 0)
- return *total;
- return 1;
+ *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count);
+ if (*total < 0) {
+ return *total;
+ }
+ return 1;
}
static int do_save_vmstate(char *buf, int64_t offset, int count, int *total)
{
- *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count);
- if (*total < 0)
- return *total;
- return 1;
+ *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count);
+ if (*total < 0) {
+ return *total;
+ }
+ return 1;
}
#define NOT_DONE 0x7fffffff
static void aio_rw_done(void *opaque, int ret)
{
- *(int *)opaque = ret;
+ *(int *)opaque = ret;
}
static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total)
{
- BlockDriverAIOCB *acb;
- int async_ret = NOT_DONE;
+ BlockDriverAIOCB *acb;
+ int async_ret = NOT_DONE;
- acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9,
- aio_rw_done, &async_ret);
- if (!acb)
- return -EIO;
-
- while (async_ret == NOT_DONE)
- qemu_aio_wait();
+ acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9,
+ aio_rw_done, &async_ret);
+ if (!acb) {
+ return -EIO;
+ }
+ while (async_ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
- *total = qiov->size;
- return async_ret < 0 ? async_ret : 1;
+ *total = qiov->size;
+ return async_ret < 0 ? async_ret : 1;
}
static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total)
{
- BlockDriverAIOCB *acb;
- int async_ret = NOT_DONE;
+ BlockDriverAIOCB *acb;
+ int async_ret = NOT_DONE;
- acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9,
- aio_rw_done, &async_ret);
- if (!acb)
- return -EIO;
+ acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9,
+ aio_rw_done, &async_ret);
+ if (!acb) {
+ return -EIO;
+ }
- while (async_ret == NOT_DONE)
- qemu_aio_wait();
+ while (async_ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
- *total = qiov->size;
- return async_ret < 0 ? async_ret : 1;
+ *total = qiov->size;
+ return async_ret < 0 ? async_ret : 1;
}
struct multiwrite_async_ret {
- int num_done;
- int error;
+ int num_done;
+ int error;
};
static void multiwrite_cb(void *opaque, int ret)
{
- struct multiwrite_async_ret *async_ret = opaque;
+ struct multiwrite_async_ret *async_ret = opaque;
- async_ret->num_done++;
- if (ret < 0) {
- async_ret->error = ret;
- }
+ async_ret->num_done++;
+ if (ret < 0) {
+ async_ret->error = ret;
+ }
}
static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total)
{
- int i, ret;
- struct multiwrite_async_ret async_ret = {
- .num_done = 0,
- .error = 0,
- };
-
- *total = 0;
- for (i = 0; i < num_reqs; i++) {
- reqs[i].cb = multiwrite_cb;
- reqs[i].opaque = &async_ret;
- *total += reqs[i].qiov->size;
- }
-
- ret = bdrv_aio_multiwrite(bs, reqs, num_reqs);
- if (ret < 0) {
- return ret;
- }
-
- while (async_ret.num_done < num_reqs) {
- qemu_aio_wait();
- }
-
- return async_ret.error < 0 ? async_ret.error : 1;
+ int i, ret;
+ struct multiwrite_async_ret async_ret = {
+ .num_done = 0,
+ .error = 0,
+ };
+
+ *total = 0;
+ for (i = 0; i < num_reqs; i++) {
+ reqs[i].cb = multiwrite_cb;
+ reqs[i].opaque = &async_ret;
+ *total += reqs[i].qiov->size;
+ }
+
+ ret = bdrv_aio_multiwrite(bs, reqs, num_reqs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ while (async_ret.num_done < num_reqs) {
+ qemu_aio_wait();
+ }
+
+ return async_ret.error < 0 ? async_ret.error : 1;
}
-static void
-read_help(void)
+static void read_help(void)
{
- printf(
+ printf(
"\n"
" reads a range of bytes from the given offset\n"
"\n"
@@ -335,94 +346,95 @@ read_help(void)
static int read_f(int argc, char **argv);
static const cmdinfo_t read_cmd = {
- .name = "read",
- .altname = "r",
- .cfunc = read_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len",
- .oneline = "reads a number of bytes at a specified offset",
- .help = read_help,
+ .name = "read",
+ .altname = "r",
+ .cfunc = read_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len",
+ .oneline = "reads a number of bytes at a specified offset",
+ .help = read_help,
};
-static int
-read_f(int argc, char **argv)
+static int read_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, pflag = 0, qflag = 0, vflag = 0;
- int Pflag = 0, sflag = 0, lflag = 0, bflag = 0;
- int c, cnt;
- char *buf;
- int64_t offset;
- int count;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int pattern = 0, pattern_offset = 0, pattern_count = 0;
-
- while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) {
- switch (c) {
- case 'b':
- bflag = 1;
- break;
- case 'C':
- Cflag = 1;
- break;
- case 'l':
- lflag = 1;
- pattern_count = cvtnum(optarg);
- if (pattern_count < 0) {
- printf("non-numeric length argument -- %s\n", optarg);
- return 0;
- }
- break;
- case 'p':
- pflag = 1;
- break;
- case 'P':
- Pflag = 1;
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- case 'q':
- qflag = 1;
- break;
- case 's':
- sflag = 1;
- pattern_offset = cvtnum(optarg);
- if (pattern_offset < 0) {
- printf("non-numeric length argument -- %s\n", optarg);
- return 0;
- }
- break;
- case 'v':
- vflag = 1;
- break;
- default:
- return command_usage(&read_cmd);
- }
- }
-
- if (optind != argc - 2)
- return command_usage(&read_cmd);
-
- if (bflag && pflag) {
- printf("-b and -p cannot be specified at the same time\n");
- return 0;
- }
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- optind++;
- count = cvtnum(argv[optind]);
- if (count < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
+ struct timeval t1, t2;
+ int Cflag = 0, pflag = 0, qflag = 0, vflag = 0;
+ int Pflag = 0, sflag = 0, lflag = 0, bflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ int count;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int pattern = 0, pattern_offset = 0, pattern_count = 0;
+
+ while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) {
+ switch (c) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ pattern_count = cvtnum(optarg);
+ if (pattern_count < 0) {
+ printf("non-numeric length argument -- %s\n", optarg);
+ return 0;
+ }
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'P':
+ Pflag = 1;
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ pattern_offset = cvtnum(optarg);
+ if (pattern_offset < 0) {
+ printf("non-numeric length argument -- %s\n", optarg);
+ return 0;
+ }
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ return command_usage(&read_cmd);
+ }
+ }
+
+ if (optind != argc - 2) {
+ return command_usage(&read_cmd);
+ }
+
+ if (bflag && pflag) {
+ printf("-b and -p cannot be specified at the same time\n");
+ return 0;
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
if (!Pflag && (lflag || sflag)) {
return command_usage(&read_cmd);
@@ -437,66 +449,68 @@ read_f(int argc, char **argv)
return 0;
}
- if (!pflag)
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
-
- if (count & 0x1ff) {
- printf("count %d is not sector aligned\n",
- count);
- return 0;
- }
- }
-
- buf = qemu_io_alloc(count, 0xab);
-
- gettimeofday(&t1, NULL);
- if (pflag)
- cnt = do_pread(buf, offset, count, &total);
- else if (bflag)
- cnt = do_load_vmstate(buf, offset, count, &total);
- else
- cnt = do_read(buf, offset, count, &total);
- gettimeofday(&t2, NULL);
-
- if (cnt < 0) {
- printf("read failed: %s\n", strerror(-cnt));
- goto out;
- }
-
- if (Pflag) {
- void* cmp_buf = malloc(pattern_count);
- memset(cmp_buf, pattern, pattern_count);
- if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) {
- printf("Pattern verification failed at offset %"
- PRId64 ", %d bytes\n",
- offset + pattern_offset, pattern_count);
- }
- free(cmp_buf);
- }
-
- if (qflag)
- goto out;
-
- if (vflag)
- dump_buffer(buf, offset, count);
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("read", &t2, offset, count, total, cnt, Cflag);
+ if (!pflag) {
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+ if (count & 0x1ff) {
+ printf("count %d is not sector aligned\n",
+ count);
+ return 0;
+ }
+ }
+
+ buf = qemu_io_alloc(count, 0xab);
+
+ gettimeofday(&t1, NULL);
+ if (pflag) {
+ cnt = do_pread(buf, offset, count, &total);
+ } else if (bflag) {
+ cnt = do_load_vmstate(buf, offset, count, &total);
+ } else {
+ cnt = do_read(buf, offset, count, &total);
+ }
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("read failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (Pflag) {
+ void *cmp_buf = malloc(pattern_count);
+ memset(cmp_buf, pattern, pattern_count);
+ if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %d bytes\n",
+ offset + pattern_offset, pattern_count);
+ }
+ free(cmp_buf);
+ }
+
+ if (qflag) {
+ goto out;
+ }
+
+ if (vflag) {
+ dump_buffer(buf, offset, count);
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("read", &t2, offset, count, total, cnt, Cflag);
out:
- qemu_io_free(buf);
+ qemu_io_free(buf);
- return 0;
+ return 0;
}
-static void
-readv_help(void)
+static void readv_help(void)
{
- printf(
+ printf(
"\n"
" reads a range of bytes from the given offset into multiple buffers\n"
"\n"
@@ -516,111 +530,112 @@ readv_help(void)
static int readv_f(int argc, char **argv);
static const cmdinfo_t readv_cmd = {
- .name = "readv",
- .cfunc = readv_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cqv] [-P pattern ] off len [len..]",
- .oneline = "reads a number of bytes at a specified offset",
- .help = readv_help,
+ .name = "readv",
+ .cfunc = readv_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cqv] [-P pattern ] off len [len..]",
+ .oneline = "reads a number of bytes at a specified offset",
+ .help = readv_help,
};
-static int
-readv_f(int argc, char **argv)
+static int readv_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, qflag = 0, vflag = 0;
- int c, cnt;
- char *buf;
- int64_t offset;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int nr_iov;
- QEMUIOVector qiov;
- int pattern = 0;
- int Pflag = 0;
-
- while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
- switch (c) {
- case 'C':
- Cflag = 1;
- break;
- case 'P':
- Pflag = 1;
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- case 'q':
- qflag = 1;
- break;
- case 'v':
- vflag = 1;
- break;
- default:
- return command_usage(&readv_cmd);
- }
- }
-
- if (optind > argc - 2)
- return command_usage(&readv_cmd);
-
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
- optind++;
-
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
- }
-
- nr_iov = argc - optind;
- buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab);
-
- gettimeofday(&t1, NULL);
- cnt = do_aio_readv(&qiov, offset, &total);
- gettimeofday(&t2, NULL);
-
- if (cnt < 0) {
- printf("readv failed: %s\n", strerror(-cnt));
- goto out;
- }
-
- if (Pflag) {
- void* cmp_buf = malloc(qiov.size);
- memset(cmp_buf, pattern, qiov.size);
- if (memcmp(buf, cmp_buf, qiov.size)) {
- printf("Pattern verification failed at offset %"
- PRId64 ", %zd bytes\n",
- offset, qiov.size);
- }
- free(cmp_buf);
- }
-
- if (qflag)
- goto out;
-
- if (vflag)
- dump_buffer(buf, offset, qiov.size);
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("read", &t2, offset, qiov.size, total, cnt, Cflag);
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0, vflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ QEMUIOVector qiov;
+ int pattern = 0;
+ int Pflag = 0;
+
+ while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'P':
+ Pflag = 1;
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ return command_usage(&readv_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ return command_usage(&readv_cmd);
+ }
+
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab);
+
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_readv(&qiov, offset, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("readv failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (Pflag) {
+ void *cmp_buf = malloc(qiov.size);
+ memset(cmp_buf, pattern, qiov.size);
+ if (memcmp(buf, cmp_buf, qiov.size)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %zd bytes\n", offset, qiov.size);
+ }
+ free(cmp_buf);
+ }
+
+ if (qflag) {
+ goto out;
+ }
+
+ if (vflag) {
+ dump_buffer(buf, offset, qiov.size);
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("read", &t2, offset, qiov.size, total, cnt, Cflag);
out:
- qemu_io_free(buf);
- return 0;
+ qemu_io_free(buf);
+ return 0;
}
-static void
-write_help(void)
+static void write_help(void)
{
- printf(
+ printf(
"\n"
" writes a range of bytes from the given offset\n"
"\n"
@@ -640,121 +655,124 @@ write_help(void)
static int write_f(int argc, char **argv);
static const cmdinfo_t write_cmd = {
- .name = "write",
- .altname = "w",
- .cfunc = write_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-abCpq] [-P pattern ] off len",
- .oneline = "writes a number of bytes at a specified offset",
- .help = write_help,
+ .name = "write",
+ .altname = "w",
+ .cfunc = write_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-abCpq] [-P pattern ] off len",
+ .oneline = "writes a number of bytes at a specified offset",
+ .help = write_help,
};
-static int
-write_f(int argc, char **argv)
+static int write_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, pflag = 0, qflag = 0, bflag = 0;
- int c, cnt;
- char *buf;
- int64_t offset;
- int count;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int pattern = 0xcd;
-
- while ((c = getopt(argc, argv, "bCpP:q")) != EOF) {
- switch (c) {
- case 'b':
- bflag = 1;
- break;
- case 'C':
- Cflag = 1;
- break;
- case 'p':
- pflag = 1;
- break;
- case 'P':
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- case 'q':
- qflag = 1;
- break;
- default:
- return command_usage(&write_cmd);
- }
- }
-
- if (optind != argc - 2)
- return command_usage(&write_cmd);
-
- if (bflag && pflag) {
- printf("-b and -p cannot be specified at the same time\n");
- return 0;
- }
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- optind++;
- count = cvtnum(argv[optind]);
- if (count < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- if (!pflag) {
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
- }
-
- if (count & 0x1ff) {
- printf("count %d is not sector aligned\n",
- count);
- return 0;
- }
- }
-
- buf = qemu_io_alloc(count, pattern);
-
- gettimeofday(&t1, NULL);
- if (pflag)
- cnt = do_pwrite(buf, offset, count, &total);
- else if (bflag)
- cnt = do_save_vmstate(buf, offset, count, &total);
- else
- cnt = do_write(buf, offset, count, &total);
- gettimeofday(&t2, NULL);
-
- if (cnt < 0) {
- printf("write failed: %s\n", strerror(-cnt));
- goto out;
- }
-
- if (qflag)
- goto out;
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("wrote", &t2, offset, count, total, cnt, Cflag);
+ struct timeval t1, t2;
+ int Cflag = 0, pflag = 0, qflag = 0, bflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ int count;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int pattern = 0xcd;
+
+ while ((c = getopt(argc, argv, "bCpP:q")) != EOF) {
+ switch (c) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ default:
+ return command_usage(&write_cmd);
+ }
+ }
+
+ if (optind != argc - 2) {
+ return command_usage(&write_cmd);
+ }
+
+ if (bflag && pflag) {
+ printf("-b and -p cannot be specified at the same time\n");
+ return 0;
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ if (!pflag) {
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ if (count & 0x1ff) {
+ printf("count %d is not sector aligned\n",
+ count);
+ return 0;
+ }
+ }
+
+ buf = qemu_io_alloc(count, pattern);
+
+ gettimeofday(&t1, NULL);
+ if (pflag) {
+ cnt = do_pwrite(buf, offset, count, &total);
+ } else if (bflag) {
+ cnt = do_save_vmstate(buf, offset, count, &total);
+ } else {
+ cnt = do_write(buf, offset, count, &total);
+ }
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("write failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (qflag) {
+ goto out;
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, offset, count, total, cnt, Cflag);
out:
- qemu_io_free(buf);
+ qemu_io_free(buf);
- return 0;
+ return 0;
}
static void
writev_help(void)
{
- printf(
+ printf(
"\n"
" writes a range of bytes from the given offset source from multiple buffers\n"
"\n"
@@ -772,90 +790,91 @@ writev_help(void)
static int writev_f(int argc, char **argv);
static const cmdinfo_t writev_cmd = {
- .name = "writev",
- .cfunc = writev_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cq] [-P pattern ] off len [len..]",
- .oneline = "writes a number of bytes at a specified offset",
- .help = writev_help,
+ .name = "writev",
+ .cfunc = writev_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..]",
+ .oneline = "writes a number of bytes at a specified offset",
+ .help = writev_help,
};
-static int
-writev_f(int argc, char **argv)
+static int writev_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, qflag = 0;
- int c, cnt;
- char *buf;
- int64_t offset;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int nr_iov;
- int pattern = 0xcd;
- QEMUIOVector qiov;
-
- while ((c = getopt(argc, argv, "CqP:")) != EOF) {
- switch (c) {
- case 'C':
- Cflag = 1;
- break;
- case 'q':
- qflag = 1;
- break;
- case 'P':
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- default:
- return command_usage(&writev_cmd);
- }
- }
-
- if (optind > argc - 2)
- return command_usage(&writev_cmd);
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
- optind++;
-
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
- }
-
- nr_iov = argc - optind;
- buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern);
-
- gettimeofday(&t1, NULL);
- cnt = do_aio_writev(&qiov, offset, &total);
- gettimeofday(&t2, NULL);
-
- if (cnt < 0) {
- printf("writev failed: %s\n", strerror(-cnt));
- goto out;
- }
-
- if (qflag)
- goto out;
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag);
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ int pattern = 0xcd;
+ QEMUIOVector qiov;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ default:
+ return command_usage(&writev_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ return command_usage(&writev_cmd);
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern);
+
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_writev(&qiov, offset, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("writev failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (qflag) {
+ goto out;
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag);
out:
- qemu_io_free(buf);
- return 0;
+ qemu_io_free(buf);
+ return 0;
}
-static void
-multiwrite_help(void)
+static void multiwrite_help(void)
{
- printf(
+ printf(
"\n"
" writes a range of bytes from the given offset source from multiple buffers,\n"
" in a batch of requests that may be merged by qemu\n"
@@ -876,217 +895,215 @@ multiwrite_help(void)
static int multiwrite_f(int argc, char **argv);
static const cmdinfo_t multiwrite_cmd = {
- .name = "multiwrite",
- .cfunc = multiwrite_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]",
- .oneline = "issues multiple write requests at once",
- .help = multiwrite_help,
+ .name = "multiwrite",
+ .cfunc = multiwrite_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]",
+ .oneline = "issues multiple write requests at once",
+ .help = multiwrite_help,
};
-static int
-multiwrite_f(int argc, char **argv)
+static int multiwrite_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, qflag = 0;
- int c, cnt;
- char **buf;
- int64_t offset, first_offset = 0;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int nr_iov;
- int nr_reqs;
- int pattern = 0xcd;
- QEMUIOVector *qiovs;
- int i;
- BlockRequest *reqs;
-
- while ((c = getopt(argc, argv, "CqP:")) != EOF) {
- switch (c) {
- case 'C':
- Cflag = 1;
- break;
- case 'q':
- qflag = 1;
- break;
- case 'P':
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- default:
- return command_usage(&writev_cmd);
- }
- }
-
- if (optind > argc - 2)
- return command_usage(&writev_cmd);
-
- nr_reqs = 1;
- for (i = optind; i < argc; i++) {
- if (!strcmp(argv[i], ";")) {
- nr_reqs++;
- }
- }
-
- reqs = qemu_malloc(nr_reqs * sizeof(*reqs));
- buf = qemu_malloc(nr_reqs * sizeof(*buf));
- qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs));
-
- for (i = 0; i < nr_reqs; i++) {
- int j;
-
- /* Read the offset of the request */
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric offset argument -- %s\n", argv[optind]);
- return 0;
- }
- optind++;
-
- if (offset & 0x1ff) {
- printf("offset %lld is not sector aligned\n",
- (long long)offset);
- return 0;
- }
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, cnt;
+ char **buf;
+ int64_t offset, first_offset = 0;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ int nr_reqs;
+ int pattern = 0xcd;
+ QEMUIOVector *qiovs;
+ int i;
+ BlockRequest *reqs;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ default:
+ return command_usage(&writev_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ return command_usage(&writev_cmd);
+ }
+
+ nr_reqs = 1;
+ for (i = optind; i < argc; i++) {
+ if (!strcmp(argv[i], ";")) {
+ nr_reqs++;
+ }
+ }
+
+ reqs = qemu_malloc(nr_reqs * sizeof(*reqs));
+ buf = qemu_malloc(nr_reqs * sizeof(*buf));
+ qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs));
+
+ for (i = 0; i < nr_reqs; i++) {
+ int j;
+
+ /* Read the offset of the request */
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric offset argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %lld is not sector aligned\n",
+ (long long)offset);
+ return 0;
+ }
if (i == 0) {
first_offset = offset;
}
- /* Read lengths for qiov entries */
- for (j = optind; j < argc; j++) {
- if (!strcmp(argv[j], ";")) {
- break;
- }
- }
+ /* Read lengths for qiov entries */
+ for (j = optind; j < argc; j++) {
+ if (!strcmp(argv[j], ";")) {
+ break;
+ }
+ }
- nr_iov = j - optind;
+ nr_iov = j - optind;
- /* Build request */
- reqs[i].qiov = &qiovs[i];
- buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern);
- reqs[i].sector = offset >> 9;
- reqs[i].nb_sectors = reqs[i].qiov->size >> 9;
+ /* Build request */
+ reqs[i].qiov = &qiovs[i];
+ buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern);
+ reqs[i].sector = offset >> 9;
+ reqs[i].nb_sectors = reqs[i].qiov->size >> 9;
- optind = j + 1;
+ optind = j + 1;
- offset += reqs[i].qiov->size;
- pattern++;
- }
+ offset += reqs[i].qiov->size;
+ pattern++;
+ }
- gettimeofday(&t1, NULL);
- cnt = do_aio_multiwrite(reqs, nr_reqs, &total);
- gettimeofday(&t2, NULL);
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_multiwrite(reqs, nr_reqs, &total);
+ gettimeofday(&t2, NULL);
- if (cnt < 0) {
- printf("aio_multiwrite failed: %s\n", strerror(-cnt));
- goto out;
- }
+ if (cnt < 0) {
+ printf("aio_multiwrite failed: %s\n", strerror(-cnt));
+ goto out;
+ }
- if (qflag)
- goto out;
+ if (qflag) {
+ goto out;
+ }
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("wrote", &t2, first_offset, total, total, cnt, Cflag);
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, first_offset, total, total, cnt, Cflag);
out:
- for (i = 0; i < nr_reqs; i++) {
- qemu_io_free(buf[i]);
- qemu_iovec_destroy(&qiovs[i]);
- }
- qemu_free(buf);
- qemu_free(reqs);
- qemu_free(qiovs);
- return 0;
+ for (i = 0; i < nr_reqs; i++) {
+ qemu_io_free(buf[i]);
+ qemu_iovec_destroy(&qiovs[i]);
+ }
+ qemu_free(buf);
+ qemu_free(reqs);
+ qemu_free(qiovs);
+ return 0;
}
struct aio_ctx {
- QEMUIOVector qiov;
- int64_t offset;
- char *buf;
- int qflag;
- int vflag;
- int Cflag;
- int Pflag;
- int pattern;
- struct timeval t1;
+ QEMUIOVector qiov;
+ int64_t offset;
+ char *buf;
+ int qflag;
+ int vflag;
+ int Cflag;
+ int Pflag;
+ int pattern;
+ struct timeval t1;
};
-static void
-aio_write_done(void *opaque, int ret)
+static void aio_write_done(void *opaque, int ret)
{
- struct aio_ctx *ctx = opaque;
- struct timeval t2;
+ struct aio_ctx *ctx = opaque;
+ struct timeval t2;
- gettimeofday(&t2, NULL);
+ gettimeofday(&t2, NULL);
- if (ret < 0) {
- printf("aio_write failed: %s\n", strerror(-ret));
- goto out;
- }
+ if (ret < 0) {
+ printf("aio_write failed: %s\n", strerror(-ret));
+ goto out;
+ }
- if (ctx->qflag) {
- goto out;
- }
+ if (ctx->qflag) {
+ goto out;
+ }
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, ctx->t1);
- print_report("wrote", &t2, ctx->offset, ctx->qiov.size,
- ctx->qiov.size, 1, ctx->Cflag);
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, ctx->t1);
+ print_report("wrote", &t2, ctx->offset, ctx->qiov.size,
+ ctx->qiov.size, 1, ctx->Cflag);
out:
- qemu_io_free(ctx->buf);
- free(ctx);
+ qemu_io_free(ctx->buf);
+ free(ctx);
}
-static void
-aio_read_done(void *opaque, int ret)
+static void aio_read_done(void *opaque, int ret)
{
- struct aio_ctx *ctx = opaque;
- struct timeval t2;
-
- gettimeofday(&t2, NULL);
-
- if (ret < 0) {
- printf("readv failed: %s\n", strerror(-ret));
- goto out;
- }
-
- if (ctx->Pflag) {
- void *cmp_buf = malloc(ctx->qiov.size);
-
- memset(cmp_buf, ctx->pattern, ctx->qiov.size);
- if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) {
- printf("Pattern verification failed at offset %"
- PRId64 ", %zd bytes\n",
- ctx->offset, ctx->qiov.size);
- }
- free(cmp_buf);
- }
-
- if (ctx->qflag) {
- goto out;
- }
-
- if (ctx->vflag) {
- dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size);
- }
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, ctx->t1);
- print_report("read", &t2, ctx->offset, ctx->qiov.size,
- ctx->qiov.size, 1, ctx->Cflag);
+ struct aio_ctx *ctx = opaque;
+ struct timeval t2;
+
+ gettimeofday(&t2, NULL);
+
+ if (ret < 0) {
+ printf("readv failed: %s\n", strerror(-ret));
+ goto out;
+ }
+
+ if (ctx->Pflag) {
+ void *cmp_buf = malloc(ctx->qiov.size);
+
+ memset(cmp_buf, ctx->pattern, ctx->qiov.size);
+ if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %zd bytes\n", ctx->offset, ctx->qiov.size);
+ }
+ free(cmp_buf);
+ }
+
+ if (ctx->qflag) {
+ goto out;
+ }
+
+ if (ctx->vflag) {
+ dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size);
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, ctx->t1);
+ print_report("read", &t2, ctx->offset, ctx->qiov.size,
+ ctx->qiov.size, 1, ctx->Cflag);
out:
- qemu_io_free(ctx->buf);
- free(ctx);
+ qemu_io_free(ctx->buf);
+ free(ctx);
}
-static void
-aio_read_help(void)
+static void aio_read_help(void)
{
- printf(
+ printf(
"\n"
" asynchronously reads a range of bytes from the given offset\n"
"\n"
@@ -1107,88 +1124,86 @@ aio_read_help(void)
static int aio_read_f(int argc, char **argv);
static const cmdinfo_t aio_read_cmd = {
- .name = "aio_read",
- .cfunc = aio_read_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cqv] [-P pattern ] off len [len..]",
- .oneline = "asynchronously reads a number of bytes",
- .help = aio_read_help,
+ .name = "aio_read",
+ .cfunc = aio_read_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cqv] [-P pattern ] off len [len..]",
+ .oneline = "asynchronously reads a number of bytes",
+ .help = aio_read_help,
};
-static int
-aio_read_f(int argc, char **argv)
+static int aio_read_f(int argc, char **argv)
{
- int nr_iov, c;
- struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
- BlockDriverAIOCB *acb;
-
- while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
- switch (c) {
- case 'C':
- ctx->Cflag = 1;
- break;
- case 'P':
- ctx->Pflag = 1;
- ctx->pattern = parse_pattern(optarg);
- if (ctx->pattern < 0) {
- free(ctx);
- return 0;
- }
- break;
- case 'q':
- ctx->qflag = 1;
- break;
- case 'v':
- ctx->vflag = 1;
- break;
- default:
- free(ctx);
- return command_usage(&aio_read_cmd);
- }
- }
-
- if (optind > argc - 2) {
- free(ctx);
- return command_usage(&aio_read_cmd);
- }
-
- ctx->offset = cvtnum(argv[optind]);
- if (ctx->offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- free(ctx);
- return 0;
- }
- optind++;
-
- if (ctx->offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- ctx->offset);
- free(ctx);
- return 0;
- }
-
- nr_iov = argc - optind;
- ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab);
-
- gettimeofday(&ctx->t1, NULL);
- acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov,
- ctx->qiov.size >> 9, aio_read_done, ctx);
- if (!acb) {
- free(ctx->buf);
- free(ctx);
- return -EIO;
- }
-
- return 0;
+ int nr_iov, c;
+ struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
+ BlockDriverAIOCB *acb;
+
+ while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
+ switch (c) {
+ case 'C':
+ ctx->Cflag = 1;
+ break;
+ case 'P':
+ ctx->Pflag = 1;
+ ctx->pattern = parse_pattern(optarg);
+ if (ctx->pattern < 0) {
+ free(ctx);
+ return 0;
+ }
+ break;
+ case 'q':
+ ctx->qflag = 1;
+ break;
+ case 'v':
+ ctx->vflag = 1;
+ break;
+ default:
+ free(ctx);
+ return command_usage(&aio_read_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ free(ctx);
+ return command_usage(&aio_read_cmd);
+ }
+
+ ctx->offset = cvtnum(argv[optind]);
+ if (ctx->offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ free(ctx);
+ return 0;
+ }
+ optind++;
+
+ if (ctx->offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ ctx->offset);
+ free(ctx);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab);
+
+ gettimeofday(&ctx->t1, NULL);
+ acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov,
+ ctx->qiov.size >> 9, aio_read_done, ctx);
+ if (!acb) {
+ free(ctx->buf);
+ free(ctx);
+ return -EIO;
+ }
+
+ return 0;
}
-static void
-aio_write_help(void)
+static void aio_write_help(void)
{
- printf(
+ printf(
"\n"
-" asynchronously writes a range of bytes from the given offset source \n"
+" asynchronously writes a range of bytes from the given offset source\n"
" from multiple buffers\n"
"\n"
" Example:\n"
@@ -1207,199 +1222,196 @@ aio_write_help(void)
static int aio_write_f(int argc, char **argv);
static const cmdinfo_t aio_write_cmd = {
- .name = "aio_write",
- .cfunc = aio_write_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cq] [-P pattern ] off len [len..]",
- .oneline = "asynchronously writes a number of bytes",
- .help = aio_write_help,
+ .name = "aio_write",
+ .cfunc = aio_write_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..]",
+ .oneline = "asynchronously writes a number of bytes",
+ .help = aio_write_help,
};
-static int
-aio_write_f(int argc, char **argv)
+static int aio_write_f(int argc, char **argv)
{
- int nr_iov, c;
- int pattern = 0xcd;
- struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
- BlockDriverAIOCB *acb;
-
- while ((c = getopt(argc, argv, "CqP:")) != EOF) {
- switch (c) {
- case 'C':
- ctx->Cflag = 1;
- break;
- case 'q':
- ctx->qflag = 1;
- break;
- case 'P':
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- default:
- free(ctx);
- return command_usage(&aio_write_cmd);
- }
- }
-
- if (optind > argc - 2) {
- free(ctx);
- return command_usage(&aio_write_cmd);
- }
-
- ctx->offset = cvtnum(argv[optind]);
- if (ctx->offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- free(ctx);
- return 0;
- }
- optind++;
-
- if (ctx->offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- ctx->offset);
- free(ctx);
- return 0;
- }
-
- nr_iov = argc - optind;
- ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern);
-
- gettimeofday(&ctx->t1, NULL);
- acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov,
- ctx->qiov.size >> 9, aio_write_done, ctx);
- if (!acb) {
- free(ctx->buf);
- free(ctx);
- return -EIO;
- }
-
- return 0;
+ int nr_iov, c;
+ int pattern = 0xcd;
+ struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
+ BlockDriverAIOCB *acb;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ ctx->Cflag = 1;
+ break;
+ case 'q':
+ ctx->qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ default:
+ free(ctx);
+ return command_usage(&aio_write_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ free(ctx);
+ return command_usage(&aio_write_cmd);
+ }
+
+ ctx->offset = cvtnum(argv[optind]);
+ if (ctx->offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ free(ctx);
+ return 0;
+ }
+ optind++;
+
+ if (ctx->offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ ctx->offset);
+ free(ctx);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern);
+
+ gettimeofday(&ctx->t1, NULL);
+ acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov,
+ ctx->qiov.size >> 9, aio_write_done, ctx);
+ if (!acb) {
+ free(ctx->buf);
+ free(ctx);
+ return -EIO;
+ }
+
+ return 0;
}
-static int
-aio_flush_f(int argc, char **argv)
+static int aio_flush_f(int argc, char **argv)
{
- qemu_aio_flush();
- return 0;
+ qemu_aio_flush();
+ return 0;
}
static const cmdinfo_t aio_flush_cmd = {
- .name = "aio_flush",
- .cfunc = aio_flush_f,
- .oneline = "completes all outstanding aio requests"
+ .name = "aio_flush",
+ .cfunc = aio_flush_f,
+ .oneline = "completes all outstanding aio requests"
};
-static int
-flush_f(int argc, char **argv)
+static int flush_f(int argc, char **argv)
{
- bdrv_flush(bs);
- return 0;
+ bdrv_flush(bs);
+ return 0;
}
static const cmdinfo_t flush_cmd = {
- .name = "flush",
- .altname = "f",
- .cfunc = flush_f,
- .oneline = "flush all in-core file state to disk",
+ .name = "flush",
+ .altname = "f",
+ .cfunc = flush_f,
+ .oneline = "flush all in-core file state to disk",
};
-static int
-truncate_f(int argc, char **argv)
+static int truncate_f(int argc, char **argv)
{
- int64_t offset;
- int ret;
-
- offset = cvtnum(argv[1]);
- if (offset < 0) {
- printf("non-numeric truncate argument -- %s\n", argv[1]);
- return 0;
- }
-
- ret = bdrv_truncate(bs, offset);
- if (ret < 0) {
- printf("truncate: %s\n", strerror(-ret));
- return 0;
- }
-
- return 0;
+ int64_t offset;
+ int ret;
+
+ offset = cvtnum(argv[1]);
+ if (offset < 0) {
+ printf("non-numeric truncate argument -- %s\n", argv[1]);
+ return 0;
+ }
+
+ ret = bdrv_truncate(bs, offset);
+ if (ret < 0) {
+ printf("truncate: %s\n", strerror(-ret));
+ return 0;
+ }
+
+ return 0;
}
static const cmdinfo_t truncate_cmd = {
- .name = "truncate",
- .altname = "t",
- .cfunc = truncate_f,
- .argmin = 1,
- .argmax = 1,
- .args = "off",
- .oneline = "truncates the current file at the given offset",
+ .name = "truncate",
+ .altname = "t",
+ .cfunc = truncate_f,
+ .argmin = 1,
+ .argmax = 1,
+ .args = "off",
+ .oneline = "truncates the current file at the given offset",
};
-static int
-length_f(int argc, char **argv)
+static int length_f(int argc, char **argv)
{
- int64_t size;
- char s1[64];
-
- size = bdrv_getlength(bs);
- if (size < 0) {
- printf("getlength: %s\n", strerror(-size));
- return 0;
- }
-
- cvtstr(size, s1, sizeof(s1));
- printf("%s\n", s1);
- return 0;
+ int64_t size;
+ char s1[64];
+
+ size = bdrv_getlength(bs);
+ if (size < 0) {
+ printf("getlength: %s\n", strerror(-size));
+ return 0;
+ }
+
+ cvtstr(size, s1, sizeof(s1));
+ printf("%s\n", s1);
+ return 0;
}
static const cmdinfo_t length_cmd = {
- .name = "length",
- .altname = "l",
- .cfunc = length_f,
- .oneline = "gets the length of the current file",
+ .name = "length",
+ .altname = "l",
+ .cfunc = length_f,
+ .oneline = "gets the length of the current file",
};
-static int
-info_f(int argc, char **argv)
+static int info_f(int argc, char **argv)
{
- BlockDriverInfo bdi;
- char s1[64], s2[64];
- int ret;
+ BlockDriverInfo bdi;
+ char s1[64], s2[64];
+ int ret;
- if (bs->drv && bs->drv->format_name)
- printf("format name: %s\n", bs->drv->format_name);
- if (bs->drv && bs->drv->protocol_name)
- printf("format name: %s\n", bs->drv->protocol_name);
+ if (bs->drv && bs->drv->format_name) {
+ printf("format name: %s\n", bs->drv->format_name);
+ }
+ if (bs->drv && bs->drv->protocol_name) {
+ printf("format name: %s\n", bs->drv->protocol_name);
+ }
- ret = bdrv_get_info(bs, &bdi);
- if (ret)
- return 0;
+ ret = bdrv_get_info(bs, &bdi);
+ if (ret) {
+ return 0;
+ }
- cvtstr(bdi.cluster_size, s1, sizeof(s1));
- cvtstr(bdi.vm_state_offset, s2, sizeof(s2));
+ cvtstr(bdi.cluster_size, s1, sizeof(s1));
+ cvtstr(bdi.vm_state_offset, s2, sizeof(s2));
- printf("cluster size: %s\n", s1);
- printf("vm state offset: %s\n", s2);
+ printf("cluster size: %s\n", s1);
+ printf("vm state offset: %s\n", s2);
- return 0;
+ return 0;
}
static const cmdinfo_t info_cmd = {
- .name = "info",
- .altname = "i",
- .cfunc = info_f,
- .oneline = "prints information about the current file",
+ .name = "info",
+ .altname = "i",
+ .cfunc = info_f,
+ .oneline = "prints information about the current file",
};
-static void
-discard_help(void)
+static void discard_help(void)
{
- printf(
+ printf(
"\n"
" discards a range of bytes from the given offset\n"
"\n"
@@ -1415,148 +1427,147 @@ discard_help(void)
static int discard_f(int argc, char **argv);
static const cmdinfo_t discard_cmd = {
- .name = "discard",
- .altname = "d",
- .cfunc = discard_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cq] off len",
- .oneline = "discards a number of bytes at a specified offset",
- .help = discard_help,
+ .name = "discard",
+ .altname = "d",
+ .cfunc = discard_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] off len",
+ .oneline = "discards a number of bytes at a specified offset",
+ .help = discard_help,
};
-static int
-discard_f(int argc, char **argv)
+static int discard_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, qflag = 0;
- int c, ret;
- int64_t offset;
- int count;
-
- while ((c = getopt(argc, argv, "Cq")) != EOF) {
- switch (c) {
- case 'C':
- Cflag = 1;
- break;
- case 'q':
- qflag = 1;
- break;
- default:
- return command_usage(&discard_cmd);
- }
- }
-
- if (optind != argc - 2) {
- return command_usage(&discard_cmd);
- }
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- optind++;
- count = cvtnum(argv[optind]);
- if (count < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- gettimeofday(&t1, NULL);
- ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, count >> BDRV_SECTOR_BITS);
- gettimeofday(&t2, NULL);
-
- if (ret < 0) {
- printf("discard failed: %s\n", strerror(-ret));
- goto out;
- }
-
- /* Finally, report back -- -C gives a parsable format */
- if (!qflag) {
- t2 = tsub(t2, t1);
- print_report("discard", &t2, offset, count, count, 1, Cflag);
- }
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, ret;
+ int64_t offset;
+ int count;
+
+ while ((c = getopt(argc, argv, "Cq")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ default:
+ return command_usage(&discard_cmd);
+ }
+ }
+
+ if (optind != argc - 2) {
+ return command_usage(&discard_cmd);
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ gettimeofday(&t1, NULL);
+ ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS,
+ count >> BDRV_SECTOR_BITS);
+ gettimeofday(&t2, NULL);
+
+ if (ret < 0) {
+ printf("discard failed: %s\n", strerror(-ret));
+ goto out;
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ if (!qflag) {
+ t2 = tsub(t2, t1);
+ print_report("discard", &t2, offset, count, count, 1, Cflag);
+ }
out:
- return 0;
+ return 0;
}
-static int
-alloc_f(int argc, char **argv)
+static int alloc_f(int argc, char **argv)
{
- int64_t offset;
- int nb_sectors, remaining;
- char s1[64];
- int num, sum_alloc;
- int ret;
-
- offset = cvtnum(argv[1]);
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
- }
-
- if (argc == 3)
- nb_sectors = cvtnum(argv[2]);
- else
- nb_sectors = 1;
-
- remaining = nb_sectors;
- sum_alloc = 0;
- while (remaining) {
- ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num);
- remaining -= num;
- if (ret) {
- sum_alloc += num;
- }
- }
-
- cvtstr(offset, s1, sizeof(s1));
-
- printf("%d/%d sectors allocated at offset %s\n",
- sum_alloc, nb_sectors, s1);
- return 0;
+ int64_t offset;
+ int nb_sectors, remaining;
+ char s1[64];
+ int num, sum_alloc;
+ int ret;
+
+ offset = cvtnum(argv[1]);
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ if (argc == 3) {
+ nb_sectors = cvtnum(argv[2]);
+ } else {
+ nb_sectors = 1;
+ }
+
+ remaining = nb_sectors;
+ sum_alloc = 0;
+ while (remaining) {
+ ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num);
+ remaining -= num;
+ if (ret) {
+ sum_alloc += num;
+ }
+ }
+
+ cvtstr(offset, s1, sizeof(s1));
+
+ printf("%d/%d sectors allocated at offset %s\n",
+ sum_alloc, nb_sectors, s1);
+ return 0;
}
static const cmdinfo_t alloc_cmd = {
- .name = "alloc",
- .altname = "a",
- .argmin = 1,
- .argmax = 2,
- .cfunc = alloc_f,
- .args = "off [sectors]",
- .oneline = "checks if a sector is present in the file",
+ .name = "alloc",
+ .altname = "a",
+ .argmin = 1,
+ .argmax = 2,
+ .cfunc = alloc_f,
+ .args = "off [sectors]",
+ .oneline = "checks if a sector is present in the file",
};
-static int
-map_f(int argc, char **argv)
+static int map_f(int argc, char **argv)
{
- int64_t offset;
- int64_t nb_sectors;
- char s1[64];
- int num, num_checked;
- int ret;
- const char *retstr;
-
- offset = 0;
- nb_sectors = bs->total_sectors;
-
- do {
- num_checked = MIN(nb_sectors, INT_MAX);
- ret = bdrv_is_allocated(bs, offset, num_checked, &num);
- retstr = ret ? " allocated" : "not allocated";
- cvtstr(offset << 9ULL, s1, sizeof(s1));
- printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n",
- offset << 9ULL, num, num_checked, retstr, s1, ret);
-
- offset += num;
- nb_sectors -= num;
- } while(offset < bs->total_sectors);
-
- return 0;
+ int64_t offset;
+ int64_t nb_sectors;
+ char s1[64];
+ int num, num_checked;
+ int ret;
+ const char *retstr;
+
+ offset = 0;
+ nb_sectors = bs->total_sectors;
+
+ do {
+ num_checked = MIN(nb_sectors, INT_MAX);
+ ret = bdrv_is_allocated(bs, offset, num_checked, &num);
+ retstr = ret ? " allocated" : "not allocated";
+ cvtstr(offset << 9ULL, s1, sizeof(s1));
+ printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n",
+ offset << 9ULL, num, num_checked, retstr, s1, ret);
+
+ offset += num;
+ nb_sectors -= num;
+ } while (offset < bs->total_sectors);
+
+ return 0;
}
static const cmdinfo_t map_cmd = {
@@ -1569,50 +1580,48 @@ static const cmdinfo_t map_cmd = {
};
-static int
-close_f(int argc, char **argv)
+static int close_f(int argc, char **argv)
{
- bdrv_close(bs);
- bs = NULL;
- return 0;
+ bdrv_close(bs);
+ bs = NULL;
+ return 0;
}
static const cmdinfo_t close_cmd = {
- .name = "close",
- .altname = "c",
- .cfunc = close_f,
- .oneline = "close the current open file",
+ .name = "close",
+ .altname = "c",
+ .cfunc = close_f,
+ .oneline = "close the current open file",
};
static int openfile(char *name, int flags, int growable)
{
- if (bs) {
- fprintf(stderr, "file open already, try 'help close'\n");
- return 1;
- }
-
- if (growable) {
- if (bdrv_file_open(&bs, name, flags)) {
- fprintf(stderr, "%s: can't open device %s\n", progname, name);
- return 1;
- }
- } else {
- bs = bdrv_new("hda");
-
- if (bdrv_open(bs, name, flags, NULL) < 0) {
- fprintf(stderr, "%s: can't open device %s\n", progname, name);
- bs = NULL;
- return 1;
- }
- }
-
- return 0;
+ if (bs) {
+ fprintf(stderr, "file open already, try 'help close'\n");
+ return 1;
+ }
+
+ if (growable) {
+ if (bdrv_file_open(&bs, name, flags)) {
+ fprintf(stderr, "%s: can't open device %s\n", progname, name);
+ return 1;
+ }
+ } else {
+ bs = bdrv_new("hda");
+
+ if (bdrv_open(bs, name, flags, NULL) < 0) {
+ fprintf(stderr, "%s: can't open device %s\n", progname, name);
+ bs = NULL;
+ return 1;
+ }
+ }
+
+ return 0;
}
-static void
-open_help(void)
+static void open_help(void)
{
- printf(
+ printf(
"\n"
" opens a new file in the requested mode\n"
"\n"
@@ -1630,80 +1639,78 @@ open_help(void)
static int open_f(int argc, char **argv);
static const cmdinfo_t open_cmd = {
- .name = "open",
- .altname = "o",
- .cfunc = open_f,
- .argmin = 1,
- .argmax = -1,
- .flags = CMD_NOFILE_OK,
- .args = "[-Crsn] [path]",
- .oneline = "open the file specified by path",
- .help = open_help,
+ .name = "open",
+ .altname = "o",
+ .cfunc = open_f,
+ .argmin = 1,
+ .argmax = -1,
+ .flags = CMD_NOFILE_OK,
+ .args = "[-Crsn] [path]",
+ .oneline = "open the file specified by path",
+ .help = open_help,
};
-static int
-open_f(int argc, char **argv)
+static int open_f(int argc, char **argv)
{
- int flags = 0;
- int readonly = 0;
- int growable = 0;
- int c;
-
- while ((c = getopt(argc, argv, "snrg")) != EOF) {
- switch (c) {
- case 's':
- flags |= BDRV_O_SNAPSHOT;
- break;
- case 'n':
- flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
- break;
- case 'r':
- readonly = 1;
- break;
- case 'g':
- growable = 1;
- break;
- default:
- return command_usage(&open_cmd);
- }
- }
-
- if (!readonly) {
- flags |= BDRV_O_RDWR;
+ int flags = 0;
+ int readonly = 0;
+ int growable = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "snrg")) != EOF) {
+ switch (c) {
+ case 's':
+ flags |= BDRV_O_SNAPSHOT;
+ break;
+ case 'n':
+ flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
+ break;
+ case 'r':
+ readonly = 1;
+ break;
+ case 'g':
+ growable = 1;
+ break;
+ default:
+ return command_usage(&open_cmd);
}
+ }
+
+ if (!readonly) {
+ flags |= BDRV_O_RDWR;
+ }
- if (optind != argc - 1)
- return command_usage(&open_cmd);
+ if (optind != argc - 1) {
+ return command_usage(&open_cmd);
+ }
- return openfile(argv[optind], flags, growable);
+ return openfile(argv[optind], flags, growable);
}
-static int
-init_args_command(
- int index)
+static int init_args_command(int index)
{
- /* only one device allowed so far */
- if (index >= 1)
- return 0;
- return ++index;
+ /* only one device allowed so far */
+ if (index >= 1) {
+ return 0;
+ }
+ return ++index;
}
-static int
-init_check_command(
- const cmdinfo_t *ct)
+static int init_check_command(const cmdinfo_t *ct)
{
- if (ct->flags & CMD_FLAG_GLOBAL)
- return 1;
- if (!(ct->flags & CMD_NOFILE_OK) && !bs) {
- fprintf(stderr, "no file open, try 'help open'\n");
- return 0;
- }
- return 1;
+ if (ct->flags & CMD_FLAG_GLOBAL) {
+ return 1;
+ }
+ if (!(ct->flags & CMD_NOFILE_OK) && !bs) {
+ fprintf(stderr, "no file open, try 'help open'\n");
+ return 0;
+ }
+ return 1;
}
static void usage(const char *name)
{
- printf(
+ printf(
"Usage: %s [-h] [-V] [-rsnm] [-c cmd] ... [file]\n"
"QEMU Disk exerciser\n"
"\n"
@@ -1717,115 +1724,117 @@ static void usage(const char *name)
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
"\n",
- name);
+ name);
}
int main(int argc, char **argv)
{
- int readonly = 0;
- int growable = 0;
- const char *sopt = "hVc:rsnmgk";
- const struct option lopt[] = {
- { "help", 0, NULL, 'h' },
- { "version", 0, NULL, 'V' },
- { "offset", 1, NULL, 'o' },
- { "cmd", 1, NULL, 'c' },
- { "read-only", 0, NULL, 'r' },
- { "snapshot", 0, NULL, 's' },
- { "nocache", 0, NULL, 'n' },
- { "misalign", 0, NULL, 'm' },
- { "growable", 0, NULL, 'g' },
- { "native-aio", 0, NULL, 'k' },
- { NULL, 0, NULL, 0 }
- };
- int c;
- int opt_index = 0;
- int flags = 0;
-
- progname = basename(argv[0]);
-
- while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
- switch (c) {
- case 's':
- flags |= BDRV_O_SNAPSHOT;
- break;
- case 'n':
- flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
- break;
- case 'c':
- add_user_command(optarg);
- break;
- case 'r':
- readonly = 1;
- break;
- case 'm':
- misalign = 1;
- break;
- case 'g':
- growable = 1;
- break;
- case 'k':
- flags |= BDRV_O_NATIVE_AIO;
- break;
- case 'V':
- printf("%s version %s\n", progname, VERSION);
- exit(0);
- case 'h':
- usage(progname);
- exit(0);
- default:
- usage(progname);
- exit(1);
- }
- }
-
- if ((argc - optind) > 1) {
- usage(progname);
- exit(1);
- }
-
- bdrv_init();
-
- /* initialize commands */
- quit_init();
- help_init();
- add_command(&open_cmd);
- add_command(&close_cmd);
- add_command(&read_cmd);
- add_command(&readv_cmd);
- add_command(&write_cmd);
- add_command(&writev_cmd);
- add_command(&multiwrite_cmd);
- add_command(&aio_read_cmd);
- add_command(&aio_write_cmd);
- add_command(&aio_flush_cmd);
- add_command(&flush_cmd);
- add_command(&truncate_cmd);
- add_command(&length_cmd);
- add_command(&info_cmd);
- add_command(&discard_cmd);
- add_command(&alloc_cmd);
- add_command(&map_cmd);
-
- add_args_command(init_args_command);
- add_check_command(init_check_command);
-
- /* open the device */
- if (!readonly) {
- flags |= BDRV_O_RDWR;
+ int readonly = 0;
+ int growable = 0;
+ const char *sopt = "hVc:rsnmgk";
+ const struct option lopt[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "offset", 1, NULL, 'o' },
+ { "cmd", 1, NULL, 'c' },
+ { "read-only", 0, NULL, 'r' },
+ { "snapshot", 0, NULL, 's' },
+ { "nocache", 0, NULL, 'n' },
+ { "misalign", 0, NULL, 'm' },
+ { "growable", 0, NULL, 'g' },
+ { "native-aio", 0, NULL, 'k' },
+ { NULL, 0, NULL, 0 }
+ };
+ int c;
+ int opt_index = 0;
+ int flags = 0;
+
+ progname = basename(argv[0]);
+
+ while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
+ switch (c) {
+ case 's':
+ flags |= BDRV_O_SNAPSHOT;
+ break;
+ case 'n':
+ flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
+ break;
+ case 'c':
+ add_user_command(optarg);
+ break;
+ case 'r':
+ readonly = 1;
+ break;
+ case 'm':
+ misalign = 1;
+ break;
+ case 'g':
+ growable = 1;
+ break;
+ case 'k':
+ flags |= BDRV_O_NATIVE_AIO;
+ break;
+ case 'V':
+ printf("%s version %s\n", progname, VERSION);
+ exit(0);
+ case 'h':
+ usage(progname);
+ exit(0);
+ default:
+ usage(progname);
+ exit(1);
}
+ }
+
+ if ((argc - optind) > 1) {
+ usage(progname);
+ exit(1);
+ }
- if ((argc - optind) == 1)
- openfile(argv[optind], flags, growable);
- command_loop();
+ bdrv_init();
+
+ /* initialize commands */
+ quit_init();
+ help_init();
+ add_command(&open_cmd);
+ add_command(&close_cmd);
+ add_command(&read_cmd);
+ add_command(&readv_cmd);
+ add_command(&write_cmd);
+ add_command(&writev_cmd);
+ add_command(&multiwrite_cmd);
+ add_command(&aio_read_cmd);
+ add_command(&aio_write_cmd);
+ add_command(&aio_flush_cmd);
+ add_command(&flush_cmd);
+ add_command(&truncate_cmd);
+ add_command(&length_cmd);
+ add_command(&info_cmd);
+ add_command(&discard_cmd);
+ add_command(&alloc_cmd);
+ add_command(&map_cmd);
+
+ add_args_command(init_args_command);
+ add_check_command(init_check_command);
+
+ /* open the device */
+ if (!readonly) {
+ flags |= BDRV_O_RDWR;
+ }
+
+ if ((argc - optind) == 1) {
+ openfile(argv[optind], flags, growable);
+ }
+ command_loop();
- /*
- * Make sure all outstanding requests get flushed the program exits.
- */
- qemu_aio_flush();
+ /*
+ * Make sure all outstanding requests get flushed the program exits.
+ */
+ qemu_aio_flush();
- if (bs)
- bdrv_close(bs);
- return 0;
+ if (bs) {
+ bdrv_close(bs);
+ }
+ return 0;
}
diff --git a/qemu-options.hx b/qemu-options.hx
index e6d7adc3af..64114dd448 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -160,6 +160,14 @@ an untrusted format header.
This option specifies the serial number to assign to the device.
@item addr=@var{addr}
Specify the controller's PCI address (if=virtio only).
+@item werror=@var{action},rerror=@var{action}
+Specify which @var{action} to take on write and read errors. Valid actions are:
+"ignore" (ignore the error and try to continue), "stop" (pause QEMU),
+"report" (report the error to the guest), "enospc" (pause QEMU only if the
+host disk is full; report the error to the guest otherwise).
+The default setting is @option{werror=enospc} and @option{rerror=report}.
+@item readonly
+Open drive @option{file} as read-only. Guest write attempts will fail.
@end table
By default, writethrough caching is used for all block device. This means that
diff --git a/qerror.c b/qerror.c
index d7fcd93cad..229d0d63e3 100644
--- a/qerror.c
+++ b/qerror.c
@@ -141,6 +141,11 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Invalid JSON syntax",
},
{
+ .error_fmt = QERR_JSON_PARSE_ERROR,
+ .desc = "JSON parse error, %(message)",
+
+ },
+ {
.error_fmt = QERR_KVM_MISSING_CAP,
.desc = "Using KVM without %(capability), %(feature) unavailable",
},
@@ -213,6 +218,14 @@ static const QErrorStringTable qerror_table[] = {
.error_fmt = QERR_VNC_SERVER_FAILED,
.desc = "Could not start VNC server on %(target)",
},
+ {
+ .error_fmt = QERR_QGA_LOGGING_FAILED,
+ .desc = "Guest agent failed to log non-optional log statement",
+ },
+ {
+ .error_fmt = QERR_QGA_COMMAND_FAILED,
+ .desc = "Guest agent command failed, error was '%(message)'",
+ },
{}
};
diff --git a/qerror.h b/qerror.h
index 16c830d8b7..7ec0fc12d7 100644
--- a/qerror.h
+++ b/qerror.h
@@ -124,6 +124,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_JSON_PARSE_ERROR \
"{ 'class': 'JSONParseError', 'data': { 'message': %s } }"
+#define QERR_BUFFER_OVERRUN \
+ "{ 'class': 'BufferOverrun', 'data': {} }"
+
#define QERR_KVM_MISSING_CAP \
"{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }"
@@ -181,4 +184,10 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_FEATURE_DISABLED \
"{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }"
+#define QERR_QGA_LOGGING_FAILED \
+ "{ 'class': 'QgaLoggingFailed', 'data': {} }"
+
+#define QERR_QGA_COMMAND_FAILED \
+ "{ 'class': 'QgaCommandFailed', 'data': { 'message': %s } }"
+
#endif /* QERROR_H */
diff --git a/qga/guest-agent-command-state.c b/qga/guest-agent-command-state.c
new file mode 100644
index 0000000000..bc6e0bd4a8
--- /dev/null
+++ b/qga/guest-agent-command-state.c
@@ -0,0 +1,73 @@
+/*
+ * QEMU Guest Agent command state interfaces
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.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 <glib.h>
+#include "qga/guest-agent-core.h"
+
+struct GACommandState {
+ GSList *groups;
+};
+
+typedef struct GACommandGroup {
+ void (*init)(void);
+ void (*cleanup)(void);
+} GACommandGroup;
+
+/* handle init/cleanup for stateful guest commands */
+
+void ga_command_state_add(GACommandState *cs,
+ void (*init)(void),
+ void (*cleanup)(void))
+{
+ GACommandGroup *cg = qemu_mallocz(sizeof(GACommandGroup));
+ cg->init = init;
+ cg->cleanup = cleanup;
+ cs->groups = g_slist_append(cs->groups, cg);
+}
+
+static void ga_command_group_init(gpointer opaque, gpointer unused)
+{
+ GACommandGroup *cg = opaque;
+
+ g_assert(cg);
+ if (cg->init) {
+ cg->init();
+ }
+}
+
+void ga_command_state_init_all(GACommandState *cs)
+{
+ g_assert(cs);
+ g_slist_foreach(cs->groups, ga_command_group_init, NULL);
+}
+
+static void ga_command_group_cleanup(gpointer opaque, gpointer unused)
+{
+ GACommandGroup *cg = opaque;
+
+ g_assert(cg);
+ if (cg->cleanup) {
+ cg->cleanup();
+ }
+}
+
+void ga_command_state_cleanup_all(GACommandState *cs)
+{
+ g_assert(cs);
+ g_slist_foreach(cs->groups, ga_command_group_cleanup, NULL);
+}
+
+GACommandState *ga_command_state_new(void)
+{
+ GACommandState *cs = qemu_mallocz(sizeof(GACommandState));
+ cs->groups = NULL;
+ return cs;
+}
diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c
new file mode 100644
index 0000000000..8c0d67ee28
--- /dev/null
+++ b/qga/guest-agent-commands.c
@@ -0,0 +1,518 @@
+/*
+ * QEMU Guest Agent commands
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.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 <glib.h>
+#include <mntent.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include "qga/guest-agent-core.h"
+#include "qga-qmp-commands.h"
+#include "qerror.h"
+#include "qemu-queue.h"
+
+static GAState *ga_state;
+
+static void disable_logging(void)
+{
+ ga_disable_logging(ga_state);
+}
+
+static void enable_logging(void)
+{
+ ga_enable_logging(ga_state);
+}
+
+/* Note: in some situations, like with the fsfreeze, logging may be
+ * temporarilly disabled. if it is necessary that a command be able
+ * to log for accounting purposes, check ga_logging_enabled() beforehand,
+ * and use the QERR_QGA_LOGGING_DISABLED to generate an error
+ */
+static void slog(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
+ va_end(ap);
+}
+
+int64_t qmp_guest_sync(int64_t id, Error **errp)
+{
+ return id;
+}
+
+void qmp_guest_ping(Error **err)
+{
+ slog("guest-ping called");
+}
+
+struct GuestAgentInfo *qmp_guest_info(Error **err)
+{
+ GuestAgentInfo *info = qemu_mallocz(sizeof(GuestAgentInfo));
+
+ info->version = g_strdup(QGA_VERSION);
+
+ return info;
+}
+
+void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
+{
+ int ret;
+ const char *shutdown_flag;
+
+ slog("guest-shutdown called, mode: %s", mode);
+ if (!has_mode || strcmp(mode, "powerdown") == 0) {
+ shutdown_flag = "-P";
+ } else if (strcmp(mode, "halt") == 0) {
+ shutdown_flag = "-H";
+ } else if (strcmp(mode, "reboot") == 0) {
+ shutdown_flag = "-r";
+ } else {
+ error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
+ "halt|powerdown|reboot");
+ return;
+ }
+
+ ret = fork();
+ if (ret == 0) {
+ /* child, start the shutdown */
+ setsid();
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+
+ ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
+ "hypervisor initiated shutdown", (char*)NULL);
+ if (ret) {
+ slog("guest-shutdown failed: %s", strerror(errno));
+ }
+ exit(!!ret);
+ } else if (ret < 0) {
+ error_set(err, QERR_UNDEFINED_ERROR);
+ }
+}
+
+typedef struct GuestFileHandle {
+ uint64_t id;
+ FILE *fh;
+ QTAILQ_ENTRY(GuestFileHandle) next;
+} GuestFileHandle;
+
+static struct {
+ QTAILQ_HEAD(, GuestFileHandle) filehandles;
+} guest_file_state;
+
+static void guest_file_handle_add(FILE *fh)
+{
+ GuestFileHandle *gfh;
+
+ gfh = qemu_mallocz(sizeof(GuestFileHandle));
+ gfh->id = fileno(fh);
+ gfh->fh = fh;
+ QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
+}
+
+static GuestFileHandle *guest_file_handle_find(int64_t id)
+{
+ GuestFileHandle *gfh;
+
+ QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
+ {
+ if (gfh->id == id) {
+ return gfh;
+ }
+ }
+
+ return NULL;
+}
+
+int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
+{
+ FILE *fh;
+ int fd;
+ int64_t ret = -1;
+
+ if (!has_mode) {
+ mode = "r";
+ }
+ slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
+ fh = fopen(path, mode);
+ if (!fh) {
+ error_set(err, QERR_OPEN_FILE_FAILED, path);
+ return -1;
+ }
+
+ /* set fd non-blocking to avoid common use cases (like reading from a
+ * named pipe) from hanging the agent
+ */
+ fd = fileno(fh);
+ ret = fcntl(fd, F_GETFL);
+ ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
+ if (ret == -1) {
+ error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
+ fclose(fh);
+ return -1;
+ }
+
+ guest_file_handle_add(fh);
+ slog("guest-file-open, handle: %d", fd);
+ return fd;
+}
+
+void qmp_guest_file_close(int64_t handle, Error **err)
+{
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ int ret;
+
+ slog("guest-file-close called, handle: %ld", handle);
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return;
+ }
+
+ ret = fclose(gfh->fh);
+ if (ret == -1) {
+ error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
+ return;
+ }
+
+ QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
+ qemu_free(gfh);
+}
+
+struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
+ int64_t count, Error **err)
+{
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileRead *read_data = NULL;
+ guchar *buf;
+ FILE *fh;
+ size_t read_count;
+
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return NULL;
+ }
+
+ if (!has_count) {
+ count = QGA_READ_COUNT_DEFAULT;
+ } else if (count < 0) {
+ error_set(err, QERR_INVALID_PARAMETER, "count");
+ return NULL;
+ }
+
+ fh = gfh->fh;
+ buf = qemu_mallocz(count+1);
+ read_count = fread(buf, 1, count, fh);
+ if (ferror(fh)) {
+ slog("guest-file-read failed, handle: %ld", handle);
+ error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
+ } else {
+ buf[read_count] = 0;
+ read_data = qemu_mallocz(sizeof(GuestFileRead));
+ read_data->count = read_count;
+ read_data->eof = feof(fh);
+ if (read_count) {
+ read_data->buf_b64 = g_base64_encode(buf, read_count);
+ }
+ }
+ qemu_free(buf);
+ clearerr(fh);
+
+ return read_data;
+}
+
+GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
+ bool has_count, int64_t count, Error **err)
+{
+ GuestFileWrite *write_data = NULL;
+ guchar *buf;
+ gsize buf_len;
+ int write_count;
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ FILE *fh;
+
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return NULL;
+ }
+
+ fh = gfh->fh;
+ buf = g_base64_decode(buf_b64, &buf_len);
+
+ if (!has_count) {
+ count = buf_len;
+ } else if (count < 0 || count > buf_len) {
+ qemu_free(buf);
+ error_set(err, QERR_INVALID_PARAMETER, "count");
+ return NULL;
+ }
+
+ write_count = fwrite(buf, 1, count, fh);
+ if (ferror(fh)) {
+ slog("guest-file-write failed, handle: %ld", handle);
+ error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
+ } else {
+ write_data = qemu_mallocz(sizeof(GuestFileWrite));
+ write_data->count = write_count;
+ write_data->eof = feof(fh);
+ }
+ qemu_free(buf);
+ clearerr(fh);
+
+ return write_data;
+}
+
+struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
+ int64_t whence, Error **err)
+{
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileSeek *seek_data = NULL;
+ FILE *fh;
+ int ret;
+
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return NULL;
+ }
+
+ fh = gfh->fh;
+ ret = fseek(fh, offset, whence);
+ if (ret == -1) {
+ error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+ } else {
+ seek_data = qemu_mallocz(sizeof(GuestFileRead));
+ seek_data->position = ftell(fh);
+ seek_data->eof = feof(fh);
+ }
+ clearerr(fh);
+
+ return seek_data;
+}
+
+void qmp_guest_file_flush(int64_t handle, Error **err)
+{
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ FILE *fh;
+ int ret;
+
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return;
+ }
+
+ fh = gfh->fh;
+ ret = fflush(fh);
+ if (ret == EOF) {
+ error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+ }
+}
+
+static void guest_file_init(void)
+{
+ QTAILQ_INIT(&guest_file_state.filehandles);
+}
+
+typedef struct GuestFsfreezeMount {
+ char *dirname;
+ char *devtype;
+ QTAILQ_ENTRY(GuestFsfreezeMount) next;
+} GuestFsfreezeMount;
+
+struct {
+ GuestFsfreezeStatus status;
+ QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
+} guest_fsfreeze_state;
+
+/*
+ * Walk the mount table and build a list of local file systems
+ */
+static int guest_fsfreeze_build_mount_list(void)
+{
+ struct mntent *ment;
+ GuestFsfreezeMount *mount, *temp;
+ char const *mtab = MOUNTED;
+ FILE *fp;
+
+ QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+ QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
+ qemu_free(mount->dirname);
+ qemu_free(mount->devtype);
+ qemu_free(mount);
+ }
+
+ fp = setmntent(mtab, "r");
+ if (!fp) {
+ g_warning("fsfreeze: unable to read mtab");
+ return -1;
+ }
+
+ while ((ment = getmntent(fp))) {
+ /*
+ * An entry which device name doesn't start with a '/' is
+ * either a dummy file system or a network file system.
+ * Add special handling for smbfs and cifs as is done by
+ * coreutils as well.
+ */
+ if ((ment->mnt_fsname[0] != '/') ||
+ (strcmp(ment->mnt_type, "smbfs") == 0) ||
+ (strcmp(ment->mnt_type, "cifs") == 0)) {
+ continue;
+ }
+
+ mount = qemu_mallocz(sizeof(GuestFsfreezeMount));
+ mount->dirname = qemu_strdup(ment->mnt_dir);
+ mount->devtype = qemu_strdup(ment->mnt_type);
+
+ QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next);
+ }
+
+ endmntent(fp);
+
+ return 0;
+}
+
+/*
+ * Return status of freeze/thaw
+ */
+GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
+{
+ return guest_fsfreeze_state.status;
+}
+
+/*
+ * Walk list of mounted file systems in the guest, and freeze the ones which
+ * are real local file systems.
+ */
+int64_t qmp_guest_fsfreeze_freeze(Error **err)
+{
+ int ret = 0, i = 0;
+ struct GuestFsfreezeMount *mount, *temp;
+ int fd;
+ char err_msg[512];
+
+ slog("guest-fsfreeze called");
+
+ if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
+ return 0;
+ }
+
+ ret = guest_fsfreeze_build_mount_list();
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* cannot risk guest agent blocking itself on a write in this state */
+ disable_logging();
+
+ QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+ fd = qemu_open(mount->dirname, O_RDONLY);
+ if (fd == -1) {
+ sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
+ error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ goto error;
+ }
+
+ /* we try to cull filesytems we know won't work in advance, but other
+ * filesytems may not implement fsfreeze for less obvious reasons.
+ * these will report EOPNOTSUPP, so we simply ignore them. when
+ * thawing, these filesystems will return an EINVAL instead, due to
+ * not being in a frozen state. Other filesystem-specific
+ * errors may result in EINVAL, however, so the user should check the
+ * number * of filesystems returned here against those returned by the
+ * thaw operation to determine whether everything completed
+ * successfully
+ */
+ ret = ioctl(fd, FIFREEZE);
+ if (ret < 0 && errno != EOPNOTSUPP) {
+ sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
+ error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ close(fd);
+ goto error;
+ }
+ close(fd);
+
+ i++;
+ }
+
+ guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
+ return i;
+
+error:
+ if (i > 0) {
+ qmp_guest_fsfreeze_thaw(NULL);
+ }
+ return 0;
+}
+
+/*
+ * Walk list of frozen file systems in the guest, and thaw them.
+ */
+int64_t qmp_guest_fsfreeze_thaw(Error **err)
+{
+ int ret;
+ GuestFsfreezeMount *mount, *temp;
+ int fd, i = 0;
+ bool has_error = false;
+
+ QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+ fd = qemu_open(mount->dirname, O_RDONLY);
+ if (fd == -1) {
+ has_error = true;
+ continue;
+ }
+ ret = ioctl(fd, FITHAW);
+ if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
+ has_error = true;
+ close(fd);
+ continue;
+ }
+ close(fd);
+ i++;
+ }
+
+ if (has_error) {
+ guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
+ } else {
+ guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
+ }
+ enable_logging();
+ return i;
+}
+
+static void guest_fsfreeze_init(void)
+{
+ guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
+ QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
+}
+
+static void guest_fsfreeze_cleanup(void)
+{
+ int64_t ret;
+ Error *err = NULL;
+
+ if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
+ ret = qmp_guest_fsfreeze_thaw(&err);
+ if (ret < 0 || err) {
+ slog("failed to clean up frozen filesystems");
+ }
+ }
+}
+
+/* register init/cleanup routines for stateful command groups */
+void ga_command_state_init(GAState *s, GACommandState *cs)
+{
+ ga_state = s;
+ ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
+ ga_command_state_add(cs, guest_file_init, NULL);
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
new file mode 100644
index 0000000000..e42b91d364
--- /dev/null
+++ b/qga/guest-agent-core.h
@@ -0,0 +1,31 @@
+/*
+ * QEMU Guest Agent core declarations
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.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 "qapi/qmp-core.h"
+#include "qemu-common.h"
+
+#define QGA_VERSION "1.0"
+#define QGA_READ_COUNT_DEFAULT 4 << 10
+
+typedef struct GAState GAState;
+typedef struct GACommandState GACommandState;
+
+void ga_command_state_init(GAState *s, GACommandState *cs);
+void ga_command_state_add(GACommandState *cs,
+ void (*init)(void),
+ void (*cleanup)(void));
+void ga_command_state_init_all(GACommandState *cs);
+void ga_command_state_cleanup_all(GACommandState *cs);
+GACommandState *ga_command_state_new(void);
+bool ga_logging_enabled(GAState *s);
+void ga_disable_logging(GAState *s);
+void ga_enable_logging(GAState *s);
diff --git a/qlist.h b/qlist.h
index dbe7b92db5..d426bd4a4b 100644
--- a/qlist.h
+++ b/qlist.h
@@ -16,6 +16,7 @@
#include "qobject.h"
#include "qemu-queue.h"
#include "qemu-common.h"
+#include "qemu-queue.h"
typedef struct QListEntry {
QObject *value;
@@ -50,4 +51,14 @@ QObject *qlist_peek(QList *qlist);
int qlist_empty(const QList *qlist);
QList *qobject_to_qlist(const QObject *obj);
+static inline const QListEntry *qlist_first(const QList *qlist)
+{
+ return QTAILQ_FIRST(&qlist->head);
+}
+
+static inline const QListEntry *qlist_next(const QListEntry *entry)
+{
+ return QTAILQ_NEXT(entry, next);
+}
+
#endif /* QLIST_H */
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 92c5c3a318..5d44edf4e7 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -694,6 +694,40 @@ Example:
EQMP
{
+ .name = "blockdev-snapshot-sync",
+ .args_type = "device:B,snapshot-file:s?,format:s?",
+ .params = "device [new-image-file] [format]",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_snapshot_blkdev,
+ },
+
+SQMP
+blockdev-snapshot-sync
+----------------------
+
+Synchronous snapshot of a block device. snapshot-file specifies the
+target of the new image. If the file exists, or if it is a device, the
+snapshot will be created in the existing file/device. If does not
+exist, a new file will be created. format specifies the format of the
+snapshot image, default is qcow2.
+
+Arguments:
+
+- "device": device name to snapshot (json-string)
+- "snapshot-file": name of new image file (json-string)
+- "format": format of new image (json-string, optional)
+
+Example:
+
+-> { "execute": "blockdev-snapshot", "arguments": { "device": "ide-hd0",
+ "snapshot-file":
+ "/some/place/my-image",
+ "format": "qcow2" } }
+<- { "return": {} }
+
+EQMP
+
+ {
.name = "balloon",
.args_type = "value:M",
.params = "target",
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 70a2111d19..3498425fff 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -2539,6 +2539,7 @@ sub process {
}
if (!defined $suppress_ifbraces{$linenr - 1} &&
$line =~ /\b(if|while|for|else)\b/ &&
+ $line !~ /\#\s*if/ &&
$line !~ /\#\s*else/) {
my $allowed = 0;
diff --git a/scripts/ordereddict.py b/scripts/ordereddict.py
new file mode 100644
index 0000000000..7242b5060d
--- /dev/null
+++ b/scripts/ordereddict.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2009 Raymond Hettinger
+#
+# 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.
+
+from UserDict import DictMixin
+
+class OrderedDict(dict, DictMixin):
+
+ def __init__(self, *args, **kwds):
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ try:
+ self.__end
+ except AttributeError:
+ self.clear()
+ self.update(*args, **kwds)
+
+ def clear(self):
+ self.__end = end = []
+ end += [None, end, end] # sentinel node for doubly linked list
+ self.__map = {} # key --> [key, prev, next]
+ dict.clear(self)
+
+ def __setitem__(self, key, value):
+ if key not in self:
+ end = self.__end
+ curr = end[1]
+ curr[2] = end[1] = self.__map[key] = [key, curr, end]
+ dict.__setitem__(self, key, value)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ key, prev, next = self.__map.pop(key)
+ prev[2] = next
+ next[1] = prev
+
+ def __iter__(self):
+ end = self.__end
+ curr = end[2]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[2]
+
+ def __reversed__(self):
+ end = self.__end
+ curr = end[1]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[1]
+
+ def popitem(self, last=True):
+ if not self:
+ raise KeyError('dictionary is empty')
+ if last:
+ key = reversed(self).next()
+ else:
+ key = iter(self).next()
+ value = self.pop(key)
+ return key, value
+
+ def __reduce__(self):
+ items = [[k, self[k]] for k in self]
+ tmp = self.__map, self.__end
+ del self.__map, self.__end
+ inst_dict = vars(self).copy()
+ self.__map, self.__end = tmp
+ if inst_dict:
+ return (self.__class__, (items,), inst_dict)
+ return self.__class__, (items,)
+
+ def keys(self):
+ return list(self)
+
+ setdefault = DictMixin.setdefault
+ update = DictMixin.update
+ pop = DictMixin.pop
+ values = DictMixin.values
+ items = DictMixin.items
+ iterkeys = DictMixin.iterkeys
+ itervalues = DictMixin.itervalues
+ iteritems = DictMixin.iteritems
+
+ def __repr__(self):
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, self.items())
+
+ def copy(self):
+ return self.__class__(self)
+
+ @classmethod
+ def fromkeys(cls, iterable, value=None):
+ d = cls()
+ for key in iterable:
+ d[key] = value
+ return d
+
+ def __eq__(self, other):
+ if isinstance(other, OrderedDict):
+ if len(self) != len(other):
+ return False
+ for p, q in zip(self.items(), other.items()):
+ if p != q:
+ return False
+ return True
+ return dict.__eq__(self, other)
+
+ def __ne__(self, other):
+ return not self == other
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
new file mode 100644
index 0000000000..9ad4c54991
--- /dev/null
+++ b/scripts/qapi-commands.py
@@ -0,0 +1,385 @@
+#
+# QAPI command marshaller generator
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+# Michael Roth <mdroth@linux.vnet.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+from qapi import *
+import sys
+import os
+import getopt
+import errno
+
+def generate_decl_enum(name, members, genlist=True):
+ return mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp);
+''',
+ name=name)
+
+def generate_command_decl(name, args, ret_type):
+ arglist=""
+ for argname, argtype, optional, structured in parse_args(args):
+ argtype = c_type(argtype)
+ if argtype == "char *":
+ argtype = "const char *"
+ if optional:
+ arglist += "bool has_%s, " % c_var(argname)
+ arglist += "%s %s, " % (argtype, c_var(argname))
+ return mcgen('''
+%(ret_type)s qmp_%(name)s(%(args)sError **errp);
+''',
+ ret_type=c_type(ret_type), name=c_var(name), args=arglist).strip()
+
+def gen_sync_call(name, args, ret_type, indent=0):
+ ret = ""
+ arglist=""
+ retval=""
+ if ret_type:
+ retval = "retval = "
+ for argname, argtype, optional, structured in parse_args(args):
+ if optional:
+ arglist += "has_%s, " % c_var(argname)
+ arglist += "%s, " % (c_var(argname))
+ push_indent(indent)
+ ret = mcgen('''
+%(retval)sqmp_%(name)s(%(args)serrp);
+
+''',
+ name=c_var(name), args=arglist, retval=retval).rstrip()
+ if ret_type:
+ ret += "\n" + mcgen(''''
+%(marshal_output_call)s
+''',
+ marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip()
+ pop_indent(indent)
+ return ret.rstrip()
+
+
+def gen_marshal_output_call(name, ret_type):
+ if not ret_type:
+ return ""
+ return "qmp_marshal_output_%s(retval, ret, errp);" % c_var(name)
+
+def gen_visitor_output_containers_decl(ret_type):
+ ret = ""
+ push_indent()
+ if ret_type:
+ ret += mcgen('''
+QmpOutputVisitor *mo;
+QapiDeallocVisitor *md;
+Visitor *v;
+''')
+ pop_indent()
+
+ return ret
+
+def gen_visitor_input_containers_decl(args):
+ ret = ""
+
+ push_indent()
+ if len(args) > 0:
+ ret += mcgen('''
+QmpInputVisitor *mi;
+QapiDeallocVisitor *md;
+Visitor *v;
+''')
+ pop_indent()
+
+ return ret.rstrip()
+
+def gen_visitor_input_vars_decl(args):
+ ret = ""
+ push_indent()
+ for argname, argtype, optional, structured in parse_args(args):
+ if optional:
+ ret += mcgen('''
+bool has_%(argname)s = false;
+''',
+ argname=c_var(argname))
+ if c_type(argtype).endswith("*"):
+ ret += mcgen('''
+%(argtype)s %(argname)s = NULL;
+''',
+ argname=c_var(argname), argtype=c_type(argtype))
+ else:
+ ret += mcgen('''
+%(argtype)s %(argname)s;
+''',
+ argname=c_var(argname), argtype=c_type(argtype))
+
+ pop_indent()
+ return ret.rstrip()
+
+def gen_visitor_input_block(args, obj, dealloc=False):
+ ret = ""
+ if len(args) == 0:
+ return ret
+
+ push_indent()
+
+ if dealloc:
+ ret += mcgen('''
+md = qapi_dealloc_visitor_new();
+v = qapi_dealloc_get_visitor(md);
+''')
+ else:
+ ret += mcgen('''
+mi = qmp_input_visitor_new(%(obj)s);
+v = qmp_input_get_visitor(mi);
+''',
+ obj=obj)
+
+ for argname, argtype, optional, structured in parse_args(args):
+ if optional:
+ ret += mcgen('''
+visit_start_optional(v, &has_%(c_name)s, "%(name)s", errp);
+if (has_%(c_name)s) {
+''',
+ c_name=c_var(argname), name=argname)
+ push_indent()
+ ret += mcgen('''
+visit_type_%(argtype)s(v, &%(c_name)s, "%(name)s", errp);
+''',
+ c_name=c_var(argname), name=argname, argtype=argtype)
+ if optional:
+ pop_indent()
+ ret += mcgen('''
+}
+visit_end_optional(v, errp);
+''')
+
+ if dealloc:
+ ret += mcgen('''
+qapi_dealloc_visitor_cleanup(md);
+''')
+ else:
+ ret += mcgen('''
+qmp_input_visitor_cleanup(mi);
+''')
+ pop_indent()
+ return ret.rstrip()
+
+def gen_marshal_output(name, args, ret_type):
+ if not ret_type:
+ return ""
+ ret = mcgen('''
+static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
+{
+ QapiDeallocVisitor *md = qapi_dealloc_visitor_new();
+ QmpOutputVisitor *mo = qmp_output_visitor_new();
+ Visitor *v;
+
+ v = qmp_output_get_visitor(mo);
+ visit_type_%(ret_type)s(v, &ret_in, "unused", errp);
+ if (!error_is_set(errp)) {
+ *ret_out = qmp_output_get_qobject(mo);
+ }
+ qmp_output_visitor_cleanup(mo);
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_%(ret_type)s(v, &ret_in, "unused", errp);
+ qapi_dealloc_visitor_cleanup(md);
+}
+''',
+ c_ret_type=c_type(ret_type), c_name=c_var(name), ret_type=ret_type)
+
+ return ret
+
+def gen_marshal_input(name, args, ret_type):
+ ret = mcgen('''
+static void qmp_marshal_input_%(c_name)s(QDict *args, QObject **ret, Error **errp)
+{
+''',
+ c_name=c_var(name))
+
+ if ret_type:
+ if c_type(ret_type).endswith("*"):
+ retval = " %s retval = NULL;" % c_type(ret_type)
+ else:
+ retval = " %s retval;" % c_type(ret_type)
+ ret += mcgen('''
+%(retval)s
+''',
+ retval=retval)
+
+ if len(args) > 0:
+ ret += mcgen('''
+%(visitor_input_containers_decl)s
+%(visitor_input_vars_decl)s
+
+%(visitor_input_block)s
+
+''',
+ visitor_input_containers_decl=gen_visitor_input_containers_decl(args),
+ visitor_input_vars_decl=gen_visitor_input_vars_decl(args),
+ visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)"))
+
+ ret += mcgen('''
+ if (error_is_set(errp)) {
+ goto out;
+ }
+%(sync_call)s
+''',
+ sync_call=gen_sync_call(name, args, ret_type, indent=4))
+ ret += mcgen('''
+
+out:
+''')
+ ret += mcgen('''
+%(visitor_input_block_cleanup)s
+ return;
+}
+''',
+ visitor_input_block_cleanup=gen_visitor_input_block(args, None, dealloc=True))
+ return ret
+
+def gen_registry(commands):
+ registry=""
+ push_indent()
+ for cmd in commands:
+ registry += mcgen('''
+qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s);
+''',
+ name=cmd['command'], c_name=c_var(cmd['command']))
+ pop_indent()
+ ret = mcgen('''
+static void qmp_init_marshal(void)
+{
+%(registry)s
+}
+
+qapi_init(qmp_init_marshal);
+''',
+ registry=registry.rstrip())
+ return ret
+
+def gen_command_decl_prologue(header, guard, prefix=""):
+ ret = mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI function prototypes
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef %(guard)s
+#define %(guard)s
+
+#include "%(prefix)sqapi-types.h"
+#include "error.h"
+
+''',
+ header=basename(h_file), guard=guardname(h_file), prefix=prefix)
+ return ret
+
+def gen_command_def_prologue(prefix="", proxy=False):
+ ret = mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QMP->QAPI command dispatch
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-objects.h"
+#include "qapi/qmp-core.h"
+#include "qapi/qapi-visit-core.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qmp-input-visitor.h"
+#include "qapi/qapi-dealloc-visitor.h"
+#include "%(prefix)sqapi-types.h"
+#include "%(prefix)sqapi-visit.h"
+
+''',
+ prefix=prefix)
+ if not proxy:
+ ret += '#include "%sqmp-commands.h"' % prefix
+ return ret + "\n"
+
+
+try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir=", "type="])
+except getopt.GetoptError, err:
+ print str(err)
+ sys.exit(1)
+
+output_dir = ""
+prefix = ""
+dispatch_type = "sync"
+c_file = 'qmp-marshal.c'
+h_file = 'qmp-commands.h'
+
+for o, a in opts:
+ if o in ("-p", "--prefix"):
+ prefix = a
+ elif o in ("-o", "--output-dir"):
+ output_dir = a + "/"
+ elif o in ("-t", "--type"):
+ dispatch_type = a
+
+c_file = output_dir + prefix + c_file
+h_file = output_dir + prefix + h_file
+
+try:
+ os.makedirs(output_dir)
+except os.error, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+exprs = parse_schema(sys.stdin)
+commands = filter(lambda expr: expr.has_key('command'), exprs)
+
+if dispatch_type == "sync":
+ fdecl = open(h_file, 'w')
+ fdef = open(c_file, 'w')
+ ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix)
+ fdecl.write(ret)
+ ret = gen_command_def_prologue(prefix=prefix)
+ fdef.write(ret)
+
+ for cmd in commands:
+ arglist = []
+ ret_type = None
+ if cmd.has_key('data'):
+ arglist = cmd['data']
+ if cmd.has_key('returns'):
+ ret_type = cmd['returns']
+ ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n"
+ fdecl.write(ret)
+ if ret_type:
+ ret = gen_marshal_output(cmd['command'], arglist, ret_type) + "\n"
+ fdef.write(ret)
+ ret = gen_marshal_input(cmd['command'], arglist, ret_type) + "\n"
+ fdef.write(ret)
+
+ fdecl.write("\n#endif");
+ ret = gen_registry(commands)
+ fdef.write(ret)
+
+ fdef.flush()
+ fdef.close()
+ fdecl.flush()
+ fdecl.close()
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
new file mode 100644
index 0000000000..cece32546a
--- /dev/null
+++ b/scripts/qapi-types.py
@@ -0,0 +1,270 @@
+#
+# QAPI types generator
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+from qapi import *
+import sys
+import os
+import getopt
+import errno
+
+def generate_fwd_struct(name, members):
+ return mcgen('''
+typedef struct %(name)s %(name)s;
+
+typedef struct %(name)sList
+{
+ %(name)s *value;
+ struct %(name)sList *next;
+} %(name)sList;
+''',
+ name=name)
+
+def generate_struct(structname, fieldname, members):
+ ret = mcgen('''
+struct %(name)s
+{
+''',
+ name=structname)
+
+ for argname, argentry, optional, structured in parse_args(members):
+ if optional:
+ ret += mcgen('''
+ bool has_%(c_name)s;
+''',
+ c_name=c_var(argname))
+ if structured:
+ push_indent()
+ ret += generate_struct("", argname, argentry)
+ pop_indent()
+ else:
+ ret += mcgen('''
+ %(c_type)s %(c_name)s;
+''',
+ c_type=c_type(argentry), c_name=c_var(argname))
+
+ if len(fieldname):
+ fieldname = " " + fieldname
+ ret += mcgen('''
+}%(field)s;
+''',
+ field=fieldname)
+
+ return ret
+
+def generate_enum_lookup(name, values):
+ ret = mcgen('''
+const char *%(name)s_lookup[] = {
+''',
+ name=name)
+ i = 0
+ for value in values:
+ ret += mcgen('''
+ "%(value)s",
+''',
+ value=c_var(value).lower())
+
+ ret += mcgen('''
+ NULL,
+};
+
+''')
+ return ret
+
+def generate_enum(name, values):
+ lookup_decl = mcgen('''
+extern const char *%(name)s_lookup[];
+''',
+ name=name)
+
+ enum_decl = mcgen('''
+typedef enum %(name)s
+{
+''',
+ name=name)
+
+ i = 0
+ for value in values:
+ enum_decl += mcgen('''
+ %(abbrev)s_%(value)s = %(i)d,
+''',
+ abbrev=de_camel_case(name).upper(),
+ value=c_var(value).upper(),
+ i=i)
+ i += 1
+
+ enum_decl += mcgen('''
+} %(name)s;
+''',
+ name=name)
+
+ return lookup_decl + enum_decl
+
+def generate_union(name, typeinfo):
+ ret = mcgen('''
+struct %(name)s
+{
+ %(name)sKind kind;
+ union {
+''',
+ name=name)
+
+ for key in typeinfo:
+ ret += mcgen('''
+ %(c_type)s %(c_name)s;
+''',
+ c_type=c_type(typeinfo[key]),
+ c_name=c_var(key))
+
+ ret += mcgen('''
+ };
+};
+''')
+
+ return ret
+
+def generate_type_cleanup_decl(name):
+ ret = mcgen('''
+void qapi_free_%(type)s(%(c_type)s obj);
+''',
+ c_type=c_type(name),type=name)
+ return ret
+
+def generate_type_cleanup(name):
+ ret = mcgen('''
+void qapi_free_%(type)s(%(c_type)s obj)
+{
+ QapiDeallocVisitor *md;
+ Visitor *v;
+
+ if (!obj) {
+ return;
+ }
+
+ md = qapi_dealloc_visitor_new();
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_%(type)s(v, &obj, NULL, NULL);
+ qapi_dealloc_visitor_cleanup(md);
+}
+''',
+ c_type=c_type(name),type=name)
+ return ret
+
+
+try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir="])
+except getopt.GetoptError, err:
+ print str(err)
+ sys.exit(1)
+
+output_dir = ""
+prefix = ""
+c_file = 'qapi-types.c'
+h_file = 'qapi-types.h'
+
+for o, a in opts:
+ if o in ("-p", "--prefix"):
+ prefix = a
+ elif o in ("-o", "--output-dir"):
+ output_dir = a + "/"
+
+c_file = output_dir + prefix + c_file
+h_file = output_dir + prefix + h_file
+
+try:
+ os.makedirs(output_dir)
+except os.error, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+fdef = open(c_file, 'w')
+fdecl = open(h_file, 'w')
+
+fdef.write(mcgen('''
+/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * deallocation functions for schema-defined QAPI types
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qapi-dealloc-visitor.h"
+#include "%(prefix)sqapi-types.h"
+#include "%(prefix)sqapi-visit.h"
+
+''', prefix=prefix))
+
+fdecl.write(mcgen('''
+/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI types
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef %(guard)s
+#define %(guard)s
+
+#include "qapi/qapi-types-core.h"
+''',
+ guard=guardname(h_file)))
+
+exprs = parse_schema(sys.stdin)
+
+for expr in exprs:
+ ret = "\n"
+ if expr.has_key('type'):
+ ret += generate_fwd_struct(expr['type'], expr['data'])
+ elif expr.has_key('enum'):
+ ret += generate_enum(expr['enum'], expr['data'])
+ fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
+ elif expr.has_key('union'):
+ ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
+ ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
+ else:
+ continue
+ fdecl.write(ret)
+
+for expr in exprs:
+ ret = "\n"
+ if expr.has_key('type'):
+ ret += generate_struct(expr['type'], "", expr['data']) + "\n"
+ ret += generate_type_cleanup_decl(expr['type'])
+ fdef.write(generate_type_cleanup(expr['type']) + "\n")
+ elif expr.has_key('union'):
+ ret += generate_union(expr['union'], expr['data'])
+ else:
+ continue
+ fdecl.write(ret)
+
+fdecl.write('''
+#endif
+''')
+
+fdecl.flush()
+fdecl.close()
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
new file mode 100644
index 0000000000..252230ef25
--- /dev/null
+++ b/scripts/qapi-visit.py
@@ -0,0 +1,246 @@
+#
+# QAPI visitor generator
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+# Michael Roth <mdroth@linux.vnet.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+from qapi import *
+import sys
+import os
+import getopt
+import errno
+
+def generate_visit_struct_body(field_prefix, members):
+ ret = ""
+ if len(field_prefix):
+ field_prefix = field_prefix + "."
+ for argname, argentry, optional, structured in parse_args(members):
+ if optional:
+ ret += mcgen('''
+visit_start_optional(m, (obj && *obj) ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", errp);
+if ((*obj)->%(prefix)shas_%(c_name)s) {
+''',
+ c_prefix=c_var(field_prefix), prefix=field_prefix,
+ c_name=c_var(argname), name=argname)
+ push_indent()
+
+ if structured:
+ ret += mcgen('''
+visit_start_struct(m, NULL, "", "%(name)s", 0, errp);
+''',
+ name=argname)
+ ret += generate_visit_struct_body(field_prefix + argname, argentry)
+ ret += mcgen('''
+visit_end_struct(m, errp);
+''')
+ else:
+ ret += mcgen('''
+visit_type_%(type)s(m, (obj && *obj) ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", errp);
+''',
+ c_prefix=c_var(field_prefix), prefix=field_prefix,
+ type=type_name(argentry), c_name=c_var(argname),
+ name=argname)
+
+ if optional:
+ pop_indent()
+ ret += mcgen('''
+}
+visit_end_optional(m, errp);
+''')
+ return ret
+
+def generate_visit_struct(name, members):
+ ret = mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
+{
+ visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), errp);
+''',
+ name=name)
+ push_indent()
+ ret += generate_visit_struct_body("", members)
+ pop_indent()
+
+ ret += mcgen('''
+ visit_end_struct(m, errp);
+}
+''')
+ return ret
+
+def generate_visit_list(name, members):
+ return mcgen('''
+
+void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
+{
+ GenericList *i;
+
+ visit_start_list(m, name, errp);
+
+ for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) {
+ %(name)sList *native_i = (%(name)sList *)i;
+ visit_type_%(name)s(m, &native_i->value, NULL, errp);
+ }
+
+ visit_end_list(m, errp);
+}
+''',
+ name=name)
+
+def generate_visit_enum(name, members):
+ return mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp)
+{
+ visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp);
+}
+''',
+ name=name)
+
+def generate_visit_union(name, members):
+ ret = generate_visit_enum('%sKind' % name, members.keys())
+
+ ret += mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
+{
+}
+''',
+ name=name)
+
+ return ret
+
+def generate_declaration(name, members, genlist=True):
+ ret = mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp);
+''',
+ name=name)
+
+ if genlist:
+ ret += mcgen('''
+void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp);
+''',
+ name=name)
+
+ return ret
+
+def generate_decl_enum(name, members, genlist=True):
+ return mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp);
+''',
+ name=name)
+
+try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir="])
+except getopt.GetoptError, err:
+ print str(err)
+ sys.exit(1)
+
+output_dir = ""
+prefix = ""
+c_file = 'qapi-visit.c'
+h_file = 'qapi-visit.h'
+
+for o, a in opts:
+ if o in ("-p", "--prefix"):
+ prefix = a
+ elif o in ("-o", "--output-dir"):
+ output_dir = a + "/"
+
+c_file = output_dir + prefix + c_file
+h_file = output_dir + prefix + h_file
+
+try:
+ os.makedirs(output_dir)
+except os.error, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+fdef = open(c_file, 'w')
+fdecl = open(h_file, 'w')
+
+fdef.write(mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI visitor functions
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "%(header)s"
+''',
+ header=basename(h_file)))
+
+fdecl.write(mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI visitor function
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef %(guard)s
+#define %(guard)s
+
+#include "qapi/qapi-visit-core.h"
+#include "%(prefix)sqapi-types.h"
+''',
+ prefix=prefix, guard=guardname(h_file)))
+
+exprs = parse_schema(sys.stdin)
+
+for expr in exprs:
+ if expr.has_key('type'):
+ ret = generate_visit_struct(expr['type'], expr['data'])
+ ret += generate_visit_list(expr['type'], expr['data'])
+ fdef.write(ret)
+
+ ret = generate_declaration(expr['type'], expr['data'])
+ fdecl.write(ret)
+ elif expr.has_key('union'):
+ ret = generate_visit_union(expr['union'], expr['data'])
+ fdef.write(ret)
+
+ ret = generate_decl_enum('%sKind' % expr['union'], expr['data'].keys())
+ ret += generate_declaration(expr['union'], expr['data'])
+ fdecl.write(ret)
+ elif expr.has_key('enum'):
+ ret = generate_visit_enum(expr['enum'], expr['data'])
+ fdef.write(ret)
+
+ ret = generate_decl_enum(expr['enum'], expr['data'])
+ fdecl.write(ret)
+
+fdecl.write('''
+#endif
+''')
+
+fdecl.flush()
+fdecl.close()
+
+fdef.flush()
+fdef.close()
diff --git a/scripts/qapi.py b/scripts/qapi.py
new file mode 100644
index 0000000000..56af2329bb
--- /dev/null
+++ b/scripts/qapi.py
@@ -0,0 +1,203 @@
+#
+# QAPI helper library
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+
+def tokenize(data):
+ while len(data):
+ if data[0] in ['{', '}', ':', ',', '[', ']']:
+ yield data[0]
+ data = data[1:]
+ elif data[0] in ' \n':
+ data = data[1:]
+ elif data[0] == "'":
+ data = data[1:]
+ string = ''
+ while data[0] != "'":
+ string += data[0]
+ data = data[1:]
+ data = data[1:]
+ yield string
+
+def parse(tokens):
+ if tokens[0] == '{':
+ ret = OrderedDict()
+ tokens = tokens[1:]
+ while tokens[0] != '}':
+ key = tokens[0]
+ tokens = tokens[1:]
+
+ tokens = tokens[1:] # :
+
+ value, tokens = parse(tokens)
+
+ if tokens[0] == ',':
+ tokens = tokens[1:]
+
+ ret[key] = value
+ tokens = tokens[1:]
+ return ret, tokens
+ elif tokens[0] == '[':
+ ret = []
+ tokens = tokens[1:]
+ while tokens[0] != ']':
+ value, tokens = parse(tokens)
+ if tokens[0] == ',':
+ tokens = tokens[1:]
+ ret.append(value)
+ tokens = tokens[1:]
+ return ret, tokens
+ else:
+ return tokens[0], tokens[1:]
+
+def evaluate(string):
+ return parse(map(lambda x: x, tokenize(string)))[0]
+
+def parse_schema(fp):
+ exprs = []
+ expr = ''
+ expr_eval = None
+
+ for line in fp:
+ if line.startswith('#') or line == '\n':
+ continue
+
+ if line.startswith(' '):
+ expr += line
+ elif expr:
+ expr_eval = evaluate(expr)
+ if expr_eval.has_key('enum'):
+ add_enum(expr_eval['enum'])
+ elif expr_eval.has_key('union'):
+ add_enum('%sKind' % expr_eval['union'])
+ exprs.append(expr_eval)
+ expr = line
+ else:
+ expr += line
+
+ if expr:
+ expr_eval = evaluate(expr)
+ if expr_eval.has_key('enum'):
+ add_enum(expr_eval['enum'])
+ elif expr_eval.has_key('union'):
+ add_enum('%sKind' % expr_eval['union'])
+ exprs.append(expr_eval)
+
+ return exprs
+
+def parse_args(typeinfo):
+ for member in typeinfo:
+ argname = member
+ argentry = typeinfo[member]
+ optional = False
+ structured = False
+ if member.startswith('*'):
+ argname = member[1:]
+ optional = True
+ if isinstance(argentry, OrderedDict):
+ structured = True
+ yield (argname, argentry, optional, structured)
+
+def de_camel_case(name):
+ new_name = ''
+ for ch in name:
+ if ch.isupper() and new_name:
+ new_name += '_'
+ if ch == '-':
+ new_name += '_'
+ else:
+ new_name += ch.lower()
+ return new_name
+
+def camel_case(name):
+ new_name = ''
+ first = True
+ for ch in name:
+ if ch in ['_', '-']:
+ first = True
+ elif first:
+ new_name += ch.upper()
+ first = False
+ else:
+ new_name += ch.lower()
+ return new_name
+
+def c_var(name):
+ return '_'.join(name.split('-')).lstrip("*")
+
+def c_list_type(name):
+ return '%sList' % name
+
+def type_name(name):
+ if type(name) == list:
+ return c_list_type(name[0])
+ return name
+
+enum_types = []
+
+def add_enum(name):
+ global enum_types
+ enum_types.append(name)
+
+def is_enum(name):
+ global enum_types
+ return (name in enum_types)
+
+def c_type(name):
+ if name == 'str':
+ return 'char *'
+ elif name == 'int':
+ return 'int64_t'
+ elif name == 'bool':
+ return 'bool'
+ elif name == 'number':
+ return 'double'
+ elif type(name) == list:
+ return '%s *' % c_list_type(name[0])
+ elif is_enum(name):
+ return name
+ elif name == None or len(name) == 0:
+ return 'void'
+ elif name == name.upper():
+ return '%sEvent *' % camel_case(name)
+ else:
+ return '%s *' % name
+
+def genindent(count):
+ ret = ""
+ for i in range(count):
+ ret += " "
+ return ret
+
+indent_level = 0
+
+def push_indent(indent_amount=4):
+ global indent_level
+ indent_level += indent_amount
+
+def pop_indent(indent_amount=4):
+ global indent_level
+ indent_level -= indent_amount
+
+def cgen(code, **kwds):
+ indent = genindent(indent_level)
+ lines = code.split('\n')
+ lines = map(lambda x: indent + x, lines)
+ return '\n'.join(lines) % kwds + '\n'
+
+def mcgen(code, **kwds):
+ return cgen('\n'.join(code.split('\n')[1:-1]), **kwds)
+
+def basename(filename):
+ return filename.split("/")[-1]
+
+def guardname(filename):
+ return filename.replace("/", "_").replace("-", "_").split(".")[0].upper()
diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h
index 78caa796b6..919be12a38 100644
--- a/target-alpha/cpu.h
+++ b/target-alpha/cpu.h
@@ -434,8 +434,9 @@ uint64_t cpu_alpha_load_fpcr (CPUState *env);
void cpu_alpha_store_fpcr (CPUState *env, uint64_t val);
#ifndef CONFIG_USER_ONLY
void swap_shadow_regs(CPUState *env);
-extern QEMU_NORETURN void do_unassigned_access(target_phys_addr_t addr,
- int, int, int, int);
+QEMU_NORETURN void cpu_unassigned_access(CPUState *env1,
+ target_phys_addr_t addr, int is_write,
+ int is_exec, int unused, int size);
#endif
/* Bits in TB->FLAGS that control how translation is processed. */
diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c
index 51d1bd7099..8f39154391 100644
--- a/target-alpha/op_helper.c
+++ b/target-alpha/op_helper.c
@@ -1301,9 +1301,11 @@ static void QEMU_NORETURN do_unaligned_access(target_ulong addr, int is_write,
helper_excp(EXCP_UNALIGN, 0);
}
-void QEMU_NORETURN do_unassigned_access(target_phys_addr_t addr, int is_write,
- int is_exec, int unused, int size)
+void QEMU_NORETURN cpu_unassigned_access(CPUState *env1,
+ target_phys_addr_t addr, int is_write,
+ int is_exec, int unused, int size)
{
+ env = env1;
env->trap_arg0 = addr;
env->trap_arg1 = is_write;
dynamic_excp(EXCP_MCHK, 0);
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1022a03753..adef42785c 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -133,6 +133,12 @@ typedef struct CPUARMState {
uint32_t c7_par; /* Translation result. */
uint32_t c9_insn; /* Cache lockdown registers. */
uint32_t c9_data;
+ uint32_t c9_pmcr; /* performance monitor control register */
+ uint32_t c9_pmcnten; /* perf monitor counter enables */
+ uint32_t c9_pmovsr; /* perf monitor overflow status */
+ uint32_t c9_pmxevtyper; /* perf monitor event type */
+ uint32_t c9_pmuserenr; /* perf monitor user enable */
+ uint32_t c9_pminten; /* perf monitor interrupt enables */
uint32_t c13_fcse; /* FCSE PID. */
uint32_t c13_context; /* Context ID. */
uint32_t c13_tls1; /* User RW Thread register. */
@@ -438,7 +444,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define cpu_signal_handler cpu_arm_signal_handler
#define cpu_list arm_cpu_list
-#define CPU_SAVE_VERSION 3
+#define CPU_SAVE_VERSION 4
/* MMU modes definitions */
#define MMU_MODE0_SUFFIX _kernel
diff --git a/target-arm/helper.c b/target-arm/helper.c
index f4d12aab55..ae4f334e40 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -269,6 +269,10 @@ void cpu_reset(CPUARMState *env)
}
env->vfp.xregs[ARM_VFP_FPEXC] = 0;
env->cp15.c2_base_mask = 0xffffc000u;
+ /* v7 performance monitor control register: same implementor
+ * field as main ID register, and we implement no event counters.
+ */
+ env->cp15.c9_pmcr = (id & 0xff000000);
#endif
set_flush_to_zero(1, &env->vfp.standard_fp_status);
set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status);
@@ -1587,6 +1591,81 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 1: /* TCM memory region registers. */
/* Not implemented. */
goto bad_reg;
+ case 12: /* Performance monitor control */
+ /* Performance monitors are implementation defined in v7,
+ * but with an ARM recommended set of registers, which we
+ * follow (although we don't actually implement any counters)
+ */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
+ switch (op2) {
+ case 0: /* performance monitor control register */
+ /* only the DP, X, D and E bits are writable */
+ env->cp15.c9_pmcr &= ~0x39;
+ env->cp15.c9_pmcr |= (val & 0x39);
+ break;
+ case 1: /* Count enable set register */
+ val &= (1 << 31);
+ env->cp15.c9_pmcnten |= val;
+ break;
+ case 2: /* Count enable clear */
+ val &= (1 << 31);
+ env->cp15.c9_pmcnten &= ~val;
+ break;
+ case 3: /* Overflow flag status */
+ env->cp15.c9_pmovsr &= ~val;
+ break;
+ case 4: /* Software increment */
+ /* RAZ/WI since we don't implement the software-count event */
+ break;
+ case 5: /* Event counter selection register */
+ /* Since we don't implement any events, writing to this register
+ * is actually UNPREDICTABLE. So we choose to RAZ/WI.
+ */
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 13: /* Performance counters */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
+ switch (op2) {
+ case 0: /* Cycle count register: not implemented, so RAZ/WI */
+ break;
+ case 1: /* Event type select */
+ env->cp15.c9_pmxevtyper = val & 0xff;
+ break;
+ case 2: /* Event count register */
+ /* Unimplemented (we have no events), RAZ/WI */
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 14: /* Performance monitor control */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
+ switch (op2) {
+ case 0: /* user enable */
+ env->cp15.c9_pmuserenr = val & 1;
+ /* changes access rights for cp registers, so flush tbs */
+ tb_flush(env);
+ break;
+ case 1: /* interrupt enable set */
+ /* We have no event counters so only the C bit can be changed */
+ val &= (1 << 31);
+ env->cp15.c9_pminten |= val;
+ break;
+ case 2: /* interrupt enable clear */
+ val &= (1 << 31);
+ env->cp15.c9_pminten &= ~val;
+ break;
+ }
+ break;
default:
goto bad_reg;
}
@@ -1878,27 +1957,81 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
return 0;
case 8: /* MMU TLB control. */
goto bad_reg;
- case 9: /* Cache lockdown. */
- switch (op1) {
- case 0: /* L1 cache. */
- if (arm_feature(env, ARM_FEATURE_OMAPCP))
- return 0;
+ case 9:
+ switch (crm) {
+ case 0: /* Cache lockdown */
+ switch (op1) {
+ case 0: /* L1 cache. */
+ if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+ return 0;
+ }
+ switch (op2) {
+ case 0:
+ return env->cp15.c9_data;
+ case 1:
+ return env->cp15.c9_insn;
+ default:
+ goto bad_reg;
+ }
+ case 1: /* L2 cache */
+ if (crm != 0) {
+ goto bad_reg;
+ }
+ /* L2 Lockdown and Auxiliary control. */
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 12: /* Performance monitor control */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
switch (op2) {
- case 0:
- return env->cp15.c9_data;
- case 1:
- return env->cp15.c9_insn;
+ case 0: /* performance monitor control register */
+ return env->cp15.c9_pmcr;
+ case 1: /* count enable set */
+ case 2: /* count enable clear */
+ return env->cp15.c9_pmcnten;
+ case 3: /* overflow flag status */
+ return env->cp15.c9_pmovsr;
+ case 4: /* software increment */
+ case 5: /* event counter selection register */
+ return 0; /* Unimplemented, RAZ/WI */
default:
goto bad_reg;
}
- case 1: /* L2 cache */
- if (crm != 0)
+ case 13: /* Performance counters */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
+ switch (op2) {
+ case 1: /* Event type select */
+ return env->cp15.c9_pmxevtyper;
+ case 0: /* Cycle count register */
+ case 2: /* Event count register */
+ /* Unimplemented, so RAZ/WI */
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ case 14: /* Performance monitor control */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
goto bad_reg;
- /* L2 Lockdown and Auxiliary control. */
- return 0;
+ }
+ switch (op2) {
+ case 0: /* user enable */
+ return env->cp15.c9_pmuserenr;
+ case 1: /* interrupt enable set */
+ case 2: /* interrupt enable clear */
+ return env->cp15.c9_pminten;
+ default:
+ goto bad_reg;
+ }
default:
goto bad_reg;
}
+ break;
case 10: /* MMU TLB lockdown. */
/* ??? TLB lockdown not implemented. */
return 0;
@@ -1994,11 +2127,11 @@ uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg)
return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp;
case 16: /* PRIMASK */
return (env->uncached_cpsr & CPSR_I) != 0;
- case 17: /* FAULTMASK */
- return (env->uncached_cpsr & CPSR_F) != 0;
- case 18: /* BASEPRI */
- case 19: /* BASEPRI_MAX */
+ case 17: /* BASEPRI */
+ case 18: /* BASEPRI_MAX */
return env->v7m.basepri;
+ case 19: /* FAULTMASK */
+ return (env->uncached_cpsr & CPSR_F) != 0;
case 20: /* CONTROL */
return env->v7m.control;
default:
@@ -2050,20 +2183,20 @@ void HELPER(v7m_msr)(CPUState *env, uint32_t reg, uint32_t val)
else
env->uncached_cpsr &= ~CPSR_I;
break;
- case 17: /* FAULTMASK */
- if (val & 1)
- env->uncached_cpsr |= CPSR_F;
- else
- env->uncached_cpsr &= ~CPSR_F;
- break;
- case 18: /* BASEPRI */
+ case 17: /* BASEPRI */
env->v7m.basepri = val & 0xff;
break;
- case 19: /* BASEPRI_MAX */
+ case 18: /* BASEPRI_MAX */
val &= 0xff;
if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0))
env->v7m.basepri = val;
break;
+ case 19: /* FAULTMASK */
+ if (val & 1)
+ env->uncached_cpsr |= CPSR_F;
+ else
+ env->uncached_cpsr &= ~CPSR_F;
+ break;
case 20: /* CONTROL */
env->v7m.control = val & 3;
switch_v7m_sp(env, (val & 2) != 0);
@@ -2452,13 +2585,15 @@ void vfp_set_fpscr(CPUState *env, uint32_t val)
#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p))
#define VFP_BINOP(name) \
-float32 VFP_HELPER(name, s)(float32 a, float32 b, CPUState *env) \
+float32 VFP_HELPER(name, s)(float32 a, float32 b, void *fpstp) \
{ \
- return float32_ ## name (a, b, &env->vfp.fp_status); \
+ float_status *fpst = fpstp; \
+ return float32_ ## name(a, b, fpst); \
} \
-float64 VFP_HELPER(name, d)(float64 a, float64 b, CPUState *env) \
+float64 VFP_HELPER(name, d)(float64 a, float64 b, void *fpstp) \
{ \
- return float64_ ## name (a, b, &env->vfp.fp_status); \
+ float_status *fpst = fpstp; \
+ return float64_ ## name(a, b, fpst); \
}
VFP_BINOP(add)
VFP_BINOP(sub)
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 7d5533f613..3ad1cb0881 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -74,14 +74,14 @@ DEF_HELPER_2(set_user_reg, void, i32, i32)
DEF_HELPER_1(vfp_get_fpscr, i32, env)
DEF_HELPER_2(vfp_set_fpscr, void, env, i32)
-DEF_HELPER_3(vfp_adds, f32, f32, f32, env)
-DEF_HELPER_3(vfp_addd, f64, f64, f64, env)
-DEF_HELPER_3(vfp_subs, f32, f32, f32, env)
-DEF_HELPER_3(vfp_subd, f64, f64, f64, env)
-DEF_HELPER_3(vfp_muls, f32, f32, f32, env)
-DEF_HELPER_3(vfp_muld, f64, f64, f64, env)
-DEF_HELPER_3(vfp_divs, f32, f32, f32, env)
-DEF_HELPER_3(vfp_divd, f64, f64, f64, env)
+DEF_HELPER_3(vfp_adds, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_addd, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_subs, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_subd, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_muls, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_muld, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_divs, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_divd, f64, f64, f64, ptr)
DEF_HELPER_1(vfp_negs, f32, f32)
DEF_HELPER_1(vfp_negd, f64, f64)
DEF_HELPER_1(vfp_abss, f32, f32)
@@ -154,22 +154,22 @@ DEF_HELPER_2(sar_cc, i32, i32, i32)
DEF_HELPER_2(ror_cc, i32, i32, i32)
/* neon_helper.c */
-DEF_HELPER_2(neon_qadd_u8, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_s8, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_u16, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_u32, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_u8, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_s8, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_u16, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_u32, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_u64, i64, i64, i64)
-DEF_HELPER_2(neon_qadd_s64, i64, i64, i64)
-DEF_HELPER_2(neon_qsub_u64, i64, i64, i64)
-DEF_HELPER_2(neon_qsub_s64, i64, i64, i64)
+DEF_HELPER_3(neon_qadd_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qadd_s64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qsub_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qsub_s64, i64, env, i64, i64)
DEF_HELPER_2(neon_hadd_s8, i32, i32, i32)
DEF_HELPER_2(neon_hadd_u8, i32, i32, i32)
@@ -247,26 +247,26 @@ DEF_HELPER_2(neon_rshl_u32, i32, i32, i32)
DEF_HELPER_2(neon_rshl_s32, i32, i32, i32)
DEF_HELPER_2(neon_rshl_u64, i64, i64, i64)
DEF_HELPER_2(neon_rshl_s64, i64, i64, i64)
-DEF_HELPER_2(neon_qshl_u8, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_s8, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_u16, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_u32, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_u64, i64, i64, i64)
-DEF_HELPER_2(neon_qshl_s64, i64, i64, i64)
-DEF_HELPER_2(neon_qshlu_s8, i32, i32, i32);
-DEF_HELPER_2(neon_qshlu_s16, i32, i32, i32);
-DEF_HELPER_2(neon_qshlu_s32, i32, i32, i32);
-DEF_HELPER_2(neon_qshlu_s64, i64, i64, i64);
-DEF_HELPER_2(neon_qrshl_u8, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_s8, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_u16, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_u32, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_u64, i64, i64, i64)
-DEF_HELPER_2(neon_qrshl_s64, i64, i64, i64)
+DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64);
+DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64)
DEF_HELPER_2(neon_add_u8, i32, i32, i32)
DEF_HELPER_2(neon_add_u16, i32, i32, i32)
@@ -295,22 +295,22 @@ DEF_HELPER_1(neon_cls_s16, i32, i32)
DEF_HELPER_1(neon_cls_s32, i32, i32)
DEF_HELPER_1(neon_cnt_u8, i32, i32)
-DEF_HELPER_2(neon_qdmulh_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qrdmulh_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qdmulh_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qrdmulh_s32, i32, i32, i32)
+DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32)
DEF_HELPER_1(neon_narrow_u8, i32, i64)
DEF_HELPER_1(neon_narrow_u16, i32, i64)
-DEF_HELPER_1(neon_unarrow_sat8, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_u8, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_s8, i32, i64)
-DEF_HELPER_1(neon_unarrow_sat16, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_u16, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_s16, i32, i64)
-DEF_HELPER_1(neon_unarrow_sat32, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_u32, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_s32, i32, i64)
+DEF_HELPER_2(neon_unarrow_sat8, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_u8, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s8, i32, env, i64)
+DEF_HELPER_2(neon_unarrow_sat16, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_u16, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s16, i32, env, i64)
+DEF_HELPER_2(neon_unarrow_sat32, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_u32, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s32, i32, env, i64)
DEF_HELPER_1(neon_narrow_high_u8, i32, i64)
DEF_HELPER_1(neon_narrow_high_u16, i32, i64)
DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64)
@@ -326,8 +326,8 @@ DEF_HELPER_2(neon_paddl_u16, i64, i64, i64)
DEF_HELPER_2(neon_paddl_u32, i64, i64, i64)
DEF_HELPER_2(neon_subl_u16, i64, i64, i64)
DEF_HELPER_2(neon_subl_u32, i64, i64, i64)
-DEF_HELPER_2(neon_addl_saturate_s32, i64, i64, i64)
-DEF_HELPER_2(neon_addl_saturate_s64, i64, i64, i64)
+DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64)
+DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64)
DEF_HELPER_2(neon_abdl_u16, i64, i32, i32)
DEF_HELPER_2(neon_abdl_s16, i64, i32, i32)
DEF_HELPER_2(neon_abdl_u32, i64, i32, i32)
@@ -343,24 +343,21 @@ 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_1(neon_qabs_s8, i32, i32)
-DEF_HELPER_1(neon_qabs_s16, i32, i32)
-DEF_HELPER_1(neon_qabs_s32, i32, i32)
-DEF_HELPER_1(neon_qneg_s8, i32, i32)
-DEF_HELPER_1(neon_qneg_s16, i32, i32)
-DEF_HELPER_1(neon_qneg_s32, i32, i32)
-
-DEF_HELPER_2(neon_min_f32, i32, i32, i32)
-DEF_HELPER_2(neon_max_f32, i32, i32, i32)
-DEF_HELPER_2(neon_abd_f32, i32, i32, i32)
-DEF_HELPER_2(neon_add_f32, i32, i32, i32)
-DEF_HELPER_2(neon_sub_f32, i32, i32, i32)
-DEF_HELPER_2(neon_mul_f32, i32, i32, i32)
-DEF_HELPER_2(neon_ceq_f32, i32, i32, i32)
-DEF_HELPER_2(neon_cge_f32, i32, i32, i32)
-DEF_HELPER_2(neon_cgt_f32, i32, i32, i32)
-DEF_HELPER_2(neon_acge_f32, i32, i32, i32)
-DEF_HELPER_2(neon_acgt_f32, i32, i32, i32)
+DEF_HELPER_2(neon_qabs_s8, i32, env, i32)
+DEF_HELPER_2(neon_qabs_s16, i32, env, i32)
+DEF_HELPER_2(neon_qabs_s32, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s8, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s16, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s32, i32, env, i32)
+
+DEF_HELPER_3(neon_min_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_max_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_abd_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_cge_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_acge_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, ptr)
/* iwmmxt_helper.c */
DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64)
@@ -375,47 +372,47 @@ DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64)
DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64)
DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64)
-#define DEF_IWMMXT_HELPER_SIZE(name) \
-DEF_HELPER_2(iwmmxt_##name##b, i64, i64, i64) \
-DEF_HELPER_2(iwmmxt_##name##w, i64, i64, i64) \
-DEF_HELPER_2(iwmmxt_##name##l, i64, i64, i64) \
-
-DEF_IWMMXT_HELPER_SIZE(unpackl)
-DEF_IWMMXT_HELPER_SIZE(unpackh)
-
-DEF_HELPER_1(iwmmxt_unpacklub, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackluw, i64, i64)
-DEF_HELPER_1(iwmmxt_unpacklul, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhub, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhuw, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhul, i64, i64)
-DEF_HELPER_1(iwmmxt_unpacklsb, i64, i64)
-DEF_HELPER_1(iwmmxt_unpacklsw, i64, i64)
-DEF_HELPER_1(iwmmxt_unpacklsl, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhsb, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhsw, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhsl, i64, i64)
-
-DEF_IWMMXT_HELPER_SIZE(cmpeq)
-DEF_IWMMXT_HELPER_SIZE(cmpgtu)
-DEF_IWMMXT_HELPER_SIZE(cmpgts)
-
-DEF_IWMMXT_HELPER_SIZE(mins)
-DEF_IWMMXT_HELPER_SIZE(minu)
-DEF_IWMMXT_HELPER_SIZE(maxs)
-DEF_IWMMXT_HELPER_SIZE(maxu)
-
-DEF_IWMMXT_HELPER_SIZE(subn)
-DEF_IWMMXT_HELPER_SIZE(addn)
-DEF_IWMMXT_HELPER_SIZE(subu)
-DEF_IWMMXT_HELPER_SIZE(addu)
-DEF_IWMMXT_HELPER_SIZE(subs)
-DEF_IWMMXT_HELPER_SIZE(adds)
-
-DEF_HELPER_2(iwmmxt_avgb0, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_avgb1, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_avgw0, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_avgw1, i64, i64, i64)
+#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \
+DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \
+DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \
+DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \
+
+DEF_IWMMXT_HELPER_SIZE_ENV(unpackl)
+DEF_IWMMXT_HELPER_SIZE_ENV(unpackh)
+
+DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq)
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu)
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(mins)
+DEF_IWMMXT_HELPER_SIZE_ENV(minu)
+DEF_IWMMXT_HELPER_SIZE_ENV(maxs)
+DEF_IWMMXT_HELPER_SIZE_ENV(maxu)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(subn)
+DEF_IWMMXT_HELPER_SIZE_ENV(addn)
+DEF_IWMMXT_HELPER_SIZE_ENV(subu)
+DEF_IWMMXT_HELPER_SIZE_ENV(addu)
+DEF_IWMMXT_HELPER_SIZE_ENV(subs)
+DEF_IWMMXT_HELPER_SIZE_ENV(adds)
+
+DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64)
DEF_HELPER_2(iwmmxt_msadb, i64, i64, i64)
@@ -434,26 +431,26 @@ DEF_HELPER_1(iwmmxt_msbb, i32, i64)
DEF_HELPER_1(iwmmxt_msbw, i32, i64)
DEF_HELPER_1(iwmmxt_msbl, i32, i64)
-DEF_HELPER_2(iwmmxt_srlw, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_srll, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_srlq, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sllw, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_slll, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sllq, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sraw, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sral, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sraq, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_rorw, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_rorl, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_rorq, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_shufh, i64, i64, i32)
-
-DEF_HELPER_2(iwmmxt_packuw, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packul, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packuq, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packsw, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packsl, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packsq, i64, i64, i64)
+DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32)
+
+DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64)
DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32)
DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32)
@@ -461,15 +458,15 @@ DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32)
DEF_HELPER_2(set_teecr, void, env, i32)
-DEF_HELPER_2(neon_unzip8, void, i32, i32)
-DEF_HELPER_2(neon_unzip16, void, i32, i32)
-DEF_HELPER_2(neon_qunzip8, void, i32, i32)
-DEF_HELPER_2(neon_qunzip16, void, i32, i32)
-DEF_HELPER_2(neon_qunzip32, void, i32, i32)
-DEF_HELPER_2(neon_zip8, void, i32, i32)
-DEF_HELPER_2(neon_zip16, void, i32, i32)
-DEF_HELPER_2(neon_qzip8, void, i32, i32)
-DEF_HELPER_2(neon_qzip16, void, i32, i32)
-DEF_HELPER_2(neon_qzip32, void, i32, i32)
+DEF_HELPER_3(neon_unzip8, void, env, i32, i32)
+DEF_HELPER_3(neon_unzip16, void, env, i32, i32)
+DEF_HELPER_3(neon_qunzip8, void, env, i32, i32)
+DEF_HELPER_3(neon_qunzip16, void, env, i32, i32)
+DEF_HELPER_3(neon_qunzip32, void, env, i32, i32)
+DEF_HELPER_3(neon_zip8, void, env, i32, i32)
+DEF_HELPER_3(neon_zip16, void, env, i32, i32)
+DEF_HELPER_3(neon_qzip8, void, env, i32, i32)
+DEF_HELPER_3(neon_qzip16, void, env, i32, i32)
+DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
#include "def-helper.h"
diff --git a/target-arm/iwmmxt_helper.c b/target-arm/iwmmxt_helper.c
index ebe6eb9fae..843994d8d5 100644
--- a/target-arm/iwmmxt_helper.c
+++ b/target-arm/iwmmxt_helper.c
@@ -23,7 +23,7 @@
#include <stdio.h>
#include "cpu.h"
-#include "exec.h"
+#include "exec-all.h"
#include "helper.h"
/* iwMMXt macros extracted from GNU gdb. */
@@ -162,7 +162,8 @@ uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b)
SIMD64_SET(NBIT64(x), SIMD_NBIT) | \
SIMD64_SET(ZBIT64(x), SIMD_ZBIT)
#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = \
(((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \
@@ -176,7 +177,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(uint64_t a, uint64_t b) \
NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = \
(((a >> SH0) & 0xffff) << 0) | \
@@ -188,7 +190,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(uint64_t a, uint64_t b) \
NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = \
(((a >> SH0) & 0xffffffff) << 0) | \
@@ -197,7 +200,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(uint64_t a, uint64_t b) \
NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUState *env, \
+ uint64_t x) \
{ \
x = \
(((x >> SH0) & 0xff) << 0) | \
@@ -209,7 +213,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(uint64_t x) \
NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUState *env, \
+ uint64_t x) \
{ \
x = \
(((x >> SH0) & 0xffff) << 0) | \
@@ -218,13 +223,15 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(uint64_t x) \
NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUState *env, \
+ uint64_t x) \
{ \
x = (((x >> SH0) & 0xffffffff) << 0); \
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUState *env, \
+ uint64_t x) \
{ \
x = \
((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \
@@ -236,7 +243,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(uint64_t x) \
NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUState *env, \
+ uint64_t x) \
{ \
x = \
((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \
@@ -245,7 +253,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(uint64_t x) \
NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUState *env, \
+ uint64_t x) \
{ \
x = EXTEND32((x >> SH0) & 0xffffffff); \
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \
@@ -255,7 +264,8 @@ IWMMXT_OP_UNPACK(l, 0, 8, 16, 24)
IWMMXT_OP_UNPACK(h, 32, 40, 48, 56)
#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \
-uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = \
CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \
@@ -269,7 +279,8 @@ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(uint64_t a, uint64_t b) \
NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \
CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \
@@ -278,7 +289,8 @@ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(uint64_t a, uint64_t b) \
NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = CMP(0, Tl, O, 0xffffffff) | \
CMP(32, Tl, O, 0xffffffff); \
@@ -317,7 +329,7 @@ IWMMXT_OP_CMP(adds, int8_t, int16_t, int32_t, +)
#define AVGB(SHR) ((( \
((a >> SHR) & 0xff) + ((b >> SHR) & 0xff) + round) >> 1) << SHR)
#define IWMMXT_OP_AVGB(r) \
-uint64_t HELPER(iwmmxt_avgb##r)(uint64_t a, uint64_t b) \
+uint64_t HELPER(iwmmxt_avgb##r)(CPUState *env, uint64_t a, uint64_t b) \
{ \
const int round = r; \
a = AVGB(0) | AVGB(8) | AVGB(16) | AVGB(24) | \
@@ -341,7 +353,7 @@ IWMMXT_OP_AVGB(1)
#define AVGW(SHR) ((( \
((a >> SHR) & 0xffff) + ((b >> SHR) & 0xffff) + round) >> 1) << SHR)
#define IWMMXT_OP_AVGW(r) \
-uint64_t HELPER(iwmmxt_avgw##r)(uint64_t a, uint64_t b) \
+uint64_t HELPER(iwmmxt_avgw##r)(CPUState *env, uint64_t a, uint64_t b) \
{ \
const int round = r; \
a = AVGW(0) | AVGW(16) | AVGW(32) | AVGW(48); \
@@ -452,7 +464,7 @@ uint32_t HELPER(iwmmxt_msbl)(uint64_t x)
}
/* FIXME: Split wCASF setting into a separate op to avoid env use. */
-uint64_t HELPER(iwmmxt_srlw)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_srlw)(CPUState *env, uint64_t x, uint32_t n)
{
x = (((x & (0xffffll << 0)) >> n) & (0xffffll << 0)) |
(((x & (0xffffll << 16)) >> n) & (0xffffll << 16)) |
@@ -464,7 +476,7 @@ uint64_t HELPER(iwmmxt_srlw)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_srll)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_srll)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((x & (0xffffffffll << 0)) >> n) |
((x >> n) & (0xffffffffll << 32));
@@ -473,14 +485,14 @@ uint64_t HELPER(iwmmxt_srll)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_srlq)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_srlq)(CPUState *env, uint64_t x, uint32_t n)
{
x >>= n;
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
-uint64_t HELPER(iwmmxt_sllw)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sllw)(CPUState *env, uint64_t x, uint32_t n)
{
x = (((x & (0xffffll << 0)) << n) & (0xffffll << 0)) |
(((x & (0xffffll << 16)) << n) & (0xffffll << 16)) |
@@ -492,7 +504,7 @@ uint64_t HELPER(iwmmxt_sllw)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_slll)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_slll)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((x << n) & (0xffffffffll << 0)) |
((x & (0xffffffffll << 32)) << n);
@@ -501,14 +513,14 @@ uint64_t HELPER(iwmmxt_slll)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_sllq)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sllq)(CPUState *env, uint64_t x, uint32_t n)
{
x <<= n;
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
-uint64_t HELPER(iwmmxt_sraw)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sraw)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((uint64_t) ((EXTEND16(x >> 0) >> n) & 0xffff) << 0) |
((uint64_t) ((EXTEND16(x >> 16) >> n) & 0xffff) << 16) |
@@ -520,7 +532,7 @@ uint64_t HELPER(iwmmxt_sraw)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_sral)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sral)(CPUState *env, uint64_t x, uint32_t n)
{
x = (((EXTEND32(x >> 0) >> n) & 0xffffffff) << 0) |
(((EXTEND32(x >> 32) >> n) & 0xffffffff) << 32);
@@ -529,14 +541,14 @@ uint64_t HELPER(iwmmxt_sral)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_sraq)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sraq)(CPUState *env, uint64_t x, uint32_t n)
{
x = (int64_t) x >> n;
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
-uint64_t HELPER(iwmmxt_rorw)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_rorw)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((((x & (0xffffll << 0)) >> n) |
((x & (0xffffll << 0)) << (16 - n))) & (0xffffll << 0)) |
@@ -552,7 +564,7 @@ uint64_t HELPER(iwmmxt_rorw)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_rorl)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_rorl)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((x & (0xffffffffll << 0)) >> n) |
((x >> n) & (0xffffffffll << 32)) |
@@ -563,14 +575,14 @@ uint64_t HELPER(iwmmxt_rorl)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_rorq)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_rorq)(CPUState *env, uint64_t x, uint32_t n)
{
x = (x >> n) | (x << (64 - n));
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
-uint64_t HELPER(iwmmxt_shufh)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_shufh)(CPUState *env, uint64_t x, uint32_t n)
{
x = (((x >> ((n << 4) & 0x30)) & 0xffff) << 0) |
(((x >> ((n << 2) & 0x30)) & 0xffff) << 16) |
@@ -583,7 +595,7 @@ uint64_t HELPER(iwmmxt_shufh)(uint64_t x, uint32_t n)
}
/* TODO: Unsigned-Saturation */
-uint64_t HELPER(iwmmxt_packuw)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packuw)(CPUState *env, uint64_t a, uint64_t b)
{
a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) |
(((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) |
@@ -597,7 +609,7 @@ uint64_t HELPER(iwmmxt_packuw)(uint64_t a, uint64_t b)
return a;
}
-uint64_t HELPER(iwmmxt_packul)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packul)(CPUState *env, uint64_t a, uint64_t b)
{
a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) |
(((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48);
@@ -607,7 +619,7 @@ uint64_t HELPER(iwmmxt_packul)(uint64_t a, uint64_t b)
return a;
}
-uint64_t HELPER(iwmmxt_packuq)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packuq)(CPUState *env, uint64_t a, uint64_t b)
{
a = (a & 0xffffffff) | ((b & 0xffffffff) << 32);
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
@@ -616,7 +628,7 @@ uint64_t HELPER(iwmmxt_packuq)(uint64_t a, uint64_t b)
}
/* TODO: Signed-Saturation */
-uint64_t HELPER(iwmmxt_packsw)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packsw)(CPUState *env, uint64_t a, uint64_t b)
{
a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) |
(((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) |
@@ -630,7 +642,7 @@ uint64_t HELPER(iwmmxt_packsw)(uint64_t a, uint64_t b)
return a;
}
-uint64_t HELPER(iwmmxt_packsl)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packsl)(CPUState *env, uint64_t a, uint64_t b)
{
a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) |
(((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48);
@@ -640,7 +652,7 @@ uint64_t HELPER(iwmmxt_packsl)(uint64_t a, uint64_t b)
return a;
}
-uint64_t HELPER(iwmmxt_packsq)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packsq)(CPUState *env, uint64_t a, uint64_t b)
{
a = (a & 0xffffffff) | ((b & 0xffffffff) << 32);
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
diff --git a/target-arm/machine.c b/target-arm/machine.c
index a18b7dc67f..7d4fc545a6 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -44,6 +44,12 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, env->cp15.c7_par);
qemu_put_be32(f, env->cp15.c9_insn);
qemu_put_be32(f, env->cp15.c9_data);
+ qemu_put_be32(f, env->cp15.c9_pmcr);
+ qemu_put_be32(f, env->cp15.c9_pmcnten);
+ qemu_put_be32(f, env->cp15.c9_pmovsr);
+ qemu_put_be32(f, env->cp15.c9_pmxevtyper);
+ qemu_put_be32(f, env->cp15.c9_pmuserenr);
+ qemu_put_be32(f, env->cp15.c9_pminten);
qemu_put_be32(f, env->cp15.c13_fcse);
qemu_put_be32(f, env->cp15.c13_context);
qemu_put_be32(f, env->cp15.c13_tls1);
@@ -152,6 +158,12 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
env->cp15.c7_par = qemu_get_be32(f);
env->cp15.c9_insn = qemu_get_be32(f);
env->cp15.c9_data = qemu_get_be32(f);
+ env->cp15.c9_pmcr = qemu_get_be32(f);
+ env->cp15.c9_pmcnten = qemu_get_be32(f);
+ env->cp15.c9_pmovsr = qemu_get_be32(f);
+ env->cp15.c9_pmxevtyper = qemu_get_be32(f);
+ env->cp15.c9_pmuserenr = qemu_get_be32(f);
+ env->cp15.c9_pminten = qemu_get_be32(f);
env->cp15.c13_fcse = qemu_get_be32(f);
env->cp15.c13_context = qemu_get_be32(f);
env->cp15.c13_tls1 = qemu_get_be32(f);
diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c
index 9165519236..28306279a8 100644
--- a/target-arm/neon_helper.c
+++ b/target-arm/neon_helper.c
@@ -10,7 +10,7 @@
#include <stdio.h>
#include "cpu.h"
-#include "exec.h"
+#include "exec-all.h"
#include "helper.h"
#define SIGNBIT (uint32_t)0x80000000
@@ -18,8 +18,6 @@
#define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] = CPSR_Q
-#define NFS (&env->vfp.standard_fp_status)
-
#define NEON_TYPE1(name, type) \
typedef struct \
{ \
@@ -115,6 +113,10 @@ NEON_TYPE1(u32, uint32_t)
uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \
NEON_VOP_BODY(vtype, n)
+#define NEON_VOP_ENV(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(CPUState *env, uint32_t arg1, uint32_t arg2) \
+NEON_VOP_BODY(vtype, n)
+
/* Pairwise operations. */
/* For 32-bit elements each segment only contains a single element, so
the elementwise and pairwise operations are the same. */
@@ -163,14 +165,14 @@ uint32_t HELPER(glue(neon_,name))(uint32_t arg) \
dest = tmp; \
}} while(0)
#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t)
-NEON_VOP(qadd_u8, neon_u8, 4)
+NEON_VOP_ENV(qadd_u8, neon_u8, 4)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t)
-NEON_VOP(qadd_u16, neon_u16, 2)
+NEON_VOP_ENV(qadd_u16, neon_u16, 2)
#undef NEON_FN
#undef NEON_USAT
-uint32_t HELPER(neon_qadd_u32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_qadd_u32)(CPUState *env, uint32_t a, uint32_t b)
{
uint32_t res = a + b;
if (res < a) {
@@ -180,7 +182,7 @@ uint32_t HELPER(neon_qadd_u32)(uint32_t a, uint32_t b)
return res;
}
-uint64_t HELPER(neon_qadd_u64)(uint64_t src1, uint64_t src2)
+uint64_t HELPER(neon_qadd_u64)(CPUState *env, uint64_t src1, uint64_t src2)
{
uint64_t res;
@@ -205,14 +207,14 @@ uint64_t HELPER(neon_qadd_u64)(uint64_t src1, uint64_t src2)
dest = tmp; \
} while(0)
#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t)
-NEON_VOP(qadd_s8, neon_s8, 4)
+NEON_VOP_ENV(qadd_s8, neon_s8, 4)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t)
-NEON_VOP(qadd_s16, neon_s16, 2)
+NEON_VOP_ENV(qadd_s16, neon_s16, 2)
#undef NEON_FN
#undef NEON_SSAT
-uint32_t HELPER(neon_qadd_s32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_qadd_s32)(CPUState *env, uint32_t a, uint32_t b)
{
uint32_t res = a + b;
if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) {
@@ -222,7 +224,7 @@ uint32_t HELPER(neon_qadd_s32)(uint32_t a, uint32_t b)
return res;
}
-uint64_t HELPER(neon_qadd_s64)(uint64_t src1, uint64_t src2)
+uint64_t HELPER(neon_qadd_s64)(CPUState *env, uint64_t src1, uint64_t src2)
{
uint64_t res;
@@ -243,14 +245,14 @@ uint64_t HELPER(neon_qadd_s64)(uint64_t src1, uint64_t src2)
dest = tmp; \
}} while(0)
#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t)
-NEON_VOP(qsub_u8, neon_u8, 4)
+NEON_VOP_ENV(qsub_u8, neon_u8, 4)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t)
-NEON_VOP(qsub_u16, neon_u16, 2)
+NEON_VOP_ENV(qsub_u16, neon_u16, 2)
#undef NEON_FN
#undef NEON_USAT
-uint32_t HELPER(neon_qsub_u32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_qsub_u32)(CPUState *env, uint32_t a, uint32_t b)
{
uint32_t res = a - b;
if (res > a) {
@@ -260,7 +262,7 @@ uint32_t HELPER(neon_qsub_u32)(uint32_t a, uint32_t b)
return res;
}
-uint64_t HELPER(neon_qsub_u64)(uint64_t src1, uint64_t src2)
+uint64_t HELPER(neon_qsub_u64)(CPUState *env, uint64_t src1, uint64_t src2)
{
uint64_t res;
@@ -286,14 +288,14 @@ uint64_t HELPER(neon_qsub_u64)(uint64_t src1, uint64_t src2)
dest = tmp; \
} while(0)
#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t)
-NEON_VOP(qsub_s8, neon_s8, 4)
+NEON_VOP_ENV(qsub_s8, neon_s8, 4)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t)
-NEON_VOP(qsub_s16, neon_s16, 2)
+NEON_VOP_ENV(qsub_s16, neon_s16, 2)
#undef NEON_FN
#undef NEON_SSAT
-uint32_t HELPER(neon_qsub_s32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_qsub_s32)(CPUState *env, uint32_t a, uint32_t b)
{
uint32_t res = a - b;
if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
@@ -303,7 +305,7 @@ uint32_t HELPER(neon_qsub_s32)(uint32_t a, uint32_t b)
return res;
}
-uint64_t HELPER(neon_qsub_s64)(uint64_t src1, uint64_t src2)
+uint64_t HELPER(neon_qsub_s64)(CPUState *env, uint64_t src1, uint64_t src2)
{
uint64_t res;
@@ -654,12 +656,12 @@ uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shiftop)
dest = ~0; \
} \
}} while (0)
-NEON_VOP(qshl_u8, neon_u8, 4)
-NEON_VOP(qshl_u16, neon_u16, 2)
-NEON_VOP(qshl_u32, neon_u32, 1)
+NEON_VOP_ENV(qshl_u8, neon_u8, 4)
+NEON_VOP_ENV(qshl_u16, neon_u16, 2)
+NEON_VOP_ENV(qshl_u32, neon_u32, 1)
#undef NEON_FN
-uint64_t HELPER(neon_qshl_u64)(uint64_t val, uint64_t shiftop)
+uint64_t HELPER(neon_qshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
{
int8_t shift = (int8_t)shiftop;
if (shift >= 64) {
@@ -709,12 +711,12 @@ uint64_t HELPER(neon_qshl_u64)(uint64_t val, uint64_t shiftop)
} \
} \
}} while (0)
-NEON_VOP(qshl_s8, neon_s8, 4)
-NEON_VOP(qshl_s16, neon_s16, 2)
-NEON_VOP(qshl_s32, neon_s32, 1)
+NEON_VOP_ENV(qshl_s8, neon_s8, 4)
+NEON_VOP_ENV(qshl_s16, neon_s16, 2)
+NEON_VOP_ENV(qshl_s32, neon_s32, 1)
#undef NEON_FN
-uint64_t HELPER(neon_qshl_s64)(uint64_t valop, uint64_t shiftop)
+uint64_t HELPER(neon_qshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
{
int8_t shift = (uint8_t)shiftop;
int64_t val = valop;
@@ -764,26 +766,26 @@ uint64_t HELPER(neon_qshl_s64)(uint64_t valop, uint64_t shiftop)
} \
} \
}} while (0)
-NEON_VOP(qshlu_s8, neon_u8, 4)
-NEON_VOP(qshlu_s16, neon_u16, 2)
+NEON_VOP_ENV(qshlu_s8, neon_u8, 4)
+NEON_VOP_ENV(qshlu_s16, neon_u16, 2)
#undef NEON_FN
-uint32_t HELPER(neon_qshlu_s32)(uint32_t valop, uint32_t shiftop)
+uint32_t HELPER(neon_qshlu_s32)(CPUState *env, uint32_t valop, uint32_t shiftop)
{
if ((int32_t)valop < 0) {
SET_QC();
return 0;
}
- return helper_neon_qshl_u32(valop, shiftop);
+ return helper_neon_qshl_u32(env, valop, shiftop);
}
-uint64_t HELPER(neon_qshlu_s64)(uint64_t valop, uint64_t shiftop)
+uint64_t HELPER(neon_qshlu_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
{
if ((int64_t)valop < 0) {
SET_QC();
return 0;
}
- return helper_neon_qshl_u64(valop, shiftop);
+ return helper_neon_qshl_u64(env, valop, shiftop);
}
/* FIXME: This is wrong. */
@@ -810,13 +812,13 @@ uint64_t HELPER(neon_qshlu_s64)(uint64_t valop, uint64_t shiftop)
dest = ~0; \
} \
}} while (0)
-NEON_VOP(qrshl_u8, neon_u8, 4)
-NEON_VOP(qrshl_u16, neon_u16, 2)
+NEON_VOP_ENV(qrshl_u8, neon_u8, 4)
+NEON_VOP_ENV(qrshl_u16, neon_u16, 2)
#undef NEON_FN
/* The addition of the rounding constant may overflow, so we use an
* intermediate 64 bits accumulator. */
-uint32_t HELPER(neon_qrshl_u32)(uint32_t val, uint32_t shiftop)
+uint32_t HELPER(neon_qrshl_u32)(CPUState *env, uint32_t val, uint32_t shiftop)
{
uint32_t dest;
int8_t shift = (int8_t)shiftop;
@@ -846,7 +848,7 @@ uint32_t HELPER(neon_qrshl_u32)(uint32_t val, uint32_t shiftop)
/* Handling addition overflow with 64 bits inputs values is more
* tricky than with 32 bits values. */
-uint64_t HELPER(neon_qrshl_u64)(uint64_t val, uint64_t shiftop)
+uint64_t HELPER(neon_qrshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
{
int8_t shift = (int8_t)shiftop;
if (shift >= 64) {
@@ -907,13 +909,13 @@ uint64_t HELPER(neon_qrshl_u64)(uint64_t val, uint64_t shiftop)
} \
} \
}} while (0)
-NEON_VOP(qrshl_s8, neon_s8, 4)
-NEON_VOP(qrshl_s16, neon_s16, 2)
+NEON_VOP_ENV(qrshl_s8, neon_s8, 4)
+NEON_VOP_ENV(qrshl_s16, neon_s16, 2)
#undef NEON_FN
/* The addition of the rounding constant may overflow, so we use an
* intermediate 64 bits accumulator. */
-uint32_t HELPER(neon_qrshl_s32)(uint32_t valop, uint32_t shiftop)
+uint32_t HELPER(neon_qrshl_s32)(CPUState *env, uint32_t valop, uint32_t shiftop)
{
int32_t dest;
int32_t val = (int32_t)valop;
@@ -942,7 +944,7 @@ uint32_t HELPER(neon_qrshl_s32)(uint32_t valop, uint32_t shiftop)
/* Handling addition overflow with 64 bits inputs values is more
* tricky than with 32 bits values. */
-uint64_t HELPER(neon_qrshl_s64)(uint64_t valop, uint64_t shiftop)
+uint64_t HELPER(neon_qrshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
{
int8_t shift = (uint8_t)shiftop;
int64_t val = valop;
@@ -1151,10 +1153,10 @@ uint32_t HELPER(neon_cnt_u8)(uint32_t x)
dest = tmp >> 16; \
} while(0)
#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 0)
-NEON_VOP(qdmulh_s16, neon_s16, 2)
+NEON_VOP_ENV(qdmulh_s16, neon_s16, 2)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 1)
-NEON_VOP(qrdmulh_s16, neon_s16, 2)
+NEON_VOP_ENV(qrdmulh_s16, neon_s16, 2)
#undef NEON_FN
#undef NEON_QDMULH16
@@ -1177,10 +1179,10 @@ NEON_VOP(qrdmulh_s16, neon_s16, 2)
dest = tmp >> 32; \
} while(0)
#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 0)
-NEON_VOP(qdmulh_s32, neon_s32, 1)
+NEON_VOP_ENV(qdmulh_s32, neon_s32, 1)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 1)
-NEON_VOP(qrdmulh_s32, neon_s32, 1)
+NEON_VOP_ENV(qrdmulh_s32, neon_s32, 1)
#undef NEON_FN
#undef NEON_QDMULH32
@@ -1221,7 +1223,7 @@ uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x)
return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000);
}
-uint32_t HELPER(neon_unarrow_sat8)(uint64_t x)
+uint32_t HELPER(neon_unarrow_sat8)(CPUState *env, uint64_t x)
{
uint16_t s;
uint8_t d;
@@ -1248,7 +1250,7 @@ uint32_t HELPER(neon_unarrow_sat8)(uint64_t x)
return res;
}
-uint32_t HELPER(neon_narrow_sat_u8)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_u8)(CPUState *env, uint64_t x)
{
uint16_t s;
uint8_t d;
@@ -1271,7 +1273,7 @@ uint32_t HELPER(neon_narrow_sat_u8)(uint64_t x)
return res;
}
-uint32_t HELPER(neon_narrow_sat_s8)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_s8)(CPUState *env, uint64_t x)
{
int16_t s;
uint8_t d;
@@ -1294,7 +1296,7 @@ uint32_t HELPER(neon_narrow_sat_s8)(uint64_t x)
return res;
}
-uint32_t HELPER(neon_unarrow_sat16)(uint64_t x)
+uint32_t HELPER(neon_unarrow_sat16)(CPUState *env, uint64_t x)
{
uint32_t high;
uint32_t low;
@@ -1317,7 +1319,7 @@ uint32_t HELPER(neon_unarrow_sat16)(uint64_t x)
return low | (high << 16);
}
-uint32_t HELPER(neon_narrow_sat_u16)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_u16)(CPUState *env, uint64_t x)
{
uint32_t high;
uint32_t low;
@@ -1334,7 +1336,7 @@ uint32_t HELPER(neon_narrow_sat_u16)(uint64_t x)
return low | (high << 16);
}
-uint32_t HELPER(neon_narrow_sat_s16)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_s16)(CPUState *env, uint64_t x)
{
int32_t low;
int32_t high;
@@ -1351,7 +1353,7 @@ uint32_t HELPER(neon_narrow_sat_s16)(uint64_t x)
return (uint16_t)low | (high << 16);
}
-uint32_t HELPER(neon_unarrow_sat32)(uint64_t x)
+uint32_t HELPER(neon_unarrow_sat32)(CPUState *env, uint64_t x)
{
if (x & 0x8000000000000000ull) {
SET_QC();
@@ -1364,7 +1366,7 @@ uint32_t HELPER(neon_unarrow_sat32)(uint64_t x)
return x;
}
-uint32_t HELPER(neon_narrow_sat_u32)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_u32)(CPUState *env, uint64_t x)
{
if (x > 0xffffffffu) {
SET_QC();
@@ -1373,7 +1375,7 @@ uint32_t HELPER(neon_narrow_sat_u32)(uint64_t x)
return x;
}
-uint32_t HELPER(neon_narrow_sat_s32)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_s32)(CPUState *env, uint64_t x)
{
if ((int64_t)x != (int32_t)x) {
SET_QC();
@@ -1480,7 +1482,7 @@ uint64_t HELPER(neon_subl_u32)(uint64_t a, uint64_t b)
return (a - b) ^ mask;
}
-uint64_t HELPER(neon_addl_saturate_s32)(uint64_t a, uint64_t b)
+uint64_t HELPER(neon_addl_saturate_s32)(CPUState *env, uint64_t a, uint64_t b)
{
uint32_t x, y;
uint32_t low, high;
@@ -1502,7 +1504,7 @@ uint64_t HELPER(neon_addl_saturate_s32)(uint64_t a, uint64_t b)
return low | ((uint64_t)high << 32);
}
-uint64_t HELPER(neon_addl_saturate_s64)(uint64_t a, uint64_t b)
+uint64_t HELPER(neon_addl_saturate_s64)(CPUState *env, uint64_t a, uint64_t b)
{
uint64_t result;
@@ -1678,7 +1680,7 @@ uint64_t HELPER(neon_negl_u64)(uint64_t x)
} else if (x < 0) { \
x = -x; \
}} while (0)
-uint32_t HELPER(neon_qabs_s8)(uint32_t x)
+uint32_t HELPER(neon_qabs_s8)(CPUState *env, uint32_t x)
{
neon_s8 vec;
NEON_UNPACK(neon_s8, vec, x);
@@ -1698,7 +1700,7 @@ uint32_t HELPER(neon_qabs_s8)(uint32_t x)
} else { \
x = -x; \
}} while (0)
-uint32_t HELPER(neon_qneg_s8)(uint32_t x)
+uint32_t HELPER(neon_qneg_s8)(CPUState *env, uint32_t x)
{
neon_s8 vec;
NEON_UNPACK(neon_s8, vec, x);
@@ -1718,7 +1720,7 @@ uint32_t HELPER(neon_qneg_s8)(uint32_t x)
} else if (x < 0) { \
x = -x; \
}} while (0)
-uint32_t HELPER(neon_qabs_s16)(uint32_t x)
+uint32_t HELPER(neon_qabs_s16)(CPUState *env, uint32_t x)
{
neon_s16 vec;
NEON_UNPACK(neon_s16, vec, x);
@@ -1736,7 +1738,7 @@ uint32_t HELPER(neon_qabs_s16)(uint32_t x)
} else { \
x = -x; \
}} while (0)
-uint32_t HELPER(neon_qneg_s16)(uint32_t x)
+uint32_t HELPER(neon_qneg_s16)(CPUState *env, uint32_t x)
{
neon_s16 vec;
NEON_UNPACK(neon_s16, vec, x);
@@ -1747,7 +1749,7 @@ uint32_t HELPER(neon_qneg_s16)(uint32_t x)
}
#undef DO_QNEG16
-uint32_t HELPER(neon_qabs_s32)(uint32_t x)
+uint32_t HELPER(neon_qabs_s32)(CPUState *env, uint32_t x)
{
if (x == SIGNBIT) {
SET_QC();
@@ -1758,7 +1760,7 @@ uint32_t HELPER(neon_qabs_s32)(uint32_t x)
return x;
}
-uint32_t HELPER(neon_qneg_s32)(uint32_t x)
+uint32_t HELPER(neon_qneg_s32)(CPUState *env, uint32_t x)
{
if (x == SIGNBIT) {
SET_QC();
@@ -1770,74 +1772,67 @@ uint32_t HELPER(neon_qneg_s32)(uint32_t x)
}
/* NEON Float helpers. */
-uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return float32_val(float32_min(make_float32(a), make_float32(b), NFS));
+ float_status *fpst = fpstp;
+ return float32_val(float32_min(make_float32(a), make_float32(b), fpst));
}
-uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return float32_val(float32_max(make_float32(a), make_float32(b), NFS));
+ float_status *fpst = fpstp;
+ return float32_val(float32_max(make_float32(a), make_float32(b), fpst));
}
-uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b, void *fpstp)
{
+ float_status *fpst = fpstp;
float32 f0 = make_float32(a);
float32 f1 = make_float32(b);
- return float32_val(float32_abs(float32_sub(f0, f1, NFS)));
-}
-
-uint32_t HELPER(neon_add_f32)(uint32_t a, uint32_t b)
-{
- return float32_val(float32_add(make_float32(a), make_float32(b), NFS));
-}
-
-uint32_t HELPER(neon_sub_f32)(uint32_t a, uint32_t b)
-{
- return float32_val(float32_sub(make_float32(a), make_float32(b), NFS));
-}
-
-uint32_t HELPER(neon_mul_f32)(uint32_t a, uint32_t b)
-{
- return float32_val(float32_mul(make_float32(a), make_float32(b), NFS));
+ return float32_val(float32_abs(float32_sub(f0, f1, fpst)));
}
/* Floating point comparisons produce an integer result.
* Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do.
* Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires.
*/
-uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return -float32_eq_quiet(make_float32(a), make_float32(b), NFS);
+ float_status *fpst = fpstp;
+ return -float32_eq_quiet(make_float32(a), make_float32(b), fpst);
}
-uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return -float32_le(make_float32(b), make_float32(a), NFS);
+ float_status *fpst = fpstp;
+ return -float32_le(make_float32(b), make_float32(a), fpst);
}
-uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return -float32_lt(make_float32(b), make_float32(a), NFS);
+ float_status *fpst = fpstp;
+ return -float32_lt(make_float32(b), make_float32(a), fpst);
}
-uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, void *fpstp)
{
+ float_status *fpst = fpstp;
float32 f0 = float32_abs(make_float32(a));
float32 f1 = float32_abs(make_float32(b));
- return -float32_le(f1, f0, NFS);
+ return -float32_le(f1, f0, fpst);
}
-uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, void *fpstp)
{
+ float_status *fpst = fpstp;
float32 f0 = float32_abs(make_float32(a));
float32 f1 = float32_abs(make_float32(b));
- return -float32_lt(f1, f0, NFS);
+ return -float32_lt(f1, f0, fpst);
}
#define ELEM(V, N, SIZE) (((V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1))
-void HELPER(neon_qunzip8)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qunzip8)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1865,7 +1860,7 @@ void HELPER(neon_qunzip8)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_qunzip16)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qunzip16)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1885,7 +1880,7 @@ void HELPER(neon_qunzip16)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_qunzip32)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qunzip32)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1901,7 +1896,7 @@ void HELPER(neon_qunzip32)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_unzip8)(uint32_t rd, uint32_t rm)
+void HELPER(neon_unzip8)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm = float64_val(env->vfp.regs[rm]);
uint64_t zd = float64_val(env->vfp.regs[rd]);
@@ -1917,7 +1912,7 @@ void HELPER(neon_unzip8)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd] = make_float64(d0);
}
-void HELPER(neon_unzip16)(uint32_t rd, uint32_t rm)
+void HELPER(neon_unzip16)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm = float64_val(env->vfp.regs[rm]);
uint64_t zd = float64_val(env->vfp.regs[rd]);
@@ -1929,7 +1924,7 @@ void HELPER(neon_unzip16)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd] = make_float64(d0);
}
-void HELPER(neon_qzip8)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qzip8)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1957,7 +1952,7 @@ void HELPER(neon_qzip8)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_qzip16)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qzip16)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1977,7 +1972,7 @@ void HELPER(neon_qzip16)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_qzip32)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qzip32)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1993,7 +1988,7 @@ void HELPER(neon_qzip32)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_zip8)(uint32_t rd, uint32_t rm)
+void HELPER(neon_zip8)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm = float64_val(env->vfp.regs[rm]);
uint64_t zd = float64_val(env->vfp.regs[rd]);
@@ -2009,7 +2004,7 @@ void HELPER(neon_zip8)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd] = make_float64(d0);
}
-void HELPER(neon_zip16)(uint32_t rd, uint32_t rm)
+void HELPER(neon_zip16)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm = float64_val(env->vfp.regs[rm]);
uint64_t zd = float64_val(env->vfp.regs[rd]);
diff --git a/target-arm/translate.c b/target-arm/translate.c
index badbc5ff31..34d5e6ef1a 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -892,13 +892,29 @@ static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn,
}
}
+static TCGv_ptr get_fpstatus_ptr(int neon)
+{
+ TCGv_ptr statusptr = tcg_temp_new_ptr();
+ int offset;
+ if (neon) {
+ offset = offsetof(CPUState, vfp.standard_fp_status);
+ } else {
+ offset = offsetof(CPUState, vfp.fp_status);
+ }
+ tcg_gen_addi_ptr(statusptr, cpu_env, offset);
+ return statusptr;
+}
+
#define VFP_OP2(name) \
static inline void gen_vfp_##name(int dp) \
{ \
- if (dp) \
- gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, cpu_env); \
- else \
- gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, cpu_env); \
+ TCGv_ptr fpst = get_fpstatus_ptr(0); \
+ if (dp) { \
+ gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, fpst); \
+ } else { \
+ gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, fpst); \
+ } \
+ tcg_temp_free_ptr(fpst); \
}
VFP_OP2(add)
@@ -911,11 +927,13 @@ VFP_OP2(div)
static inline void gen_vfp_F1_mul(int dp)
{
/* Like gen_vfp_mul() but put result in F1 */
+ TCGv_ptr fpst = get_fpstatus_ptr(0);
if (dp) {
- gen_helper_vfp_muld(cpu_F1d, cpu_F0d, cpu_F1d, cpu_env);
+ gen_helper_vfp_muld(cpu_F1d, cpu_F0d, cpu_F1d, fpst);
} else {
- gen_helper_vfp_muls(cpu_F1s, cpu_F0s, cpu_F1s, cpu_env);
+ gen_helper_vfp_muls(cpu_F1s, cpu_F0s, cpu_F1s, fpst);
}
+ tcg_temp_free_ptr(fpst);
}
static inline void gen_vfp_F1_neg(int dp)
@@ -979,14 +997,7 @@ static inline void gen_vfp_F1_ld0(int dp)
#define VFP_GEN_ITOF(name) \
static inline void gen_vfp_##name(int dp, int neon) \
{ \
- TCGv_ptr statusptr = tcg_temp_new_ptr(); \
- int offset; \
- if (neon) { \
- offset = offsetof(CPUState, vfp.standard_fp_status); \
- } else { \
- offset = offsetof(CPUState, vfp.fp_status); \
- } \
- tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
+ TCGv_ptr statusptr = get_fpstatus_ptr(neon); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0d, cpu_F0s, statusptr); \
} else { \
@@ -1002,14 +1013,7 @@ VFP_GEN_ITOF(sito)
#define VFP_GEN_FTOI(name) \
static inline void gen_vfp_##name(int dp, int neon) \
{ \
- TCGv_ptr statusptr = tcg_temp_new_ptr(); \
- int offset; \
- if (neon) { \
- offset = offsetof(CPUState, vfp.standard_fp_status); \
- } else { \
- offset = offsetof(CPUState, vfp.fp_status); \
- } \
- tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
+ TCGv_ptr statusptr = get_fpstatus_ptr(neon); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0s, cpu_F0d, statusptr); \
} else { \
@@ -1028,14 +1032,7 @@ VFP_GEN_FTOI(tosiz)
static inline void gen_vfp_##name(int dp, int shift, int neon) \
{ \
TCGv tmp_shift = tcg_const_i32(shift); \
- TCGv_ptr statusptr = tcg_temp_new_ptr(); \
- int offset; \
- if (neon) { \
- offset = offsetof(CPUState, vfp.standard_fp_status); \
- } else { \
- offset = offsetof(CPUState, vfp.fp_status); \
- } \
- tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
+ TCGv_ptr statusptr = get_fpstatus_ptr(neon); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, statusptr); \
} else { \
@@ -1206,15 +1203,22 @@ static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \
}
-#define IWMMXT_OP_SIZE(name) \
-IWMMXT_OP(name##b) \
-IWMMXT_OP(name##w) \
-IWMMXT_OP(name##l)
+#define IWMMXT_OP_ENV(name) \
+static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
+{ \
+ iwmmxt_load_reg(cpu_V1, rn); \
+ gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \
+}
-#define IWMMXT_OP_1(name) \
+#define IWMMXT_OP_ENV_SIZE(name) \
+IWMMXT_OP_ENV(name##b) \
+IWMMXT_OP_ENV(name##w) \
+IWMMXT_OP_ENV(name##l)
+
+#define IWMMXT_OP_ENV1(name) \
static inline void gen_op_iwmmxt_##name##_M0(void) \
{ \
- gen_helper_iwmmxt_##name(cpu_M0, cpu_M0); \
+ gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \
}
IWMMXT_OP(maddsq)
@@ -1228,51 +1232,51 @@ IWMMXT_OP(muluhw)
IWMMXT_OP(macsw)
IWMMXT_OP(macuw)
-IWMMXT_OP_SIZE(unpackl)
-IWMMXT_OP_SIZE(unpackh)
-
-IWMMXT_OP_1(unpacklub)
-IWMMXT_OP_1(unpackluw)
-IWMMXT_OP_1(unpacklul)
-IWMMXT_OP_1(unpackhub)
-IWMMXT_OP_1(unpackhuw)
-IWMMXT_OP_1(unpackhul)
-IWMMXT_OP_1(unpacklsb)
-IWMMXT_OP_1(unpacklsw)
-IWMMXT_OP_1(unpacklsl)
-IWMMXT_OP_1(unpackhsb)
-IWMMXT_OP_1(unpackhsw)
-IWMMXT_OP_1(unpackhsl)
-
-IWMMXT_OP_SIZE(cmpeq)
-IWMMXT_OP_SIZE(cmpgtu)
-IWMMXT_OP_SIZE(cmpgts)
-
-IWMMXT_OP_SIZE(mins)
-IWMMXT_OP_SIZE(minu)
-IWMMXT_OP_SIZE(maxs)
-IWMMXT_OP_SIZE(maxu)
-
-IWMMXT_OP_SIZE(subn)
-IWMMXT_OP_SIZE(addn)
-IWMMXT_OP_SIZE(subu)
-IWMMXT_OP_SIZE(addu)
-IWMMXT_OP_SIZE(subs)
-IWMMXT_OP_SIZE(adds)
-
-IWMMXT_OP(avgb0)
-IWMMXT_OP(avgb1)
-IWMMXT_OP(avgw0)
-IWMMXT_OP(avgw1)
+IWMMXT_OP_ENV_SIZE(unpackl)
+IWMMXT_OP_ENV_SIZE(unpackh)
+
+IWMMXT_OP_ENV1(unpacklub)
+IWMMXT_OP_ENV1(unpackluw)
+IWMMXT_OP_ENV1(unpacklul)
+IWMMXT_OP_ENV1(unpackhub)
+IWMMXT_OP_ENV1(unpackhuw)
+IWMMXT_OP_ENV1(unpackhul)
+IWMMXT_OP_ENV1(unpacklsb)
+IWMMXT_OP_ENV1(unpacklsw)
+IWMMXT_OP_ENV1(unpacklsl)
+IWMMXT_OP_ENV1(unpackhsb)
+IWMMXT_OP_ENV1(unpackhsw)
+IWMMXT_OP_ENV1(unpackhsl)
+
+IWMMXT_OP_ENV_SIZE(cmpeq)
+IWMMXT_OP_ENV_SIZE(cmpgtu)
+IWMMXT_OP_ENV_SIZE(cmpgts)
+
+IWMMXT_OP_ENV_SIZE(mins)
+IWMMXT_OP_ENV_SIZE(minu)
+IWMMXT_OP_ENV_SIZE(maxs)
+IWMMXT_OP_ENV_SIZE(maxu)
+
+IWMMXT_OP_ENV_SIZE(subn)
+IWMMXT_OP_ENV_SIZE(addn)
+IWMMXT_OP_ENV_SIZE(subu)
+IWMMXT_OP_ENV_SIZE(addu)
+IWMMXT_OP_ENV_SIZE(subs)
+IWMMXT_OP_ENV_SIZE(adds)
+
+IWMMXT_OP_ENV(avgb0)
+IWMMXT_OP_ENV(avgb1)
+IWMMXT_OP_ENV(avgw0)
+IWMMXT_OP_ENV(avgw1)
IWMMXT_OP(msadb)
-IWMMXT_OP(packuw)
-IWMMXT_OP(packul)
-IWMMXT_OP(packuq)
-IWMMXT_OP(packsw)
-IWMMXT_OP(packsl)
-IWMMXT_OP(packsq)
+IWMMXT_OP_ENV(packuw)
+IWMMXT_OP_ENV(packul)
+IWMMXT_OP_ENV(packuq)
+IWMMXT_OP_ENV(packsw)
+IWMMXT_OP_ENV(packsl)
+IWMMXT_OP_ENV(packsq)
static void gen_op_iwmmxt_set_mup(void)
{
@@ -2006,13 +2010,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
}
switch ((insn >> 22) & 3) {
case 1:
- gen_helper_iwmmxt_srlw(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 2:
- gen_helper_iwmmxt_srll(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 3:
- gen_helper_iwmmxt_srlq(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, tmp);
break;
}
tcg_temp_free_i32(tmp);
@@ -2034,13 +2038,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
}
switch ((insn >> 22) & 3) {
case 1:
- gen_helper_iwmmxt_sraw(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 2:
- gen_helper_iwmmxt_sral(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 3:
- gen_helper_iwmmxt_sraq(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, tmp);
break;
}
tcg_temp_free_i32(tmp);
@@ -2062,13 +2066,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
}
switch ((insn >> 22) & 3) {
case 1:
- gen_helper_iwmmxt_sllw(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 2:
- gen_helper_iwmmxt_slll(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 3:
- gen_helper_iwmmxt_sllq(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, tmp);
break;
}
tcg_temp_free_i32(tmp);
@@ -2090,21 +2094,21 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
tcg_temp_free_i32(tmp);
return 1;
}
- gen_helper_iwmmxt_rorw(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 2:
if (gen_iwmmxt_shift(insn, 0x1f, tmp)) {
tcg_temp_free_i32(tmp);
return 1;
}
- gen_helper_iwmmxt_rorl(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 3:
if (gen_iwmmxt_shift(insn, 0x3f, tmp)) {
tcg_temp_free_i32(tmp);
return 1;
}
- gen_helper_iwmmxt_rorq(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, tmp);
break;
}
tcg_temp_free_i32(tmp);
@@ -2238,7 +2242,7 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
rd0 = (insn >> 16) & 0xf;
gen_op_iwmmxt_movq_M0_wRn(rd0);
tmp = tcg_const_i32(((insn >> 16) & 0xf0) | (insn & 0x0f));
- gen_helper_iwmmxt_shufh(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp);
tcg_temp_free(tmp);
gen_op_iwmmxt_movq_wRn_M0(wrd);
gen_op_iwmmxt_set_mup();
@@ -2467,12 +2471,28 @@ static int disas_cp_insn(CPUState *env, DisasContext *s, uint32_t insn)
return 0;
}
-static int cp15_user_ok(uint32_t insn)
+static int cp15_user_ok(CPUState *env, uint32_t insn)
{
int cpn = (insn >> 16) & 0xf;
int cpm = insn & 0xf;
int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38);
+ if (arm_feature(env, ARM_FEATURE_V7) && cpn == 9) {
+ /* Performance monitor registers fall into three categories:
+ * (a) always UNDEF in usermode
+ * (b) UNDEF only if PMUSERENR.EN is 0
+ * (c) always read OK and UNDEF on write (PMUSERENR only)
+ */
+ if ((cpm == 12 && (op < 6)) ||
+ (cpm == 13 && (op < 3))) {
+ return env->cp15.c9_pmuserenr;
+ } else if (cpm == 14 && op == 0 && (insn & ARM_CP_RW_BIT)) {
+ /* PMUSERENR, read only */
+ return 1;
+ }
+ return 0;
+ }
+
if (cpn == 13 && cpm == 0) {
/* TLS register. */
if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT)))
@@ -2559,7 +2579,7 @@ static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn)
/* cdp */
return 1;
}
- if (IS_USER(s) && !cp15_user_ok(insn)) {
+ if (IS_USER(s) && !cp15_user_ok(env, insn)) {
return 1;
}
@@ -3706,13 +3726,13 @@ static int gen_neon_unzip(int rd, int rm, int size, int q)
if (q) {
switch (size) {
case 0:
- gen_helper_neon_qunzip8(tmp, tmp2);
+ gen_helper_neon_qunzip8(cpu_env, tmp, tmp2);
break;
case 1:
- gen_helper_neon_qunzip16(tmp, tmp2);
+ gen_helper_neon_qunzip16(cpu_env, tmp, tmp2);
break;
case 2:
- gen_helper_neon_qunzip32(tmp, tmp2);
+ gen_helper_neon_qunzip32(cpu_env, tmp, tmp2);
break;
default:
abort();
@@ -3720,10 +3740,10 @@ static int gen_neon_unzip(int rd, int rm, int size, int q)
} else {
switch (size) {
case 0:
- gen_helper_neon_unzip8(tmp, tmp2);
+ gen_helper_neon_unzip8(cpu_env, tmp, tmp2);
break;
case 1:
- gen_helper_neon_unzip16(tmp, tmp2);
+ gen_helper_neon_unzip16(cpu_env, tmp, tmp2);
break;
default:
abort();
@@ -3745,13 +3765,13 @@ static int gen_neon_zip(int rd, int rm, int size, int q)
if (q) {
switch (size) {
case 0:
- gen_helper_neon_qzip8(tmp, tmp2);
+ gen_helper_neon_qzip8(cpu_env, tmp, tmp2);
break;
case 1:
- gen_helper_neon_qzip16(tmp, tmp2);
+ gen_helper_neon_qzip16(cpu_env, tmp, tmp2);
break;
case 2:
- gen_helper_neon_qzip32(tmp, tmp2);
+ gen_helper_neon_qzip32(cpu_env, tmp, tmp2);
break;
default:
abort();
@@ -3759,10 +3779,10 @@ static int gen_neon_zip(int rd, int rm, int size, int q)
} else {
switch (size) {
case 0:
- gen_helper_neon_zip8(tmp, tmp2);
+ gen_helper_neon_zip8(cpu_env, tmp, tmp2);
break;
case 1:
- gen_helper_neon_zip16(tmp, tmp2);
+ gen_helper_neon_zip16(cpu_env, tmp, tmp2);
break;
default:
abort();
@@ -4162,9 +4182,9 @@ static inline void gen_neon_narrow(int size, TCGv dest, TCGv_i64 src)
static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv_i64 src)
{
switch (size) {
- case 0: gen_helper_neon_narrow_sat_s8(dest, src); break;
- case 1: gen_helper_neon_narrow_sat_s16(dest, src); break;
- case 2: gen_helper_neon_narrow_sat_s32(dest, src); break;
+ case 0: gen_helper_neon_narrow_sat_s8(dest, cpu_env, src); break;
+ case 1: gen_helper_neon_narrow_sat_s16(dest, cpu_env, src); break;
+ case 2: gen_helper_neon_narrow_sat_s32(dest, cpu_env, src); break;
default: abort();
}
}
@@ -4172,9 +4192,9 @@ static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv_i64 src)
static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src)
{
switch (size) {
- case 0: gen_helper_neon_narrow_sat_u8(dest, src); break;
- case 1: gen_helper_neon_narrow_sat_u16(dest, src); break;
- case 2: gen_helper_neon_narrow_sat_u32(dest, src); break;
+ case 0: gen_helper_neon_narrow_sat_u8(dest, cpu_env, src); break;
+ case 1: gen_helper_neon_narrow_sat_u16(dest, cpu_env, src); break;
+ case 2: gen_helper_neon_narrow_sat_u32(dest, cpu_env, src); break;
default: abort();
}
}
@@ -4182,9 +4202,9 @@ static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src)
static inline void gen_neon_unarrow_sats(int size, TCGv dest, TCGv_i64 src)
{
switch (size) {
- case 0: gen_helper_neon_unarrow_sat8(dest, src); break;
- case 1: gen_helper_neon_unarrow_sat16(dest, src); break;
- case 2: gen_helper_neon_unarrow_sat32(dest, src); break;
+ case 0: gen_helper_neon_unarrow_sat8(dest, cpu_env, src); break;
+ case 1: gen_helper_neon_unarrow_sat16(dest, cpu_env, src); break;
+ case 2: gen_helper_neon_unarrow_sat32(dest, cpu_env, src); break;
default: abort();
}
}
@@ -4276,8 +4296,8 @@ static inline void gen_neon_negl(TCGv_i64 var, int size)
static inline void gen_neon_addl_saturate(TCGv_i64 op0, TCGv_i64 op1, int size)
{
switch (size) {
- case 1: gen_helper_neon_addl_saturate_s32(op0, op0, op1); break;
- case 2: gen_helper_neon_addl_saturate_s64(op0, op0, op1); break;
+ case 1: gen_helper_neon_addl_saturate_s32(op0, cpu_env, op0, op1); break;
+ case 2: gen_helper_neon_addl_saturate_s64(op0, cpu_env, op0, op1); break;
default: abort();
}
}
@@ -4553,16 +4573,20 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
switch (op) {
case NEON_3R_VQADD:
if (u) {
- gen_helper_neon_qadd_u64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qadd_u64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
} else {
- gen_helper_neon_qadd_s64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qadd_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
}
break;
case NEON_3R_VQSUB:
if (u) {
- gen_helper_neon_qsub_u64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qsub_u64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
} else {
- gen_helper_neon_qsub_s64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qsub_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
}
break;
case NEON_3R_VSHL:
@@ -4574,9 +4598,11 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
break;
case NEON_3R_VQSHL:
if (u) {
- gen_helper_neon_qshl_u64(cpu_V0, cpu_V1, cpu_V0);
+ gen_helper_neon_qshl_u64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
} else {
- gen_helper_neon_qshl_s64(cpu_V0, cpu_V1, cpu_V0);
+ gen_helper_neon_qshl_s64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
}
break;
case NEON_3R_VRSHL:
@@ -4588,9 +4614,11 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
break;
case NEON_3R_VQRSHL:
if (u) {
- gen_helper_neon_qrshl_u64(cpu_V0, cpu_V1, cpu_V0);
+ gen_helper_neon_qrshl_u64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
} else {
- gen_helper_neon_qrshl_s64(cpu_V0, cpu_V1, cpu_V0);
+ gen_helper_neon_qrshl_s64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
}
break;
case NEON_3R_VADD_VSUB:
@@ -4688,7 +4716,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
GEN_NEON_INTEGER_OP(hadd);
break;
case NEON_3R_VQADD:
- GEN_NEON_INTEGER_OP(qadd);
+ GEN_NEON_INTEGER_OP_ENV(qadd);
break;
case NEON_3R_VRHADD:
GEN_NEON_INTEGER_OP(rhadd);
@@ -4731,7 +4759,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
GEN_NEON_INTEGER_OP(hsub);
break;
case NEON_3R_VQSUB:
- GEN_NEON_INTEGER_OP(qsub);
+ GEN_NEON_INTEGER_OP_ENV(qsub);
break;
case NEON_3R_VCGT:
GEN_NEON_INTEGER_OP(cgt);
@@ -4743,13 +4771,13 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
GEN_NEON_INTEGER_OP(shl);
break;
case NEON_3R_VQSHL:
- GEN_NEON_INTEGER_OP(qshl);
+ GEN_NEON_INTEGER_OP_ENV(qshl);
break;
case NEON_3R_VRSHL:
GEN_NEON_INTEGER_OP(rshl);
break;
case NEON_3R_VQRSHL:
- GEN_NEON_INTEGER_OP(qrshl);
+ GEN_NEON_INTEGER_OP_ENV(qrshl);
break;
case NEON_3R_VMAX:
GEN_NEON_INTEGER_OP(max);
@@ -4831,14 +4859,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
case NEON_3R_VQDMULH_VQRDMULH: /* Multiply high. */
if (!u) { /* VQDMULH */
switch (size) {
- case 1: gen_helper_neon_qdmulh_s16(tmp, tmp, tmp2); break;
- case 2: gen_helper_neon_qdmulh_s32(tmp, tmp, tmp2); break;
+ case 1:
+ gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2);
+ break;
+ case 2:
+ gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2);
+ break;
default: abort();
}
} else { /* VQRDMULH */
switch (size) {
- case 1: gen_helper_neon_qrdmulh_s16(tmp, tmp, tmp2); break;
- case 2: gen_helper_neon_qrdmulh_s32(tmp, tmp, tmp2); break;
+ case 1:
+ gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2);
+ break;
+ case 2:
+ gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2);
+ break;
default: abort();
}
}
@@ -4852,57 +4888,78 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
}
break;
case NEON_3R_FLOAT_ARITH: /* Floating point arithmetic. */
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
switch ((u << 2) | size) {
case 0: /* VADD */
- gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ case 4: /* VPADD */
+ gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus);
break;
case 2: /* VSUB */
- gen_helper_neon_sub_f32(tmp, tmp, tmp2);
- break;
- case 4: /* VPADD */
- gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ gen_helper_vfp_subs(tmp, tmp, tmp2, fpstatus);
break;
case 6: /* VABD */
- gen_helper_neon_abd_f32(tmp, tmp, tmp2);
+ gen_helper_neon_abd_f32(tmp, tmp, tmp2, fpstatus);
break;
default:
abort();
}
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_FLOAT_MULTIPLY:
- gen_helper_neon_mul_f32(tmp, tmp, tmp2);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_vfp_muls(tmp, tmp, tmp2, fpstatus);
if (!u) {
tcg_temp_free_i32(tmp2);
tmp2 = neon_load_reg(rd, pass);
if (size == 0) {
- gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus);
} else {
- gen_helper_neon_sub_f32(tmp, tmp2, tmp);
+ gen_helper_vfp_subs(tmp, tmp2, tmp, fpstatus);
}
}
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_FLOAT_CMP:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
if (!u) {
- gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
+ gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus);
} else {
- if (size == 0)
- gen_helper_neon_cge_f32(tmp, tmp, tmp2);
- else
- gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
+ if (size == 0) {
+ gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus);
+ } else {
+ gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus);
+ }
}
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_FLOAT_ACMP:
- if (size == 0)
- gen_helper_neon_acge_f32(tmp, tmp, tmp2);
- else
- gen_helper_neon_acgt_f32(tmp, tmp, tmp2);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ if (size == 0) {
+ gen_helper_neon_acge_f32(tmp, tmp, tmp2, fpstatus);
+ } else {
+ gen_helper_neon_acgt_f32(tmp, tmp, tmp2, fpstatus);
+ }
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_FLOAT_MINMAX:
- if (size == 0)
- gen_helper_neon_max_f32(tmp, tmp, tmp2);
- else
- gen_helper_neon_min_f32(tmp, tmp, tmp2);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ if (size == 0) {
+ gen_helper_neon_max_f32(tmp, tmp, tmp2, fpstatus);
+ } else {
+ gen_helper_neon_min_f32(tmp, tmp, tmp2, fpstatus);
+ }
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_VRECPS_VRSQRTS:
if (size == 0)
gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env);
@@ -5009,14 +5066,15 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
break;
case 6: /* VQSHLU */
- gen_helper_neon_qshlu_s64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qshlu_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
break;
case 7: /* VQSHL */
if (u) {
- gen_helper_neon_qshl_u64(cpu_V0,
+ gen_helper_neon_qshl_u64(cpu_V0, cpu_env,
cpu_V0, cpu_V1);
} else {
- gen_helper_neon_qshl_s64(cpu_V0,
+ gen_helper_neon_qshl_s64(cpu_V0, cpu_env,
cpu_V0, cpu_V1);
}
break;
@@ -5068,20 +5126,23 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
case 6: /* VQSHLU */
switch (size) {
case 0:
- gen_helper_neon_qshlu_s8(tmp, tmp, tmp2);
+ gen_helper_neon_qshlu_s8(tmp, cpu_env,
+ tmp, tmp2);
break;
case 1:
- gen_helper_neon_qshlu_s16(tmp, tmp, tmp2);
+ gen_helper_neon_qshlu_s16(tmp, cpu_env,
+ tmp, tmp2);
break;
case 2:
- gen_helper_neon_qshlu_s32(tmp, tmp, tmp2);
+ gen_helper_neon_qshlu_s32(tmp, cpu_env,
+ tmp, tmp2);
break;
default:
abort();
}
break;
case 7: /* VQSHL */
- GEN_NEON_INTEGER_OP(qshl);
+ GEN_NEON_INTEGER_OP_ENV(qshl);
break;
}
tcg_temp_free_i32(tmp2);
@@ -5590,18 +5651,20 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
tmp2 = neon_load_reg(rn, pass);
if (op == 12) {
if (size == 1) {
- gen_helper_neon_qdmulh_s16(tmp, tmp, tmp2);
+ gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2);
} else {
- gen_helper_neon_qdmulh_s32(tmp, tmp, tmp2);
+ gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2);
}
} else if (op == 13) {
if (size == 1) {
- gen_helper_neon_qrdmulh_s16(tmp, tmp, tmp2);
+ gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2);
} else {
- gen_helper_neon_qrdmulh_s32(tmp, tmp, tmp2);
+ gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2);
}
} else if (op & 1) {
- gen_helper_neon_mul_f32(tmp, tmp, tmp2);
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_vfp_muls(tmp, tmp, tmp2, fpstatus);
+ tcg_temp_free_ptr(fpstatus);
} else {
switch (size) {
case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
@@ -5619,14 +5682,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
gen_neon_add(size, tmp, tmp2);
break;
case 1:
- gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case 4:
gen_neon_rsb(size, tmp, tmp2);
break;
case 5:
- gen_helper_neon_sub_f32(tmp, tmp2, tmp);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_vfp_subs(tmp, tmp2, tmp, fpstatus);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
default:
abort();
}
@@ -5960,17 +6031,29 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
break;
case NEON_2RM_VQABS:
switch (size) {
- case 0: gen_helper_neon_qabs_s8(tmp, tmp); break;
- case 1: gen_helper_neon_qabs_s16(tmp, tmp); break;
- case 2: gen_helper_neon_qabs_s32(tmp, tmp); break;
+ case 0:
+ gen_helper_neon_qabs_s8(tmp, cpu_env, tmp);
+ break;
+ case 1:
+ gen_helper_neon_qabs_s16(tmp, cpu_env, tmp);
+ break;
+ case 2:
+ gen_helper_neon_qabs_s32(tmp, cpu_env, tmp);
+ break;
default: abort();
}
break;
case NEON_2RM_VQNEG:
switch (size) {
- case 0: gen_helper_neon_qneg_s8(tmp, tmp); break;
- case 1: gen_helper_neon_qneg_s16(tmp, tmp); break;
- case 2: gen_helper_neon_qneg_s32(tmp, tmp); break;
+ case 0:
+ gen_helper_neon_qneg_s8(tmp, cpu_env, tmp);
+ break;
+ case 1:
+ gen_helper_neon_qneg_s16(tmp, cpu_env, tmp);
+ break;
+ case 2:
+ gen_helper_neon_qneg_s32(tmp, cpu_env, tmp);
+ break;
default: abort();
}
break;
@@ -6024,30 +6107,50 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
tcg_temp_free(tmp2);
break;
case NEON_2RM_VCGT0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
+ gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VCGE0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_cge_f32(tmp, tmp, tmp2);
+ gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VCEQ0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
+ gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VCLE0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_cge_f32(tmp, tmp2, tmp);
+ gen_helper_neon_cge_f32(tmp, tmp2, tmp, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VCLT0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_cgt_f32(tmp, tmp2, tmp);
+ gen_helper_neon_cgt_f32(tmp, tmp2, tmp, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VABS_F:
gen_vfp_abs(0);
break;
diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h
index 51a13e38d1..76f4fc4a7a 100644
--- a/target-microblaze/cpu.h
+++ b/target-microblaze/cpu.h
@@ -347,8 +347,8 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
}
#if !defined(CONFIG_USER_ONLY)
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size);
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size);
#endif
static inline bool cpu_has_work(CPUState *env)
diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c
index 1a0a476a62..664ffe5990 100644
--- a/target-microblaze/op_helper.c
+++ b/target-microblaze/op_helper.c
@@ -488,20 +488,14 @@ void helper_mmu_write(uint32_t rn, uint32_t v)
mmu_write(env, rn, v);
}
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size)
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size)
{
CPUState *saved_env;
- if (!cpu_single_env) {
- /* XXX: ??? */
- return;
- }
-
- /* XXX: hack to restore env in all cases, even if not called from
- generated code */
saved_env = env;
- env = cpu_single_env;
+ env = env1;
+
qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n",
addr, is_write, is_exec);
if (!(env->sregs[SR_MSR] & MSR_EE)) {
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index b0ac4da5a7..33be2962a2 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -493,8 +493,8 @@ void r4k_helper_tlbwr (void);
void r4k_helper_tlbp (void);
void r4k_helper_tlbr (void);
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int unused, int size);
+void cpu_unassigned_access(CPUState *env, target_phys_addr_t addr,
+ int is_write, int is_exec, int unused, int size);
#endif
void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf);
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 6b966b1849..01315ef0dc 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -1980,9 +1980,11 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
env = saved_env;
}
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int unused, int size)
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int unused, int size)
{
+ env = env1;
+
if (is_exec)
helper_raise_exception(EXCP_IBE);
else
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index 22ee2743d7..a51863cf07 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -290,18 +290,51 @@ enum {
#endif
#define TTE_VALID_BIT (1ULL << 63)
+#define TTE_NFO_BIT (1ULL << 60)
#define TTE_USED_BIT (1ULL << 41)
#define TTE_LOCKED_BIT (1ULL << 6)
+#define TTE_SIDEEFFECT_BIT (1ULL << 3)
+#define TTE_PRIV_BIT (1ULL << 2)
+#define TTE_W_OK_BIT (1ULL << 1)
#define TTE_GLOBAL_BIT (1ULL << 0)
#define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT)
+#define TTE_IS_NFO(tte) ((tte) & TTE_NFO_BIT)
#define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT)
#define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT)
+#define TTE_IS_SIDEEFFECT(tte) ((tte) & TTE_SIDEEFFECT_BIT)
+#define TTE_IS_PRIV(tte) ((tte) & TTE_PRIV_BIT)
+#define TTE_IS_W_OK(tte) ((tte) & TTE_W_OK_BIT)
#define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT)
#define TTE_SET_USED(tte) ((tte) |= TTE_USED_BIT)
#define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT)
+#define TTE_PGSIZE(tte) (((tte) >> 61) & 3ULL)
+#define TTE_PA(tte) ((tte) & 0x1ffffffe000ULL)
+
+#define SFSR_NF_BIT (1ULL << 24) /* JPS1 NoFault */
+#define SFSR_TM_BIT (1ULL << 15) /* JPS1 TLB Miss */
+#define SFSR_FT_VA_IMMU_BIT (1ULL << 13) /* USIIi VA out of range (IMMU) */
+#define SFSR_FT_VA_DMMU_BIT (1ULL << 12) /* USIIi VA out of range (DMMU) */
+#define SFSR_FT_NFO_BIT (1ULL << 11) /* NFO page access */
+#define SFSR_FT_ILL_BIT (1ULL << 10) /* illegal LDA/STA ASI */
+#define SFSR_FT_ATOMIC_BIT (1ULL << 9) /* atomic op on noncacheable area */
+#define SFSR_FT_NF_E_BIT (1ULL << 8) /* NF access on side effect area */
+#define SFSR_FT_PRIV_BIT (1ULL << 7) /* privilege violation */
+#define SFSR_PR_BIT (1ULL << 3) /* privilege mode */
+#define SFSR_WRITE_BIT (1ULL << 2) /* write access mode */
+#define SFSR_OW_BIT (1ULL << 1) /* status overwritten */
+#define SFSR_VALID_BIT (1ULL << 0) /* status valid */
+
+#define SFSR_ASI_SHIFT 16 /* 23:16 ASI value */
+#define SFSR_ASI_MASK (0xffULL << SFSR_ASI_SHIFT)
+#define SFSR_CT_PRIMARY (0ULL << 4) /* 5:4 context type */
+#define SFSR_CT_SECONDARY (1ULL << 4)
+#define SFSR_CT_NUCLEUS (2ULL << 4)
+#define SFSR_CT_NOTRANS (3ULL << 4)
+#define SFSR_CT_MASK (3ULL << 4)
+
typedef struct SparcTLBEntry {
uint64_t tag;
uint64_t tte;
@@ -510,12 +543,14 @@ static inline int tlb_compare_context(const SparcTLBEntry *tlb,
/* cpu-exec.c */
#if !defined(CONFIG_USER_ONLY)
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size);
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size);
+#if defined(TARGET_SPARC64)
target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
int mmu_idx);
#endif
+#endif
int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
#define cpu_init cpu_sparc_init
diff --git a/target-sparc/helper.c b/target-sparc/helper.c
index 7eea1acbd5..acc07f5cba 100644
--- a/target-sparc/helper.c
+++ b/target-sparc/helper.c
@@ -378,7 +378,7 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb,
{
uint64_t mask;
- switch ((tlb->tte >> 61) & 3) {
+ switch (TTE_PGSIZE(tlb->tte)) {
default:
case 0x0: // 8k
mask = 0xffffffffffffe000ULL;
@@ -413,6 +413,7 @@ static int get_physical_address_data(CPUState *env,
{
unsigned int i;
uint64_t context;
+ uint64_t sfsr = 0;
int is_user = (mmu_idx == MMU_USER_IDX ||
mmu_idx == MMU_USER_SECONDARY_IDX);
@@ -427,54 +428,88 @@ static int get_physical_address_data(CPUState *env,
case MMU_USER_IDX:
case MMU_KERNEL_IDX:
context = env->dmmu.mmu_primary_context & 0x1fff;
+ sfsr |= SFSR_CT_PRIMARY;
break;
case MMU_USER_SECONDARY_IDX:
case MMU_KERNEL_SECONDARY_IDX:
context = env->dmmu.mmu_secondary_context & 0x1fff;
+ sfsr |= SFSR_CT_SECONDARY;
break;
case MMU_NUCLEUS_IDX:
+ sfsr |= SFSR_CT_NUCLEUS;
+ /* FALLTHRU */
default:
context = 0;
break;
}
+ if (rw == 1) {
+ sfsr |= SFSR_WRITE_BIT;
+ } else if (rw == 4) {
+ sfsr |= SFSR_NF_BIT;
+ }
+
for (i = 0; i < 64; i++) {
// ctx match, vaddr match, valid?
if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) {
-
- uint8_t fault_type = 0;
+ int do_fault = 0;
// access ok?
- if ((env->dtlb[i].tte & 0x4) && is_user) {
- fault_type |= 1; /* privilege violation */
- env->exception_index = TT_DFAULT;
+ /* multiple bits in SFSR.FT may be set on TT_DFAULT */
+ if (TTE_IS_PRIV(env->dtlb[i].tte) && is_user) {
+ do_fault = 1;
+ sfsr |= SFSR_FT_PRIV_BIT; /* privilege violation */
DPRINTF_MMU("DFAULT at %" PRIx64 " context %" PRIx64
" mmu_idx=%d tl=%d\n",
address, context, mmu_idx, env->tl);
- } else if (!(env->dtlb[i].tte & 0x2) && (rw == 1)) {
+ }
+ if (rw == 4) {
+ if (TTE_IS_SIDEEFFECT(env->dtlb[i].tte)) {
+ do_fault = 1;
+ sfsr |= SFSR_FT_NF_E_BIT;
+ }
+ } else {
+ if (TTE_IS_NFO(env->dtlb[i].tte)) {
+ do_fault = 1;
+ sfsr |= SFSR_FT_NFO_BIT;
+ }
+ }
+
+ if (do_fault) {
+ /* faults above are reported with TT_DFAULT. */
+ env->exception_index = TT_DFAULT;
+ } else if (!TTE_IS_W_OK(env->dtlb[i].tte) && (rw == 1)) {
+ do_fault = 1;
env->exception_index = TT_DPROT;
DPRINTF_MMU("DPROT at %" PRIx64 " context %" PRIx64
" mmu_idx=%d tl=%d\n",
address, context, mmu_idx, env->tl);
- } else {
+ }
+
+ if (!do_fault) {
*prot = PAGE_READ;
- if (env->dtlb[i].tte & 0x2)
+ if (TTE_IS_W_OK(env->dtlb[i].tte)) {
*prot |= PAGE_WRITE;
+ }
TTE_SET_USED(env->dtlb[i].tte);
return 0;
}
- if (env->dmmu.sfsr & 1) /* Fault status register */
- env->dmmu.sfsr = 2; /* overflow (not read before
- another fault) */
+ if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */
+ sfsr |= SFSR_OW_BIT; /* overflow (not read before
+ another fault) */
+ }
- env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1;
+ if (env->pstate & PS_PRIV) {
+ sfsr |= SFSR_PR_BIT;
+ }
- env->dmmu.sfsr |= (fault_type << 7);
+ /* FIXME: ASI field in SFSR must be set */
+ env->dmmu.sfsr = sfsr | SFSR_VALID_BIT;
env->dmmu.sfar = address; /* Fault address register */
@@ -487,6 +522,11 @@ static int get_physical_address_data(CPUState *env,
DPRINTF_MMU("DMISS at %" PRIx64 " context %" PRIx64 "\n",
address, context);
+ /*
+ * On MMU misses:
+ * - UltraSPARC IIi: SFSR and SFAR unmodified
+ * - JPS1: SFAR updated and some fields of SFSR updated
+ */
env->dmmu.tag_access = (address & ~0x1fffULL) | context;
env->exception_index = TT_DMISS;
return 1;
@@ -522,11 +562,23 @@ static int get_physical_address_code(CPUState *env,
if (ultrasparc_tag_match(&env->itlb[i],
address, context, physical)) {
// access ok?
- if ((env->itlb[i].tte & 0x4) && is_user) {
- if (env->immu.sfsr) /* Fault status register */
- env->immu.sfsr = 2; /* overflow (not read before
- another fault) */
- env->immu.sfsr |= (is_user << 3) | 1;
+ if (TTE_IS_PRIV(env->itlb[i].tte) && is_user) {
+ /* Fault status register */
+ if (env->immu.sfsr & SFSR_VALID_BIT) {
+ env->immu.sfsr = SFSR_OW_BIT; /* overflow (not read before
+ another fault) */
+ } else {
+ env->immu.sfsr = 0;
+ }
+ if (env->pstate & PS_PRIV) {
+ env->immu.sfsr |= SFSR_PR_BIT;
+ }
+ if (env->tl > 0) {
+ env->immu.sfsr |= SFSR_CT_NUCLEUS;
+ }
+
+ /* FIXME: ASI field in SFSR must be set */
+ env->immu.sfsr |= SFSR_FT_PRIV_BIT | SFSR_VALID_BIT;
env->exception_index = TT_TFAULT;
env->immu.tag_access = (address & ~0x1fffULL) | context;
@@ -632,7 +684,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
} else {
(*cpu_fprintf)(f, "DMMU dump\n");
for (i = 0; i < 64; i++) {
- switch ((env->dtlb[i].tte >> 61) & 3) {
+ switch (TTE_PGSIZE(env->dtlb[i].tte)) {
default:
case 0x0:
mask = " 8k";
@@ -647,16 +699,17 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
mask = " 4M";
break;
}
- if ((env->dtlb[i].tte & 0x8000000000000000ULL) != 0) {
+ if (TTE_IS_VALID(env->dtlb[i].tte)) {
(*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64
", %s, %s, %s, %s, ctx %" PRId64 " %s\n",
i,
env->dtlb[i].tag & (uint64_t)~0x1fffULL,
- env->dtlb[i].tte & (uint64_t)0x1ffffffe000ULL,
+ TTE_PA(env->dtlb[i].tte),
mask,
- env->dtlb[i].tte & 0x4? "priv": "user",
- env->dtlb[i].tte & 0x2? "RW": "RO",
- env->dtlb[i].tte & 0x40? "locked": "unlocked",
+ TTE_IS_PRIV(env->dtlb[i].tte) ? "priv" : "user",
+ TTE_IS_W_OK(env->dtlb[i].tte) ? "RW" : "RO",
+ TTE_IS_LOCKED(env->dtlb[i].tte) ?
+ "locked" : "unlocked",
env->dtlb[i].tag & (uint64_t)0x1fffULL,
TTE_IS_GLOBAL(env->dtlb[i].tte)?
"global" : "local");
@@ -668,7 +721,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
} else {
(*cpu_fprintf)(f, "IMMU dump\n");
for (i = 0; i < 64; i++) {
- switch ((env->itlb[i].tte >> 61) & 3) {
+ switch (TTE_PGSIZE(env->itlb[i].tte)) {
default:
case 0x0:
mask = " 8k";
@@ -683,15 +736,16 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
mask = " 4M";
break;
}
- if ((env->itlb[i].tte & 0x8000000000000000ULL) != 0) {
+ if (TTE_IS_VALID(env->itlb[i].tte)) {
(*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64
", %s, %s, %s, ctx %" PRId64 " %s\n",
i,
env->itlb[i].tag & (uint64_t)~0x1fffULL,
- env->itlb[i].tte & (uint64_t)0x1ffffffe000ULL,
+ TTE_PA(env->itlb[i].tte),
mask,
- env->itlb[i].tte & 0x4? "priv": "user",
- env->itlb[i].tte & 0x40? "locked": "unlocked",
+ TTE_IS_PRIV(env->itlb[i].tte) ? "priv" : "user",
+ TTE_IS_LOCKED(env->itlb[i].tte) ?
+ "locked" : "unlocked",
env->itlb[i].tag & (uint64_t)0x1fffULL,
TTE_IS_GLOBAL(env->itlb[i].tte)?
"global" : "local");
@@ -705,26 +759,43 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
#if !defined(CONFIG_USER_ONLY)
+static int cpu_sparc_get_phys_page(CPUState *env, target_phys_addr_t *phys,
+ target_ulong addr, int rw, int mmu_idx)
+{
+ target_ulong page_size;
+ int prot, access_index;
+
+ return get_physical_address(env, phys, &prot, &access_index, addr, rw,
+ mmu_idx, &page_size);
+}
+
+#if defined(TARGET_SPARC64)
target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
int mmu_idx)
{
target_phys_addr_t phys_addr;
- target_ulong page_size;
- int prot, access_index;
- if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2,
- mmu_idx, &page_size) != 0)
- if (get_physical_address(env, &phys_addr, &prot, &access_index, addr,
- 0, mmu_idx, &page_size) != 0)
- return -1;
- if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED)
+ if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 4, mmu_idx) != 0) {
return -1;
+ }
return phys_addr;
}
+#endif
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
{
- return cpu_get_phys_page_nofault(env, addr, cpu_mmu_index(env));
+ target_phys_addr_t phys_addr;
+ int mmu_idx = cpu_mmu_index(env);
+
+ if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) {
+ if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) {
+ return -1;
+ }
+ }
+ if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) {
+ return -1;
+ }
+ return phys_addr;
}
#endif
diff --git a/target-sparc/helper.h b/target-sparc/helper.h
index 023f4d6023..2d36af3a31 100644
--- a/target-sparc/helper.h
+++ b/target-sparc/helper.h
@@ -148,8 +148,8 @@ F_HELPER_0_0(expand);
VIS_HELPER(padd);
VIS_HELPER(psub);
#define VIS_CMPHELPER(name) \
- F_HELPER_0_0(name##16); \
- F_HELPER_0_0(name##32)
+ DEF_HELPER_0(f##name##16, i64); \
+ DEF_HELPER_0(f##name##32, i64)
VIS_CMPHELPER(cmpgt);
VIS_CMPHELPER(cmpeq);
VIS_CMPHELPER(cmple);
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
index 15af27ba1f..8962e38219 100644
--- a/target-sparc/op_helper.c
+++ b/target-sparc/op_helper.c
@@ -79,9 +79,14 @@
#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */
#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */
-#if defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64)
+#if !defined(CONFIG_USER_ONLY)
+static void do_unassigned_access(target_phys_addr_t addr, int is_write,
+ int is_exec, int is_asi, int size);
+#else
+#ifdef TARGET_SPARC64
static void do_unassigned_access(target_ulong addr, int is_write, int is_exec,
- int is_asi, int size);
+ int is_asi, int size);
+#endif
#endif
#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
@@ -287,7 +292,8 @@ static inline int is_translating_asi(int asi)
*/
switch (asi) {
case 0x04 ... 0x11:
- case 0x18 ... 0x19:
+ case 0x16 ... 0x19:
+ case 0x1E ... 0x1F:
case 0x24 ... 0x2C:
case 0x70 ... 0x73:
case 0x78 ... 0x79:
@@ -525,6 +531,7 @@ typedef union {
uint16_t w[4];
int16_t sw[4];
uint32_t l[2];
+ uint64_t ll;
float64 d;
} vis64;
@@ -789,32 +796,34 @@ VIS_HELPER(helper_fpadd, FADD)
VIS_HELPER(helper_fpsub, FSUB)
#define VIS_CMPHELPER(name, F) \
- void name##16(void) \
+ uint64_t name##16(void) \
{ \
vis64 s, d; \
\
s.d = DT0; \
d.d = DT1; \
\
- d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0))? 1: 0; \
- d.VIS_W64(0) |= F(d.VIS_W64(1), s.VIS_W64(1))? 2: 0; \
- d.VIS_W64(0) |= F(d.VIS_W64(2), s.VIS_W64(2))? 4: 0; \
- d.VIS_W64(0) |= F(d.VIS_W64(3), s.VIS_W64(3))? 8: 0; \
+ d.VIS_W64(0) = F(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0; \
+ d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0; \
+ d.VIS_W64(0) |= F(s.VIS_W64(2), d.VIS_W64(2)) ? 4 : 0; \
+ d.VIS_W64(0) |= F(s.VIS_W64(3), d.VIS_W64(3)) ? 8 : 0; \
+ d.VIS_W64(1) = d.VIS_W64(2) = d.VIS_W64(3) = 0; \
\
- DT0 = d.d; \
+ return d.ll; \
} \
\
- void name##32(void) \
+ uint64_t name##32(void) \
{ \
vis64 s, d; \
\
s.d = DT0; \
d.d = DT1; \
\
- d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0))? 1: 0; \
- d.VIS_L64(0) |= F(d.VIS_L64(1), s.VIS_L64(1))? 2: 0; \
+ d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0; \
+ d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0; \
+ d.VIS_L64(1) = 0; \
\
- DT0 = d.d; \
+ return d.ll; \
}
#define FCMPGT(a, b) ((a) > (b))
@@ -2558,24 +2567,30 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
helper_check_align(addr, size - 1);
addr = asi_address_mask(env, asi, addr);
- switch (asi) {
- case 0x82: // Primary no-fault
- case 0x8a: // Primary no-fault LE
- case 0x83: // Secondary no-fault
- case 0x8b: // Secondary no-fault LE
- {
- /* secondary space access has lowest asi bit equal to 1 */
- int access_mmu_idx = ( asi & 1 ) ? MMU_KERNEL_IDX
- : MMU_KERNEL_SECONDARY_IDX;
+ /* process nonfaulting loads first */
+ if ((asi & 0xf6) == 0x82) {
+ int mmu_idx;
+
+ /* secondary space access has lowest asi bit equal to 1 */
+ if (env->pstate & PS_PRIV) {
+ mmu_idx = (asi & 1) ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX;
+ } else {
+ mmu_idx = (asi & 1) ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX;
+ }
- if (cpu_get_phys_page_nofault(env, addr, access_mmu_idx) == -1ULL) {
+ if (cpu_get_phys_page_nofault(env, addr, mmu_idx) == -1ULL) {
#ifdef DEBUG_ASI
- dump_asi("read ", last_addr, asi, size, ret);
+ dump_asi("read ", last_addr, asi, size, ret);
#endif
- return 0;
- }
+ /* env->exception_index is set in get_physical_address_data(). */
+ raise_exception(env->exception_index);
}
- // Fall through
+
+ /* convert nonfaulting load ASIs to normal load ASIs */
+ asi &= ~0x02;
+ }
+
+ switch (asi) {
case 0x10: // As if user primary
case 0x11: // As if user secondary
case 0x18: // As if user primary LE
@@ -2853,8 +2868,6 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
case 0x1d: // Bypass, non-cacheable LE
case 0x88: // Primary LE
case 0x89: // Secondary LE
- case 0x8a: // Primary no-fault LE
- case 0x8b: // Secondary no-fault LE
switch(size) {
case 2:
ret = bswap16(ret);
@@ -4231,8 +4244,8 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
#ifndef TARGET_SPARC64
#if !defined(CONFIG_USER_ONLY)
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size)
+static void do_unassigned_access(target_phys_addr_t addr, int is_write,
+ int is_exec, int is_asi, int size)
{
CPUState *saved_env;
int fault_type;
@@ -4297,8 +4310,8 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
static void do_unassigned_access(target_ulong addr, int is_write, int is_exec,
int is_asi, int size)
#else
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size)
+static void do_unassigned_access(target_phys_addr_t addr, int is_write,
+ int is_exec, int is_asi, int size)
#endif
{
CPUState *saved_env;
@@ -4347,3 +4360,12 @@ void helper_tick_set_limit(void *opaque, uint64_t limit)
#endif
}
#endif
+
+#if !defined(CONFIG_USER_ONLY)
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size)
+{
+ env = env1;
+ do_unassigned_access(addr, is_write, is_exec, is_asi, size);
+}
+#endif
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index 27c2cf98e8..15967c551a 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -2864,6 +2864,7 @@ static void disas_sparc_insn(DisasContext * dc)
break;
case 0x102: /* V9 fmovdcc %icc */
FMOVDCC(0);
+ break;
case 0x103: /* V9 fmovqcc %icc */
CHECK_FPU_FEATURE(dc, FLOAT128);
FMOVQCC(0);
@@ -3789,57 +3790,57 @@ static void disas_sparc_insn(DisasContext * dc)
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmple16();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmple16(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x022: /* VIS I fcmpne16 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpne16();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpne16(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x024: /* VIS I fcmple32 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmple32();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmple32(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x026: /* VIS I fcmpne32 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpne32();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpne32(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x028: /* VIS I fcmpgt16 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpgt16();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpgt16(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02a: /* VIS I fcmpeq16 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpeq16();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpeq16(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02c: /* VIS I fcmpgt32 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpgt32();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpgt32(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02e: /* VIS I fcmpeq32 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpeq32();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpeq32(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x031: /* VIS I fmul8x16 */
CHECK_FPU_FEATURE(dc, VIS1);
diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
index 12ff9d5259..a33d21fd07 100644
--- a/tcg/mips/tcg-target.c
+++ b/tcg/mips/tcg-target.c
@@ -1484,7 +1484,7 @@ static void tcg_target_qemu_prologue(TCGContext *s)
}
/* Call generated code */
- tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1]), 0);
+ tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1], 0);
tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]);
tb_ret_addr = s->code_ptr;
diff --git a/test-qmp-commands.c b/test-qmp-commands.c
new file mode 100644
index 0000000000..775290439a
--- /dev/null
+++ b/test-qmp-commands.c
@@ -0,0 +1,113 @@
+#include <glib.h>
+#include "qemu-objects.h"
+#include "test-qmp-commands.h"
+#include "qapi/qmp-core.h"
+#include "module.h"
+
+void qmp_user_def_cmd(Error **errp)
+{
+}
+
+void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
+{
+}
+
+UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **errp)
+{
+ UserDefTwo *ret;
+ UserDefOne *ud1c = qemu_mallocz(sizeof(UserDefOne));
+ UserDefOne *ud1d = qemu_mallocz(sizeof(UserDefOne));
+
+ ud1c->string = strdup(ud1a->string);
+ ud1c->integer = ud1a->integer;
+ ud1d->string = strdup(ud1b->string);
+ ud1d->integer = ud1b->integer;
+
+ ret = qemu_mallocz(sizeof(UserDefTwo));
+ ret->string = strdup("blah1");
+ ret->dict.string = strdup("blah2");
+ ret->dict.dict.userdef = ud1c;
+ ret->dict.dict.string = strdup("blah3");
+ ret->dict.has_dict2 = true;
+ ret->dict.dict2.userdef = ud1d;
+ ret->dict.dict2.string = strdup("blah4");
+
+ return ret;
+}
+
+/* test commands with no input and no return value */
+static void test_dispatch_cmd(void)
+{
+ QDict *req = qdict_new();
+ QObject *resp;
+
+ qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
+
+ resp = qmp_dispatch(QOBJECT(req));
+ assert(resp != NULL);
+ assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+ g_print("\nresp: %s\n", qstring_get_str(qobject_to_json(resp)));
+
+ qobject_decref(resp);
+ QDECREF(req);
+}
+
+/* test commands that return an error due to invalid parameters */
+static void test_dispatch_cmd_error(void)
+{
+ QDict *req = qdict_new();
+ QObject *resp;
+
+ qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
+
+ resp = qmp_dispatch(QOBJECT(req));
+ assert(resp != NULL);
+ assert(qdict_haskey(qobject_to_qdict(resp), "error"));
+ g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp)));
+
+ qobject_decref(resp);
+ QDECREF(req);
+}
+
+/* test commands that involve both input parameters and return values */
+static void test_dispatch_cmd_io(void)
+{
+ QDict *req = qdict_new();
+ QDict *args = qdict_new();
+ QDict *ud1a = qdict_new();
+ QDict *ud1b = qdict_new();
+ QObject *resp;
+
+ qdict_put_obj(ud1a, "integer", QOBJECT(qint_from_int(42)));
+ qdict_put_obj(ud1a, "string", QOBJECT(qstring_from_str("hello")));
+ qdict_put_obj(ud1b, "integer", QOBJECT(qint_from_int(422)));
+ qdict_put_obj(ud1b, "string", QOBJECT(qstring_from_str("hello2")));
+ qdict_put_obj(args, "ud1a", QOBJECT(ud1a));
+ qdict_put_obj(args, "ud1b", QOBJECT(ud1b));
+ qdict_put_obj(req, "arguments", QOBJECT(args));
+
+ qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
+
+ /* TODO: put in full payload and check for errors */
+ resp = qmp_dispatch(QOBJECT(req));
+ assert(resp != NULL);
+ assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+ g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp)));
+
+ qobject_decref(resp);
+ QDECREF(req);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/0.15/dispatch_cmd", test_dispatch_cmd);
+ g_test_add_func("/0.15/dispatch_cmd_error", test_dispatch_cmd_error);
+ g_test_add_func("/0.15/dispatch_cmd_io", test_dispatch_cmd_io);
+
+ module_call_init(MODULE_INIT_QAPI);
+ g_test_run();
+
+ return 0;
+}
diff --git a/test-visitor.c b/test-visitor.c
new file mode 100644
index 0000000000..5133ad6b19
--- /dev/null
+++ b/test-visitor.c
@@ -0,0 +1,306 @@
+#include <glib.h>
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qmp-input-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestStruct
+{
+ int64_t x;
+ int64_t y;
+} TestStruct;
+
+typedef struct TestStructList
+{
+ TestStruct *value;
+ struct TestStructList *next;
+} TestStructList;
+
+static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp)
+{
+ visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), errp);
+ visit_type_int(v, &(*obj)->x, "x", errp);
+ visit_type_int(v, &(*obj)->y, "y", errp);
+ visit_end_struct(v, errp);
+}
+
+static void visit_type_TestStructList(Visitor *m, TestStructList ** obj, const char *name, Error **errp)
+{
+ GenericList *i;
+
+ visit_start_list(m, name, errp);
+
+ for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) {
+ TestStructList *native_i = (TestStructList *)i;
+ visit_type_TestStruct(m, &native_i->value, NULL, errp);
+ }
+
+ visit_end_list(m, errp);
+}
+
+/* test core visitor methods */
+static void test_visitor_core(void)
+{
+ QmpOutputVisitor *mo;
+ QmpInputVisitor *mi;
+ Visitor *v;
+ TestStruct ts = { 42, 82 };
+ TestStruct *pts = &ts;
+ TestStructList *lts = NULL;
+ Error *err = NULL;
+ QObject *obj;
+ QString *str;
+ int64_t value = 0;
+
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+
+ visit_type_TestStruct(v, &pts, NULL, &err);
+
+ obj = qmp_output_get_qobject(mo);
+
+ str = qobject_to_json(obj);
+
+ printf("%s\n", qstring_get_str(str));
+
+ QDECREF(str);
+
+ obj = QOBJECT(qint_from_int(0x42));
+
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+
+ visit_type_int(v, &value, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+
+ g_assert(value == 0x42);
+
+ qobject_decref(obj);
+
+ obj = qobject_from_json("{'x': 42, 'y': 84}");
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+
+ pts = NULL;
+
+ visit_type_TestStruct(v, &pts, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+
+ g_assert(pts != NULL);
+ g_assert(pts->x == 42);
+ g_assert(pts->y == 84);
+
+ qobject_decref(obj);
+
+ obj = qobject_from_json("[{'x': 42, 'y': 84}, {'x': 12, 'y': 24}]");
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+
+ visit_type_TestStructList(v, &lts, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+
+ g_assert(lts != NULL);
+ g_assert(lts->value->x == 42);
+ g_assert(lts->value->y == 84);
+
+ lts = lts->next;
+ g_assert(lts != NULL);
+ g_assert(lts->value->x == 12);
+ g_assert(lts->value->y == 24);
+
+ g_assert(lts->next == NULL);
+
+ qobject_decref(obj);
+}
+
+/* test deep nesting with refs to other user-defined types */
+static void test_nested_structs(void)
+{
+ QmpOutputVisitor *mo;
+ QmpInputVisitor *mi;
+ Visitor *v;
+ UserDefOne ud1;
+ UserDefOne *ud1_p = &ud1, *ud1c_p = NULL;
+ UserDefTwo ud2;
+ UserDefTwo *ud2_p = &ud2, *ud2c_p = NULL;
+ Error *err = NULL;
+ QObject *obj;
+ QString *str;
+
+ ud1.integer = 42;
+ ud1.string = strdup("fourty two");
+
+ /* sanity check */
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+ visit_type_UserDefOne(v, &ud1_p, "o_O", &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ obj = qmp_output_get_qobject(mo);
+ g_assert(obj);
+ qobject_decref(obj);
+
+ ud2.string = strdup("fourty three");
+ ud2.dict.string = strdup("fourty four");
+ ud2.dict.dict.userdef = ud1_p;
+ ud2.dict.dict.string = strdup("fourty five");
+ ud2.dict.has_dict2 = true;
+ ud2.dict.dict2.userdef = ud1_p;
+ ud2.dict.dict2.string = strdup("fourty six");
+
+ /* c type -> qobject */
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+ visit_type_UserDefTwo(v, &ud2_p, "unused", &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ obj = qmp_output_get_qobject(mo);
+ g_assert(obj);
+ str = qobject_to_json_pretty(obj);
+ g_print("%s\n", qstring_get_str(str));
+ QDECREF(str);
+
+ /* qobject -> c type, should match original struct */
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+ visit_type_UserDefTwo(v, &ud2c_p, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+
+ g_assert(!g_strcmp0(ud2c_p->string, ud2.string));
+ g_assert(!g_strcmp0(ud2c_p->dict.string, ud2.dict.string));
+
+ ud1c_p = ud2c_p->dict.dict.userdef;
+ g_assert(ud1c_p->integer == ud1_p->integer);
+ g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string));
+
+ g_assert(!g_strcmp0(ud2c_p->dict.dict.string, ud2.dict.dict.string));
+
+ ud1c_p = ud2c_p->dict.dict2.userdef;
+ g_assert(ud1c_p->integer == ud1_p->integer);
+ g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string));
+
+ g_assert(!g_strcmp0(ud2c_p->dict.dict2.string, ud2.dict.dict2.string));
+ qemu_free(ud1.string);
+ qemu_free(ud2.string);
+ qemu_free(ud2.dict.string);
+ qemu_free(ud2.dict.dict.string);
+ qemu_free(ud2.dict.dict2.string);
+
+ qapi_free_UserDefTwo(ud2c_p);
+
+ qobject_decref(obj);
+}
+
+/* test enum values */
+static void test_enums(void)
+{
+ QmpOutputVisitor *mo;
+ QmpInputVisitor *mi;
+ Visitor *v;
+ EnumOne enum1 = ENUM_ONE_VALUE2, enum1_cpy = ENUM_ONE_VALUE1;
+ Error *err = NULL;
+ QObject *obj;
+ QString *str;
+
+ /* C type -> QObject */
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+ visit_type_EnumOne(v, &enum1, "unused", &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ obj = qmp_output_get_qobject(mo);
+ g_assert(obj);
+ str = qobject_to_json_pretty(obj);
+ g_print("%s\n", qstring_get_str(str));
+ QDECREF(str);
+ g_assert(g_strcmp0(qstring_get_str(qobject_to_qstring(obj)), "value2") == 0);
+
+ /* QObject -> C type */
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+ visit_type_EnumOne(v, &enum1_cpy, "unused", &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ g_debug("enum1_cpy, enum1: %d, %d", enum1_cpy, enum1);
+ g_assert(enum1_cpy == enum1);
+
+ qobject_decref(obj);
+}
+
+/* test enum values nested in schema-defined structs */
+static void test_nested_enums(void)
+{
+ QmpOutputVisitor *mo;
+ QmpInputVisitor *mi;
+ Visitor *v;
+ NestedEnumsOne *nested_enums, *nested_enums_cpy = NULL;
+ Error *err = NULL;
+ QObject *obj;
+ QString *str;
+
+ nested_enums = qemu_mallocz(sizeof(NestedEnumsOne));
+ nested_enums->enum1 = ENUM_ONE_VALUE1;
+ nested_enums->enum2 = ENUM_ONE_VALUE2;
+ nested_enums->enum3 = ENUM_ONE_VALUE3;
+ nested_enums->enum4 = ENUM_ONE_VALUE3;
+ nested_enums->has_enum2 = false;
+ nested_enums->has_enum4 = true;
+
+ /* C type -> QObject */
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+ visit_type_NestedEnumsOne(v, &nested_enums, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ obj = qmp_output_get_qobject(mo);
+ g_assert(obj);
+ str = qobject_to_json_pretty(obj);
+ g_print("%s\n", qstring_get_str(str));
+ QDECREF(str);
+
+ /* QObject -> C type */
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+ visit_type_NestedEnumsOne(v, &nested_enums_cpy, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ g_assert(nested_enums_cpy);
+ g_assert(nested_enums_cpy->enum1 == nested_enums->enum1);
+ g_assert(nested_enums_cpy->enum3 == nested_enums->enum3);
+ g_assert(nested_enums_cpy->enum4 == nested_enums->enum4);
+ g_assert(nested_enums_cpy->has_enum2 == false);
+ g_assert(nested_enums_cpy->has_enum4 == true);
+
+ qobject_decref(obj);
+ qapi_free_NestedEnumsOne(nested_enums);
+ qapi_free_NestedEnumsOne(nested_enums_cpy);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/0.15/visitor_core", test_visitor_core);
+ g_test_add_func("/0.15/nested_structs", test_nested_structs);
+ g_test_add_func("/0.15/enums", test_enums);
+ g_test_add_func("/0.15/nested_enums", test_nested_enums);
+
+ g_test_run();
+
+ return 0;
+}
diff --git a/trace-events b/trace-events
index ad11b09c0a..713f042081 100644
--- a/trace-events
+++ b/trace-events
@@ -103,6 +103,10 @@ disable cs4231_mem_readl_reg(uint32_t reg, uint32_t ret) "read reg %d: 0x%08x"
disable cs4231_mem_writel_reg(uint32_t reg, uint32_t old, uint32_t val) "write reg %d: 0x%08x -> 0x%08x"
disable cs4231_mem_writel_dreg(uint32_t reg, uint32_t old, uint32_t val) "write dreg %d: 0x%02x -> 0x%02x"
+# hw/ds1225y.c
+disable nvram_read(uint32_t addr, uint32_t ret) "read addr %d: 0x%02x"
+disable nvram_write(uint32_t addr, uint32_t old, uint32_t val) "write addr %d: 0x%02x -> 0x%02x"
+
# hw/eccmemctl.c
disable ecc_mem_writel_mer(uint32_t val) "Write memory enable %08x"
disable ecc_mem_writel_mdr(uint32_t val) "Write memory delay %08x"
@@ -212,13 +216,13 @@ disable usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr m
disable usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
disable usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d"
disable usb_ehci_state(const char *schedule, const char *state) "%s schedule %s"
-disable usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t next, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"
+disable usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"
disable usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ %08x - rl %d, mplen %d, eps %d, ep %d, dev %d"
disable usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ %08x - c %d, h %d, dtc %d, i %d"
-disable usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t next, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x"
+disable usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x"
disable usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d"
disable usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d"
-disable usb_ehci_itd(uint32_t addr, uint32_t next, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d"
+disable usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d"
disable usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s"
disable usb_ehci_port_detach(uint32_t port) "detach port #%d"
disable usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
diff --git a/usb-redir.c b/usb-redir.c
new file mode 100644
index 0000000000..e2129931a0
--- /dev/null
+++ b/usb-redir.c
@@ -0,0 +1,1218 @@
+/*
+ * USB redirector usb-guest
+ *
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Red Hat Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "monitor.h"
+#include "sysemu.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <usbredirparser.h>
+
+#include "hw/usb.h"
+
+#define MAX_ENDPOINTS 32
+#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
+#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
+
+typedef struct AsyncURB AsyncURB;
+typedef struct USBRedirDevice USBRedirDevice;
+
+/* Struct to hold buffered packets (iso or int input packets) */
+struct buf_packet {
+ uint8_t *data;
+ int len;
+ int status;
+ QTAILQ_ENTRY(buf_packet)next;
+};
+
+struct endp_data {
+ uint8_t type;
+ uint8_t interval;
+ uint8_t interface; /* bInterfaceNumber this ep belongs to */
+ uint8_t iso_started;
+ uint8_t iso_error; /* For reporting iso errors to the HC */
+ uint8_t interrupt_started;
+ uint8_t interrupt_error;
+ QTAILQ_HEAD(, buf_packet) bufpq;
+};
+
+struct USBRedirDevice {
+ USBDevice dev;
+ /* Properties */
+ CharDriverState *cs;
+ uint8_t debug;
+ /* Data passed from chardev the fd_read cb to the usbredirparser read cb */
+ const uint8_t *read_buf;
+ int read_buf_size;
+ /* For async handling of open/close */
+ QEMUBH *open_close_bh;
+ /* To delay the usb attach in case of quick chardev close + open */
+ QEMUTimer *attach_timer;
+ int64_t next_attach_time;
+ struct usbredirparser *parser;
+ struct endp_data endpoint[MAX_ENDPOINTS];
+ uint32_t packet_id;
+ QTAILQ_HEAD(, AsyncURB) asyncq;
+};
+
+struct AsyncURB {
+ USBRedirDevice *dev;
+ USBPacket *packet;
+ uint32_t packet_id;
+ int get;
+ union {
+ struct usb_redir_control_packet_header control_packet;
+ struct usb_redir_bulk_packet_header bulk_packet;
+ struct usb_redir_interrupt_packet_header interrupt_packet;
+ };
+ QTAILQ_ENTRY(AsyncURB)next;
+};
+
+static void usbredir_device_connect(void *priv,
+ struct usb_redir_device_connect_header *device_connect);
+static void usbredir_device_disconnect(void *priv);
+static void usbredir_interface_info(void *priv,
+ struct usb_redir_interface_info_header *interface_info);
+static void usbredir_ep_info(void *priv,
+ struct usb_redir_ep_info_header *ep_info);
+static void usbredir_configuration_status(void *priv, uint32_t id,
+ struct usb_redir_configuration_status_header *configuration_status);
+static void usbredir_alt_setting_status(void *priv, uint32_t id,
+ struct usb_redir_alt_setting_status_header *alt_setting_status);
+static void usbredir_iso_stream_status(void *priv, uint32_t id,
+ struct usb_redir_iso_stream_status_header *iso_stream_status);
+static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+ struct usb_redir_interrupt_receiving_status_header
+ *interrupt_receiving_status);
+static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+ struct usb_redir_bulk_streams_status_header *bulk_streams_status);
+static void usbredir_control_packet(void *priv, uint32_t id,
+ struct usb_redir_control_packet_header *control_packet,
+ uint8_t *data, int data_len);
+static void usbredir_bulk_packet(void *priv, uint32_t id,
+ struct usb_redir_bulk_packet_header *bulk_packet,
+ uint8_t *data, int data_len);
+static void usbredir_iso_packet(void *priv, uint32_t id,
+ struct usb_redir_iso_packet_header *iso_packet,
+ uint8_t *data, int data_len);
+static void usbredir_interrupt_packet(void *priv, uint32_t id,
+ struct usb_redir_interrupt_packet_header *interrupt_header,
+ uint8_t *data, int data_len);
+
+static int usbredir_handle_status(USBRedirDevice *dev,
+ int status, int actual_len);
+
+#define VERSION "qemu usb-redir guest " QEMU_VERSION
+
+/*
+ * Logging stuff
+ */
+
+#define ERROR(...) \
+ do { \
+ if (dev->debug >= usbredirparser_error) { \
+ error_report("usb-redir error: " __VA_ARGS__); \
+ } \
+ } while (0)
+#define WARNING(...) \
+ do { \
+ if (dev->debug >= usbredirparser_warning) { \
+ error_report("usb-redir warning: " __VA_ARGS__); \
+ } \
+ } while (0)
+#define INFO(...) \
+ do { \
+ if (dev->debug >= usbredirparser_info) { \
+ error_report("usb-redir: " __VA_ARGS__); \
+ } \
+ } while (0)
+#define DPRINTF(...) \
+ do { \
+ if (dev->debug >= usbredirparser_debug) { \
+ error_report("usb-redir: " __VA_ARGS__); \
+ } \
+ } while (0)
+#define DPRINTF2(...) \
+ do { \
+ if (dev->debug >= usbredirparser_debug_data) { \
+ error_report("usb-redir: " __VA_ARGS__); \
+ } \
+ } while (0)
+
+static void usbredir_log(void *priv, int level, const char *msg)
+{
+ USBRedirDevice *dev = priv;
+
+ if (dev->debug < level) {
+ return;
+ }
+
+ error_report("%s\n", msg);
+}
+
+static void usbredir_log_data(USBRedirDevice *dev, const char *desc,
+ const uint8_t *data, int len)
+{
+ int i, j, n;
+
+ if (dev->debug < usbredirparser_debug_data) {
+ return;
+ }
+
+ for (i = 0; i < len; i += j) {
+ char buf[128];
+
+ n = sprintf(buf, "%s", desc);
+ for (j = 0; j < 8 && i + j < len; j++) {
+ n += sprintf(buf + n, " %02X", data[i + j]);
+ }
+ error_report("%s\n", buf);
+ }
+}
+
+/*
+ * usbredirparser io functions
+ */
+
+static int usbredir_read(void *priv, uint8_t *data, int count)
+{
+ USBRedirDevice *dev = priv;
+
+ if (dev->read_buf_size < count) {
+ count = dev->read_buf_size;
+ }
+
+ memcpy(data, dev->read_buf, count);
+
+ dev->read_buf_size -= count;
+ if (dev->read_buf_size) {
+ dev->read_buf += count;
+ } else {
+ dev->read_buf = NULL;
+ }
+
+ return count;
+}
+
+static int usbredir_write(void *priv, uint8_t *data, int count)
+{
+ USBRedirDevice *dev = priv;
+
+ return qemu_chr_write(dev->cs, data, count);
+}
+
+/*
+ * Async and buffered packets helpers
+ */
+
+static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
+{
+ AsyncURB *aurb = (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
+ aurb->dev = dev;
+ aurb->packet = p;
+ aurb->packet_id = dev->packet_id;
+ QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
+ dev->packet_id++;
+
+ return aurb;
+}
+
+static void async_free(USBRedirDevice *dev, AsyncURB *aurb)
+{
+ QTAILQ_REMOVE(&dev->asyncq, aurb, next);
+ qemu_free(aurb);
+}
+
+static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id)
+{
+ AsyncURB *aurb;
+
+ QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+ if (aurb->packet_id == packet_id) {
+ return aurb;
+ }
+ }
+ ERROR("could not find async urb for packet_id %u\n", packet_id);
+ return NULL;
+}
+
+static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ AsyncURB *aurb;
+
+ QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+ if (p != aurb->packet) {
+ continue;
+ }
+
+ DPRINTF("async cancel id %u\n", aurb->packet_id);
+ usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id);
+ usbredirparser_do_write(dev->parser);
+
+ /* Mark it as dead */
+ aurb->packet = NULL;
+ break;
+ }
+}
+
+static struct buf_packet *bufp_alloc(USBRedirDevice *dev,
+ uint8_t *data, int len, int status, uint8_t ep)
+{
+ struct buf_packet *bufp = qemu_malloc(sizeof(struct buf_packet));
+ bufp->data = data;
+ bufp->len = len;
+ bufp->status = status;
+ QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+ return bufp;
+}
+
+static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
+ uint8_t ep)
+{
+ QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+ free(bufp->data);
+ qemu_free(bufp);
+}
+
+static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep)
+{
+ struct buf_packet *buf, *buf_next;
+
+ QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) {
+ bufp_free(dev, buf, ep);
+ }
+}
+
+/*
+ * USBDevice callbacks
+ */
+
+static void usbredir_handle_reset(USBDevice *udev)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+ DPRINTF("reset device\n");
+ usbredirparser_send_reset(dev->parser);
+ usbredirparser_do_write(dev->parser);
+}
+
+static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
+ uint8_t ep)
+{
+ int status, len;
+
+ if (!dev->endpoint[EP2I(ep)].iso_started &&
+ !dev->endpoint[EP2I(ep)].iso_error) {
+ struct usb_redir_start_iso_stream_header start_iso = {
+ .endpoint = ep,
+ /* TODO maybe do something with these depending on ep interval? */
+ .pkts_per_urb = 32,
+ .no_urbs = 3,
+ };
+ /* No id, we look at the ep when receiving a status back */
+ usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
+ usbredirparser_do_write(dev->parser);
+ DPRINTF("iso stream started ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].iso_started = 1;
+ }
+
+ if (ep & USB_DIR_IN) {
+ struct buf_packet *isop;
+
+ isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+ if (isop == NULL) {
+ DPRINTF2("iso-token-in ep %02X, no isop\n", ep);
+ /* Check iso_error for stream errors, otherwise its an underrun */
+ status = dev->endpoint[EP2I(ep)].iso_error;
+ dev->endpoint[EP2I(ep)].iso_error = 0;
+ return usbredir_handle_status(dev, status, 0);
+ }
+ DPRINTF2("iso-token-in ep %02X status %d len %d\n", ep, isop->status,
+ isop->len);
+
+ status = isop->status;
+ if (status != usb_redir_success) {
+ bufp_free(dev, isop, ep);
+ return usbredir_handle_status(dev, status, 0);
+ }
+
+ len = isop->len;
+ if (len > p->len) {
+ ERROR("received iso data is larger then packet ep %02X\n", ep);
+ bufp_free(dev, isop, ep);
+ return USB_RET_NAK;
+ }
+ memcpy(p->data, isop->data, len);
+ bufp_free(dev, isop, ep);
+ return len;
+ } else {
+ /* If the stream was not started because of a pending error don't
+ send the packet to the usb-host */
+ if (dev->endpoint[EP2I(ep)].iso_started) {
+ struct usb_redir_iso_packet_header iso_packet = {
+ .endpoint = ep,
+ .length = p->len
+ };
+ /* No id, we look at the ep when receiving a status back */
+ usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet,
+ p->data, p->len);
+ usbredirparser_do_write(dev->parser);
+ }
+ status = dev->endpoint[EP2I(ep)].iso_error;
+ dev->endpoint[EP2I(ep)].iso_error = 0;
+ DPRINTF2("iso-token-out ep %02X status %d len %d\n", ep, status,
+ p->len);
+ return usbredir_handle_status(dev, status, p->len);
+ }
+}
+
+static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
+{
+ struct usb_redir_stop_iso_stream_header stop_iso_stream = {
+ .endpoint = ep
+ };
+ if (dev->endpoint[EP2I(ep)].iso_started) {
+ usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream);
+ DPRINTF("iso stream stopped ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].iso_started = 0;
+ }
+ usbredir_free_bufpq(dev, ep);
+}
+
+static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
+ uint8_t ep)
+{
+ AsyncURB *aurb = async_alloc(dev, p);
+ struct usb_redir_bulk_packet_header bulk_packet;
+
+ DPRINTF("bulk-out ep %02X len %d id %u\n", ep, p->len, aurb->packet_id);
+
+ bulk_packet.endpoint = ep;
+ bulk_packet.length = p->len;
+ bulk_packet.stream_id = 0;
+ aurb->bulk_packet = bulk_packet;
+
+ if (ep & USB_DIR_IN) {
+ usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+ &bulk_packet, NULL, 0);
+ } else {
+ usbredir_log_data(dev, "bulk data out:", p->data, p->len);
+ usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+ &bulk_packet, p->data, p->len);
+ }
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
+ USBPacket *p, uint8_t ep)
+{
+ if (ep & USB_DIR_IN) {
+ /* Input interrupt endpoint, buffered packet input */
+ struct buf_packet *intp;
+ int status, len;
+
+ if (!dev->endpoint[EP2I(ep)].interrupt_started &&
+ !dev->endpoint[EP2I(ep)].interrupt_error) {
+ struct usb_redir_start_interrupt_receiving_header start_int = {
+ .endpoint = ep,
+ };
+ /* No id, we look at the ep when receiving a status back */
+ usbredirparser_send_start_interrupt_receiving(dev->parser, 0,
+ &start_int);
+ usbredirparser_do_write(dev->parser);
+ DPRINTF("interrupt recv started ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].interrupt_started = 1;
+ }
+
+ intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+ if (intp == NULL) {
+ DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
+ /* Check interrupt_error for stream errors */
+ status = dev->endpoint[EP2I(ep)].interrupt_error;
+ dev->endpoint[EP2I(ep)].interrupt_error = 0;
+ return usbredir_handle_status(dev, status, 0);
+ }
+ DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
+ intp->status, intp->len);
+
+ status = intp->status;
+ if (status != usb_redir_success) {
+ bufp_free(dev, intp, ep);
+ return usbredir_handle_status(dev, status, 0);
+ }
+
+ len = intp->len;
+ if (len > p->len) {
+ ERROR("received int data is larger then packet ep %02X\n", ep);
+ bufp_free(dev, intp, ep);
+ return USB_RET_NAK;
+ }
+ memcpy(p->data, intp->data, len);
+ bufp_free(dev, intp, ep);
+ return len;
+ } else {
+ /* Output interrupt endpoint, normal async operation */
+ AsyncURB *aurb = async_alloc(dev, p);
+ struct usb_redir_interrupt_packet_header interrupt_packet;
+
+ DPRINTF("interrupt-out ep %02X len %d id %u\n", ep, p->len,
+ aurb->packet_id);
+
+ interrupt_packet.endpoint = ep;
+ interrupt_packet.length = p->len;
+ aurb->interrupt_packet = interrupt_packet;
+
+ usbredir_log_data(dev, "interrupt data out:", p->data, p->len);
+ usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
+ &interrupt_packet, p->data, p->len);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+ }
+}
+
+static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
+ uint8_t ep)
+{
+ struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = {
+ .endpoint = ep
+ };
+ if (dev->endpoint[EP2I(ep)].interrupt_started) {
+ usbredirparser_send_stop_interrupt_receiving(dev->parser, 0,
+ &stop_interrupt_recv);
+ DPRINTF("interrupt recv stopped ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].interrupt_started = 0;
+ }
+ usbredir_free_bufpq(dev, ep);
+}
+
+static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ uint8_t ep;
+
+ ep = p->devep;
+ if (p->pid == USB_TOKEN_IN) {
+ ep |= USB_DIR_IN;
+ }
+
+ switch (dev->endpoint[EP2I(ep)].type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ ERROR("handle_data called for control transfer on ep %02X\n", ep);
+ return USB_RET_NAK;
+ case USB_ENDPOINT_XFER_ISOC:
+ return usbredir_handle_iso_data(dev, p, ep);
+ case USB_ENDPOINT_XFER_BULK:
+ return usbredir_handle_bulk_data(dev, p, ep);;
+ case USB_ENDPOINT_XFER_INT:
+ return usbredir_handle_interrupt_data(dev, p, ep);;
+ default:
+ ERROR("handle_data ep %02X has unknown type %d\n", ep,
+ dev->endpoint[EP2I(ep)].type);
+ return USB_RET_NAK;
+ }
+}
+
+static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
+ int config)
+{
+ struct usb_redir_set_configuration_header set_config;
+ AsyncURB *aurb = async_alloc(dev, p);
+ int i;
+
+ DPRINTF("set config %d id %u\n", config, aurb->packet_id);
+
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ switch (dev->endpoint[i].type) {
+ case USB_ENDPOINT_XFER_ISOC:
+ usbredir_stop_iso_stream(dev, I2EP(i));
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (i & 0x10) {
+ usbredir_stop_interrupt_receiving(dev, I2EP(i));
+ }
+ break;
+ }
+ usbredir_free_bufpq(dev, I2EP(i));
+ }
+
+ set_config.configuration = config;
+ usbredirparser_send_set_configuration(dev->parser, aurb->packet_id,
+ &set_config);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
+{
+ AsyncURB *aurb = async_alloc(dev, p);
+
+ DPRINTF("get config id %u\n", aurb->packet_id);
+
+ aurb->get = 1;
+ usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
+ int interface, int alt)
+{
+ struct usb_redir_set_alt_setting_header set_alt;
+ AsyncURB *aurb = async_alloc(dev, p);
+ int i;
+
+ DPRINTF("set interface %d alt %d id %u\n", interface, alt,
+ aurb->packet_id);
+
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ if (dev->endpoint[i].interface == interface) {
+ switch (dev->endpoint[i].type) {
+ case USB_ENDPOINT_XFER_ISOC:
+ usbredir_stop_iso_stream(dev, I2EP(i));
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (i & 0x10) {
+ usbredir_stop_interrupt_receiving(dev, I2EP(i));
+ }
+ break;
+ }
+ usbredir_free_bufpq(dev, I2EP(i));
+ }
+ }
+
+ set_alt.interface = interface;
+ set_alt.alt = alt;
+ usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id,
+ &set_alt);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
+ int interface)
+{
+ struct usb_redir_get_alt_setting_header get_alt;
+ AsyncURB *aurb = async_alloc(dev, p);
+
+ DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
+
+ get_alt.interface = interface;
+ aurb->get = 1;
+ usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
+ &get_alt);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ struct usb_redir_control_packet_header control_packet;
+ AsyncURB *aurb;
+
+ /* Special cases for certain standard device requests */
+ switch (request) {
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ DPRINTF("set address %d\n", value);
+ dev->dev.addr = value;
+ return 0;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ return usbredir_set_config(dev, p, value & 0xff);
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ return usbredir_get_config(dev, p);
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ return usbredir_set_interface(dev, p, index, value);
+ case InterfaceRequest | USB_REQ_GET_INTERFACE:
+ return usbredir_get_interface(dev, p, index);
+ }
+
+ /* "Normal" ctrl requests */
+ aurb = async_alloc(dev, p);
+
+ /* Note request is (bRequestType << 8) | bRequest */
+ DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n",
+ request >> 8, request & 0xff, value, index, length,
+ aurb->packet_id);
+
+ control_packet.request = request & 0xFF;
+ control_packet.requesttype = request >> 8;
+ control_packet.endpoint = control_packet.requesttype & USB_DIR_IN;
+ control_packet.value = value;
+ control_packet.index = index;
+ control_packet.length = length;
+ aurb->control_packet = control_packet;
+
+ if (control_packet.requesttype & USB_DIR_IN) {
+ usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+ &control_packet, NULL, 0);
+ } else {
+ usbredir_log_data(dev, "ctrl data out:", data, length);
+ usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+ &control_packet, data, length);
+ }
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+/*
+ * Close events can be triggered by usbredirparser_do_write which gets called
+ * from within the USBDevice data / control packet callbacks and doing a
+ * usb_detach from within these callbacks is not a good idea.
+ *
+ * So we use a bh handler to take care of close events. We also handle
+ * open events from this callback to make sure that a close directly followed
+ * by an open gets handled in the right order.
+ */
+static void usbredir_open_close_bh(void *opaque)
+{
+ USBRedirDevice *dev = opaque;
+
+ usbredir_device_disconnect(dev);
+
+ if (dev->parser) {
+ usbredirparser_destroy(dev->parser);
+ dev->parser = NULL;
+ }
+
+ if (dev->cs->opened) {
+ dev->parser = qemu_oom_check(usbredirparser_create());
+ dev->parser->priv = dev;
+ dev->parser->log_func = usbredir_log;
+ dev->parser->read_func = usbredir_read;
+ dev->parser->write_func = usbredir_write;
+ dev->parser->device_connect_func = usbredir_device_connect;
+ dev->parser->device_disconnect_func = usbredir_device_disconnect;
+ dev->parser->interface_info_func = usbredir_interface_info;
+ dev->parser->ep_info_func = usbredir_ep_info;
+ dev->parser->configuration_status_func = usbredir_configuration_status;
+ dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
+ dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
+ dev->parser->interrupt_receiving_status_func =
+ usbredir_interrupt_receiving_status;
+ dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
+ dev->parser->control_packet_func = usbredir_control_packet;
+ dev->parser->bulk_packet_func = usbredir_bulk_packet;
+ dev->parser->iso_packet_func = usbredir_iso_packet;
+ dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
+ dev->read_buf = NULL;
+ dev->read_buf_size = 0;
+ usbredirparser_init(dev->parser, VERSION, NULL, 0, 0);
+ usbredirparser_do_write(dev->parser);
+ }
+}
+
+static void usbredir_do_attach(void *opaque)
+{
+ USBRedirDevice *dev = opaque;
+
+ usb_device_attach(&dev->dev);
+}
+
+/*
+ * chardev callbacks
+ */
+
+static int usbredir_chardev_can_read(void *opaque)
+{
+ USBRedirDevice *dev = opaque;
+
+ if (dev->parser) {
+ /* usbredir_parser_do_read will consume *all* data we give it */
+ return 1024 * 1024;
+ } else {
+ /* usbredir_open_close_bh hasn't handled the open event yet */
+ return 0;
+ }
+}
+
+static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
+{
+ USBRedirDevice *dev = opaque;
+
+ /* No recursion allowed! */
+ assert(dev->read_buf == NULL);
+
+ dev->read_buf = buf;
+ dev->read_buf_size = size;
+
+ usbredirparser_do_read(dev->parser);
+ /* Send any acks, etc. which may be queued now */
+ usbredirparser_do_write(dev->parser);
+}
+
+static void usbredir_chardev_event(void *opaque, int event)
+{
+ USBRedirDevice *dev = opaque;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ case CHR_EVENT_CLOSED:
+ qemu_bh_schedule(dev->open_close_bh);
+ break;
+ }
+}
+
+/*
+ * init + destroy
+ */
+
+static int usbredir_initfn(USBDevice *udev)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ int i;
+
+ if (dev->cs == NULL) {
+ qerror_report(QERR_MISSING_PARAMETER, "chardev");
+ return -1;
+ }
+
+ dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
+ dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
+
+ QTAILQ_INIT(&dev->asyncq);
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ QTAILQ_INIT(&dev->endpoint[i].bufpq);
+ }
+
+ /* We'll do the attach once we receive the speed from the usb-host */
+ udev->auto_attach = 0;
+
+ qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
+ usbredir_chardev_read, usbredir_chardev_event, dev);
+
+ return 0;
+}
+
+static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
+{
+ AsyncURB *aurb, *next_aurb;
+ int i;
+
+ QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) {
+ async_free(dev, aurb);
+ }
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ usbredir_free_bufpq(dev, I2EP(i));
+ }
+}
+
+static void usbredir_handle_destroy(USBDevice *udev)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+ qemu_chr_close(dev->cs);
+ /* Note must be done after qemu_chr_close, as that causes a close event */
+ qemu_bh_delete(dev->open_close_bh);
+
+ qemu_del_timer(dev->attach_timer);
+ qemu_free_timer(dev->attach_timer);
+
+ usbredir_cleanup_device_queues(dev);
+
+ if (dev->parser) {
+ usbredirparser_destroy(dev->parser);
+ }
+}
+
+/*
+ * usbredirparser packet complete callbacks
+ */
+
+static int usbredir_handle_status(USBRedirDevice *dev,
+ int status, int actual_len)
+{
+ switch (status) {
+ case usb_redir_success:
+ return actual_len;
+ case usb_redir_stall:
+ return USB_RET_STALL;
+ case usb_redir_cancelled:
+ WARNING("returning cancelled packet to HC?\n");
+ case usb_redir_inval:
+ case usb_redir_ioerror:
+ case usb_redir_timeout:
+ default:
+ return USB_RET_NAK;
+ }
+}
+
+static void usbredir_device_connect(void *priv,
+ struct usb_redir_device_connect_header *device_connect)
+{
+ USBRedirDevice *dev = priv;
+
+ switch (device_connect->speed) {
+ case usb_redir_speed_low:
+ DPRINTF("attaching low speed device\n");
+ dev->dev.speed = USB_SPEED_LOW;
+ break;
+ case usb_redir_speed_full:
+ DPRINTF("attaching full speed device\n");
+ dev->dev.speed = USB_SPEED_FULL;
+ break;
+ case usb_redir_speed_high:
+ DPRINTF("attaching high speed device\n");
+ dev->dev.speed = USB_SPEED_HIGH;
+ break;
+ case usb_redir_speed_super:
+ DPRINTF("attaching super speed device\n");
+ dev->dev.speed = USB_SPEED_SUPER;
+ break;
+ default:
+ DPRINTF("attaching unknown speed device, assuming full speed\n");
+ dev->dev.speed = USB_SPEED_FULL;
+ }
+ dev->dev.speedmask = (1 << dev->dev.speed);
+ qemu_mod_timer(dev->attach_timer, dev->next_attach_time);
+}
+
+static void usbredir_device_disconnect(void *priv)
+{
+ USBRedirDevice *dev = priv;
+
+ /* Stop any pending attaches */
+ qemu_del_timer(dev->attach_timer);
+
+ if (dev->dev.attached) {
+ usb_device_detach(&dev->dev);
+ usbredir_cleanup_device_queues(dev);
+ /*
+ * Delay next usb device attach to give the guest a chance to see
+ * see the detach / attach in case of quick close / open succession
+ */
+ dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200;
+ }
+}
+
+static void usbredir_interface_info(void *priv,
+ struct usb_redir_interface_info_header *interface_info)
+{
+ /* The intention is to allow specifying acceptable interface classes
+ for redirection on the cmdline and in the future verify this here,
+ and disconnect (or never connect) the device if a not accepted
+ interface class is detected */
+}
+
+static void usbredir_ep_info(void *priv,
+ struct usb_redir_ep_info_header *ep_info)
+{
+ USBRedirDevice *dev = priv;
+ int i;
+
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ dev->endpoint[i].type = ep_info->type[i];
+ dev->endpoint[i].interval = ep_info->interval[i];
+ dev->endpoint[i].interface = ep_info->interface[i];
+ if (dev->endpoint[i].type != usb_redir_type_invalid) {
+ DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i),
+ dev->endpoint[i].type, dev->endpoint[i].interface);
+ }
+ }
+}
+
+static void usbredir_configuration_status(void *priv, uint32_t id,
+ struct usb_redir_configuration_status_header *config_status)
+{
+ USBRedirDevice *dev = priv;
+ AsyncURB *aurb;
+ int len = 0;
+
+ DPRINTF("set config status %d config %d id %u\n", config_status->status,
+ config_status->configuration, id);
+
+ aurb = async_find(dev, id);
+ if (!aurb) {
+ return;
+ }
+ if (aurb->packet) {
+ if (aurb->get) {
+ dev->dev.data_buf[0] = config_status->configuration;
+ len = 1;
+ }
+ aurb->packet->len =
+ usbredir_handle_status(dev, config_status->status, len);
+ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+}
+
+static void usbredir_alt_setting_status(void *priv, uint32_t id,
+ struct usb_redir_alt_setting_status_header *alt_setting_status)
+{
+ USBRedirDevice *dev = priv;
+ AsyncURB *aurb;
+ int len = 0;
+
+ DPRINTF("alt status %d intf %d alt %d id: %u\n",
+ alt_setting_status->status,
+ alt_setting_status->interface,
+ alt_setting_status->alt, id);
+
+ aurb = async_find(dev, id);
+ if (!aurb) {
+ return;
+ }
+ if (aurb->packet) {
+ if (aurb->get) {
+ dev->dev.data_buf[0] = alt_setting_status->alt;
+ len = 1;
+ }
+ aurb->packet->len =
+ usbredir_handle_status(dev, alt_setting_status->status, len);
+ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+}
+
+static void usbredir_iso_stream_status(void *priv, uint32_t id,
+ struct usb_redir_iso_stream_status_header *iso_stream_status)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = iso_stream_status->endpoint;
+
+ DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
+ ep, id);
+
+ dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status;
+ if (iso_stream_status->status == usb_redir_stall) {
+ DPRINTF("iso stream stopped by peer ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].iso_started = 0;
+ }
+}
+
+static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+ struct usb_redir_interrupt_receiving_status_header
+ *interrupt_receiving_status)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = interrupt_receiving_status->endpoint;
+
+ DPRINTF("interrupt recv status %d ep %02X id %u\n",
+ interrupt_receiving_status->status, ep, id);
+
+ dev->endpoint[EP2I(ep)].interrupt_error =
+ interrupt_receiving_status->status;
+ if (interrupt_receiving_status->status == usb_redir_stall) {
+ DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].interrupt_started = 0;
+ }
+}
+
+static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+ struct usb_redir_bulk_streams_status_header *bulk_streams_status)
+{
+}
+
+static void usbredir_control_packet(void *priv, uint32_t id,
+ struct usb_redir_control_packet_header *control_packet,
+ uint8_t *data, int data_len)
+{
+ USBRedirDevice *dev = priv;
+ int len = control_packet->length;
+ AsyncURB *aurb;
+
+ DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
+ len, id);
+
+ aurb = async_find(dev, id);
+ if (!aurb) {
+ free(data);
+ return;
+ }
+
+ aurb->control_packet.status = control_packet->status;
+ aurb->control_packet.length = control_packet->length;
+ if (memcmp(&aurb->control_packet, control_packet,
+ sizeof(*control_packet))) {
+ ERROR("return control packet mismatch, please report this!\n");
+ len = USB_RET_NAK;
+ }
+
+ if (aurb->packet) {
+ len = usbredir_handle_status(dev, control_packet->status, len);
+ if (len > 0) {
+ usbredir_log_data(dev, "ctrl data in:", data, data_len);
+ if (data_len <= sizeof(dev->dev.data_buf)) {
+ memcpy(dev->dev.data_buf, data, data_len);
+ } else {
+ ERROR("ctrl buffer too small (%d > %zu)\n",
+ data_len, sizeof(dev->dev.data_buf));
+ len = USB_RET_STALL;
+ }
+ }
+ aurb->packet->len = len;
+ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+ free(data);
+}
+
+static void usbredir_bulk_packet(void *priv, uint32_t id,
+ struct usb_redir_bulk_packet_header *bulk_packet,
+ uint8_t *data, int data_len)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = bulk_packet->endpoint;
+ int len = bulk_packet->length;
+ AsyncURB *aurb;
+
+ DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
+ ep, len, id);
+
+ aurb = async_find(dev, id);
+ if (!aurb) {
+ free(data);
+ return;
+ }
+
+ if (aurb->bulk_packet.endpoint != bulk_packet->endpoint ||
+ aurb->bulk_packet.stream_id != bulk_packet->stream_id) {
+ ERROR("return bulk packet mismatch, please report this!\n");
+ len = USB_RET_NAK;
+ }
+
+ if (aurb->packet) {
+ len = usbredir_handle_status(dev, bulk_packet->status, len);
+ if (len > 0) {
+ usbredir_log_data(dev, "bulk data in:", data, data_len);
+ if (data_len <= aurb->packet->len) {
+ memcpy(aurb->packet->data, data, data_len);
+ } else {
+ ERROR("bulk buffer too small (%d > %d)\n", data_len,
+ aurb->packet->len);
+ len = USB_RET_STALL;
+ }
+ }
+ aurb->packet->len = len;
+ usb_packet_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+ free(data);
+}
+
+static void usbredir_iso_packet(void *priv, uint32_t id,
+ struct usb_redir_iso_packet_header *iso_packet,
+ uint8_t *data, int data_len)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = iso_packet->endpoint;
+
+ DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep,
+ data_len, id);
+
+ if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
+ ERROR("received iso packet for non iso endpoint %02X\n", ep);
+ free(data);
+ return;
+ }
+
+ if (dev->endpoint[EP2I(ep)].iso_started == 0) {
+ DPRINTF("received iso packet for non started stream ep %02X\n", ep);
+ free(data);
+ return;
+ }
+
+ /* bufp_alloc also adds the packet to the ep queue */
+ bufp_alloc(dev, data, data_len, iso_packet->status, ep);
+}
+
+static void usbredir_interrupt_packet(void *priv, uint32_t id,
+ struct usb_redir_interrupt_packet_header *interrupt_packet,
+ uint8_t *data, int data_len)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = interrupt_packet->endpoint;
+
+ DPRINTF("interrupt-in status %d ep %02X len %d id %u\n",
+ interrupt_packet->status, ep, data_len, id);
+
+ if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
+ ERROR("received int packet for non interrupt endpoint %02X\n", ep);
+ free(data);
+ return;
+ }
+
+ if (ep & USB_DIR_IN) {
+ if (dev->endpoint[EP2I(ep)].interrupt_started == 0) {
+ DPRINTF("received int packet while not started ep %02X\n", ep);
+ free(data);
+ return;
+ }
+
+ /* bufp_alloc also adds the packet to the ep queue */
+ bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
+ } else {
+ int len = interrupt_packet->length;
+
+ AsyncURB *aurb = async_find(dev, id);
+ if (!aurb) {
+ return;
+ }
+
+ if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) {
+ ERROR("return int packet mismatch, please report this!\n");
+ len = USB_RET_NAK;
+ }
+
+ if (aurb->packet) {
+ aurb->packet->len = usbredir_handle_status(dev,
+ interrupt_packet->status, len);
+ usb_packet_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+ }
+}
+
+static struct USBDeviceInfo usbredir_dev_info = {
+ .product_desc = "USB Redirection Device",
+ .qdev.name = "usb-redir",
+ .qdev.size = sizeof(USBRedirDevice),
+ .init = usbredir_initfn,
+ .handle_destroy = usbredir_handle_destroy,
+ .handle_packet = usb_generic_handle_packet,
+ .cancel_packet = usbredir_cancel_packet,
+ .handle_reset = usbredir_handle_reset,
+ .handle_data = usbredir_handle_data,
+ .handle_control = usbredir_handle_control,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
+ DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void usbredir_register_devices(void)
+{
+ usb_qdev_register(&usbredir_dev_info);
+}
+device_init(usbredir_register_devices);