diff options
author | 2011-07-27 16:49:38 +0300 | |
---|---|---|
committer | 2011-07-27 16:49:38 +0300 | |
commit | 8d0159316d202df2370ccea623b7ef6c9303ce1f (patch) | |
tree | 16b5368255e7edf4070320b5ca128aa5a96cd3d4 | |
parent | device-assignment: remove unused variable e_device (diff) | |
parent | Merge commit 'v0.15.0-rc0' into upstream-merge (diff) | |
download | qemu-kvm-8d0159316d202df2370ccea623b7ef6c9303ce1f.tar.gz qemu-kvm-8d0159316d202df2370ccea623b7ef6c9303ce1f.tar.bz2 qemu-kvm-8d0159316d202df2370ccea623b7ef6c9303ce1f.zip |
Merge branch 'upstream-merge' into next
* upstream-merge: (118 commits)
Bump version to reflect v0.15.0-rc0
Correct spelling of licensed
Register Linux dyntick timer as per-thread signal
mc146818rtc: Handle host clock resets
qemu-timer: Introduce clock reset notifier
notifier: Pass data argument to callback
ide: Turn properties any IDE device must have into bus properties
virtio-serial: Turn props any virtio-serial-bus device must have into bus props
virtio-serial: Clean up virtser_bus_dev_print() output
usb-ccid: Drop unused CCIDCardInfo callback print()
Remove unused USES_X509_AUTH macro from VNC sasl code
Introduce a 'client_add' monitor command accepting an open FD
Store VNC auth scheme per-client as well as per-server
do not reset no_shutdown after we shutdown the vm
vl.c: Don't limit node count by smp count
vga: Fix type of lfb/map_addr/end.
qemu-char: Print strerror message on failure
qemu-timer: change unix timer to dynticks
iothread: replace fair_mutex with a condition variable
report serial devices created with -device in the PIIX4 config space
...
Signed-off-by: Avi Kivity <avi@redhat.com>
224 files changed, 10632 insertions, 3032 deletions
diff --git a/.gitignore b/.gitignore index 113876e81..0d58a9ac6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ libdis* libhw32 libhw64 libuser +qapi-generated qemu-doc.html qemu-tech.html qemu-doc.info @@ -32,6 +33,7 @@ qemu-options.texi qemu-img-cmds.texi qemu-img-cmds.h qemu-io +qemu-ga qemu-monitor.texi QMP/qmp-commands.txt .gdbinit @@ -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; \ @@ -369,4 +406,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 9f99ed4d7..f546fc391 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -172,7 +172,7 @@ user-obj-y += cutils.o cache-utils.o hw-obj-y = hw-obj-y += 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_bridge.o @@ -208,6 +208,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 @@ -375,7 +376,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 c511010dc..f2df4c586 100644 --- a/Makefile.target +++ b/Makefile.target @@ -100,7 +100,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. @@ -197,7 +197,7 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o vl.o balloon.o # need to fix this properly obj-$(CONFIG_NO_PCI) += pci-stub.o obj-$(CONFIG_PCI) += pci.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 @@ -210,6 +210,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 @@ -1 +1 @@ -0.14.50 +0.14.90 @@ -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) @@ -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 382473933..84088477a 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 ac95b88fe..14b2f67f1 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 e1ae3e8c2..6a0a21b69 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 34b64aa20..cd89c8312 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 56bd7195a..91067e759 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 80d106c2b..77a4de510 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 922b23d8f..37478d255 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, §ors, 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 1e265d274..efb68038c 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 c263663c5..30ab29aed 100644 --- a/blockdev.c +++ b/blockdev.c @@ -581,7 +581,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; @@ -590,7 +590,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/compatfd.c b/compatfd.c index 41586ceae..31654c62a 100644 --- a/compatfd.c +++ b/compatfd.c @@ -115,3 +115,14 @@ int qemu_signalfd(const sigset_t *mask) return qemu_signalfd_compat(mask); } + +bool qemu_signalfd_available(void) +{ +#ifdef CONFIG_SIGNALFD + errno = 0; + syscall(SYS_signalfd, -1, NULL, _NSIG / 8); + return errno != ENOSYS; +#else + return false; +#endif +} diff --git a/compatfd.h b/compatfd.h index fc3791520..6b04877b9 100644 --- a/compatfd.h +++ b/compatfd.h @@ -39,5 +39,6 @@ struct qemu_signalfd_siginfo { }; int qemu_signalfd(const sigset_t *mask); +bool qemu_signalfd_available(void); #endif diff --git a/compiler.h b/compiler.h new file mode 100644 index 000000000..9af5dc6c9 --- /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 */ @@ -190,6 +190,7 @@ spice="" rbd="" smartcard="" smartcard_nss="" +usb_redir="" opengl="" # parse CC options first @@ -773,6 +774,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 @@ -1053,6 +1058,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 @@ -1838,6 +1845,17 @@ 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 + ########################################## # libpci header probe for kvm_cap_device_assignment if test $kvm_cap_device_assignment = "yes" ; then @@ -1859,6 +1877,7 @@ EOF fi fi +########################################## # pthread probe PTHREADLIBS_LIST="-lpthread -lpthreadGC2" @@ -2427,6 +2446,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 + ########################################## ########################################## @@ -2576,6 +2611,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" @@ -2676,6 +2712,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 @@ -2908,6 +2945,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 @@ -2974,6 +3012,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 @@ -3546,6 +3588,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" @@ -1514,7 +1514,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) chr->init(chr); } -CharDriverState *text_console_init(QemuOpts *opts) +int text_console_init(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; TextConsole *s; @@ -1546,7 +1546,7 @@ CharDriverState *text_console_init(QemuOpts *opts) if (!s) { free(chr); - return NULL; + return -EBUSY; } s->chr = chr; @@ -1554,7 +1554,9 @@ CharDriverState *text_console_init(QemuOpts *opts) s->g_height = height; chr->opaque = s; chr->chr_set_echo = text_console_set_echo; - return chr; + + *_chr = chr; + return 0; } void text_consoles_set_display(DisplayState *ds) @@ -354,7 +354,7 @@ void vga_hw_text_update(console_ch_t *chardata); int is_graphic_console(void); int is_fixedsize_console(void); -CharDriverState *text_console_init(QemuOpts *opts); +int text_console_init(QemuOpts *opts, CharDriverState **_chr); void text_consoles_set_display(DisplayState *ds); void console_select(unsigned int index); void console_color_init(DisplayState *ds); @@ -372,6 +372,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen); void vnc_display_init(DisplayState *ds); void vnc_display_close(DisplayState *ds); int vnc_display_open(DisplayState *ds, const char *display); +void vnc_display_add_client(DisplayState *ds, int csock, int skipauth); int vnc_display_disable_login(DisplayState *ds); char *vnc_display_local_addr(DisplayState *ds); #ifdef CONFIG_VNC diff --git a/cpu-common.h b/cpu-common.h index a5b80e135..44b04b383 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); @@ -636,7 +636,8 @@ void vm_stop(int reason) #else /* CONFIG_IOTHREAD */ QemuMutex qemu_global_mutex; -static QemuMutex qemu_fair_mutex; +static QemuCond qemu_io_proceeded_cond; +static bool iothread_requesting_mutex; static QemuThread io_thread; @@ -672,7 +673,7 @@ int qemu_init_main_loop(void) qemu_cond_init(&qemu_system_cond); qemu_cond_init(&qemu_pause_cond); qemu_cond_init(&qemu_work_cond); - qemu_mutex_init(&qemu_fair_mutex); + qemu_cond_init(&qemu_io_proceeded_cond); qemu_mutex_init(&qemu_global_mutex); qemu_mutex_lock(&qemu_global_mutex); @@ -760,17 +761,9 @@ static void qemu_tcg_wait_io_event(void) qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex); } - qemu_mutex_unlock(&qemu_global_mutex); - - /* - * Users of qemu_global_mutex can be starved, having no chance - * to acquire it since this path will get to it first. - * So use another lock to provide fairness. - */ - qemu_mutex_lock(&qemu_fair_mutex); - qemu_mutex_unlock(&qemu_fair_mutex); - - qemu_mutex_lock(&qemu_global_mutex); + while (iothread_requesting_mutex) { + qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex); + } for (env = first_cpu; env != NULL; env = env->next_cpu) { qemu_wait_io_event_common(env); @@ -913,12 +906,13 @@ void qemu_mutex_lock_iothread(void) if (kvm_enabled()) { qemu_mutex_lock(&qemu_global_mutex); } else { - qemu_mutex_lock(&qemu_fair_mutex); + iothread_requesting_mutex = true; if (qemu_mutex_trylock(&qemu_global_mutex)) { qemu_cpu_kick_thread(first_cpu); qemu_mutex_lock(&qemu_global_mutex); } - qemu_mutex_unlock(&qemu_fair_mutex); + iothread_requesting_mutex = false; + qemu_cond_broadcast(&qemu_io_proceeded_cond); } } diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt new file mode 100644 index 000000000..b7befb5e4 --- /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$ @@ -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 69acf3b97..9b8d62c23 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 @@ -3246,7 +3246,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; } @@ -3257,7 +3257,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; } @@ -3268,7 +3268,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; } @@ -3279,7 +3279,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 } @@ -3289,7 +3289,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 } @@ -3299,7 +3299,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 aceba7486..a7ee976e4 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/a9mpcore.c b/hw/a9mpcore.c index b5e532839..6f108f4ce 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* 64 external IRQ lines. */ diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index c30a050d2..44eb8aee5 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -23,6 +23,7 @@ #include "acpi.h" #include "sysemu.h" #include "range.h" +#include "ioport.h" //#define DEBUG @@ -69,6 +70,7 @@ typedef struct PIIX4PMState { qemu_irq irq; qemu_irq smi_irq; int kvm_enabled; + Notifier machine_ready; /* for pci hotplug */ ACPIGPE gpe; @@ -317,6 +319,19 @@ static void piix4_powerdown(void *opaque, int irq, int power_failing) acpi_pm1_evt_power_down(pm1a, tmr); } +static void piix4_pm_machine_ready(Notifier *n, void *opaque) +{ + PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready); + uint8_t *pci_conf; + + pci_conf = s->dev.config; + pci_conf[0x5f] = (isa_is_ioport_assigned(0x378) ? 0x80 : 0) | 0x10; + pci_conf[0x63] = 0x60; + pci_conf[0x67] = (isa_is_ioport_assigned(0x3f8) ? 0x08 : 0) | + (isa_is_ioport_assigned(0x2f8) ? 0x90 : 0); + +} + static PIIX4PMState *global_piix4_pm_state; /* cpu hotadd */ static int piix4_pm_initfn(PCIDevice *dev) @@ -355,11 +370,6 @@ static int piix4_pm_initfn(PCIDevice *dev) /* XXX: which specification is used ? The i82731AB has different mappings */ - pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10; - pci_conf[0x63] = 0x60; - pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) | - (serial_hds[1] != NULL ? 0x90 : 0); - pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x09; @@ -372,6 +382,8 @@ static int piix4_pm_initfn(PCIDevice *dev) qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1); pm_smbus_init(&s->dev.qdev, &s->smb); + s->machine_ready.notify = piix4_pm_machine_ready; + qemu_add_machine_init_done_notifier(&s->machine_ready); qemu_register_reset(piix4_reset, s); piix4_acpi_system_hot_add_init(dev->bus, s); diff --git a/hw/an5206.c b/hw/an5206.c index 42a0163fb..04ca420a9 100644 --- a/hw/an5206.c +++ b/hw/an5206.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 9aeeaea75..f8a747289 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * */ diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index 3bbd8856c..b47707f7b 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines diff --git a/hw/arm_boot.c b/hw/arm_boot.c index e0215768b..215d5dec6 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 0e934ecd6..fb07314d5 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* This file contains implementation code for the RealView EB interrupt diff --git a/hw/arm_pic.c b/hw/arm_pic.c index f44568ceb..985148a38 100644 --- a/hw/arm_pic.c +++ b/hw/arm_pic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL + * This code is licensed under the LGPL */ #include "hw.h" diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index 9225b588b..fd0c8bc3d 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/arm_timer.c b/hw/arm_timer.c index dac9e7075..fd9448f05 100644 --- a/hw/arm_timer.c +++ b/hw/arm_timer.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index d06eec9b3..1df8d4db4 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. * * The ARMv7M System controller is fairly tightly tied in with the * NVIC. Much of that is also implemented here. @@ -576,7 +576,7 @@ static void baum_close(struct CharDriverState *chr) qemu_free(baum); } -CharDriverState *chr_baum_init(QemuOpts *opts) +int chr_baum_init(QemuOpts *opts, CharDriverState **_chr) { BaumDriverState *baum; CharDriverState *chr; @@ -629,7 +629,8 @@ CharDriverState *chr_baum_init(QemuOpts *opts) qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; fail: qemu_free_timer(baum->cellCount_timer); @@ -638,5 +639,5 @@ fail_handle: qemu_free(handle); qemu_free(chr); qemu_free(baum); - return NULL; + return -EIO; } diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c index 2937b5c4a..53e9c5c4c 100644 --- a/hw/bitbang_i2c.c +++ b/hw/bitbang_i2c.c @@ -4,7 +4,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "hw.h" #include "bitbang_i2c.h" diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c index 0b0718426..4762e8511 100644 --- a/hw/ccid-card-emulated.c +++ b/hw/ccid-card-emulated.c @@ -4,7 +4,7 @@ * Copyright (c) 2011 Red Hat. * Written by Alon Levy. * - * This code is licenced under the GNU LGPL, version 2 or later. + * This code is licensed under the GNU LGPL, version 2 or later. */ /* @@ -4,7 +4,7 @@ * Copyright (c) 2011 Red Hat. * Written by Alon Levy. * - * This code is licenced under the GNU LGPL, version 2 or later. + * This code is licensed under the GNU LGPL, version 2 or later. */ #ifndef CCID_H @@ -29,7 +29,6 @@ struct CCIDCardState { */ struct CCIDCardInfo { DeviceInfo qdev; - void (*print)(Monitor *mon, CCIDCardState *card, int indent); const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *len); void (*apdu_from_guest)(CCIDCardState *card, const uint8_t *apdu, diff --git a/hw/dp8393x.c b/hw/dp8393x.c index c332dd59d..1bcd8eeba 100644 --- a/hw/dp8393x.c +++ b/hw/dp8393x.c @@ -898,7 +898,7 @@ void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift, s->watchdog = qemu_new_timer_ns(vm_clock, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(s->conf.macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/ds1225y.c b/hw/ds1225y.c index b1c52321f..662d7b516 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/ds1338.c b/hw/ds1338.c index 6f5ae5e6c..3522af5b5 100644 --- a/hw/ds1338.c +++ b/hw/ds1338.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "i2c.h" diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c index cec1cc8e8..eed9e3843 100644 --- a/hw/dummy_m68k.c +++ b/hw/dummy_m68k.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" @@ -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/etraxfs_eth.c b/hw/etraxfs_eth.c index 6aa400720..dff5f55f3 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -602,7 +602,7 @@ void *etraxfs_eth_init(NICInfo *nd, target_phys_addr_t base, int phyaddr) DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory (base, 0x5c, eth->ethregs); - memcpy(eth->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + eth->conf.macaddr = nd->macaddr; eth->conf.vlan = nd->vlan; eth->conf.peer = nd->netdev; diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 85c8c3c7b..34e7526d5 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -316,7 +316,7 @@ int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data, return 1; } -static void fw_cfg_machine_ready(struct Notifier* n) +static void fw_cfg_machine_ready(struct Notifier *n, void *data) { uint32_t len; FWCfgState *s = container_of(n, FWCfgState, machine_ready); @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "i2c.h" diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index d9b8f24bb..6bd8d2027 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -31,6 +31,10 @@ static struct BusInfo ide_bus_info = { .name = "IDE", .size = sizeof(IDEBus), .get_fw_dev_path = idebus_get_fw_dev_path, + .props = (Property[]) { + DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), + DEFINE_PROP_END_OF_LIST(), + }, }; void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id) @@ -174,7 +178,6 @@ static int ide_drive_initfn(IDEDevice *dev) } #define DEFINE_IDE_DEV_PROPERTIES() \ - DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1), \ DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ DEFINE_PROP_STRING("serial", IDEDrive, dev.serial) diff --git a/hw/integratorcp.c b/hw/integratorcp.c index a6c27be82..281410899 100644 --- a/hw/integratorcp.c +++ b/hw/integratorcp.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "sysbus.h" diff --git a/hw/lan9118.c b/hw/lan9118.c index 3f3c05df4..73a8661ca 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2 + * This code is licensed under the GNU GPL v2 */ #include "sysbus.h" diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 940b43abf..e9904c49d 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* ??? Need to check if the {read,write}[wl] routines work properly on @@ -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/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c index 3eff925b0..f8c524286 100644 --- a/hw/marvell_88w8618_audio.c +++ b/hw/marvell_88w8618_audio.c @@ -4,7 +4,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "sysbus.h" #include "hw.h" diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 1c9a706b1..feb3b25ac 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -99,6 +99,7 @@ typedef struct RTCState { QEMUTimer *coalesced_timer; QEMUTimer *second_timer; QEMUTimer *second_timer2; + Notifier clock_reset_notifier; } RTCState; static void rtc_set_time(RTCState *s); @@ -572,6 +573,22 @@ static const VMStateDescription vmstate_rtc = { } }; +static void rtc_notify_clock_reset(Notifier *notifier, void *data) +{ + RTCState *s = container_of(notifier, RTCState, clock_reset_notifier); + int64_t now = *(int64_t *)data; + + rtc_set_date_from_host(&s->dev); + s->next_second_time = now + (get_ticks_per_sec() * 99) / 100; + qemu_mod_timer(s->second_timer2, s->next_second_time); + rtc_timer_update(s, now); +#ifdef TARGET_I386 + if (rtc_td_hack) { + rtc_coalesced_timer_update(s); + } +#endif +} + static void rtc_reset(void *opaque) { RTCState *s = opaque; @@ -608,6 +625,9 @@ static int rtc_initfn(ISADevice *dev) s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s); s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s); + s->clock_reset_notifier.notify = rtc_notify_clock_reset; + qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier); + s->next_second_time = qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100; qemu_mod_timer(s->second_timer2, s->next_second_time); diff --git a/hw/mcf5206.c b/hw/mcf5206.c index 2a618d444..fce282d98 100644 --- a/hw/mcf5206.c +++ b/hw/mcf5206.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf5208.c b/hw/mcf5208.c index 17a692d4a..78fbc5f23 100644 --- a/hw/mcf5208.c +++ b/hw/mcf5208.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c index 21035da34..748eb5906 100644 --- a/hw/mcf_fec.c +++ b/hw/mcf_fec.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "net.h" @@ -471,7 +471,7 @@ void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq) DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x400, s->mmio_index); - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/mcf_intc.c b/hw/mcf_intc.c index ac0429519..6cb0a09b7 100644 --- a/hw/mcf_intc.c +++ b/hw/mcf_intc.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c index db57096af..905e116de 100644 --- a/hw/mcf_uart.c +++ b/hw/mcf_uart.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.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 a1003945f..f6ab6dc45 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/mipsnet.c b/hw/mipsnet.c index 26aad51ea..0db3ba7a8 100644 --- a/hw/mipsnet.c +++ b/hw/mipsnet.c @@ -258,7 +258,7 @@ void mipsnet_init (int base, qemu_irq irq, NICInfo *nd) s->irq = irq; if (nd) { - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/mpcore.c b/hw/mpcore.c index 379065a3e..d77850751 100644 --- a/hw/mpcore.c +++ b/hw/mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/msmouse.c b/hw/msmouse.c index 05f893ca9..67c6cd43e 100644 --- a/hw/msmouse.c +++ b/hw/msmouse.c @@ -64,7 +64,7 @@ static void msmouse_chr_close (struct CharDriverState *chr) qemu_free (chr); } -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) +int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; @@ -74,5 +74,6 @@ CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); - return chr; + *_chr = chr; + return 0; } diff --git a/hw/msmouse.h b/hw/msmouse.h index 456cb2142..8b853b35b 100644 --- a/hw/msmouse.h +++ b/hw/msmouse.h @@ -1,2 +1,2 @@ /* msmouse.c */ -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts); +int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr); diff --git a/hw/multiboot.c b/hw/multiboot.c index 6e6cfb953..2426e8483 100644 --- a/hw/multiboot.c +++ b/hw/multiboot.c @@ -97,11 +97,11 @@ typedef struct { static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline) { - int len = strlen(cmdline) + 1; target_phys_addr_t p = s->offset_cmdlines; + char *b = (char *)s->mb_buf + p; - pstrcpy((char *)s->mb_buf + p, len, cmdline); - s->offset_cmdlines += len; + get_opt_value(b, strlen(cmdline) + 1, cmdline); + s->offset_cmdlines += strlen(b) + 1; return s->mb_buf_phys + p; } @@ -238,7 +238,7 @@ int load_multiboot(void *fw_cfg, const char *r = initrd_filename; mbs.mb_buf_size += strlen(r) + 1; mbs.mb_mods_avail = 1; - while ((r = strchr(r, ','))) { + while (*(r = get_opt_value(NULL, 0, r))) { mbs.mb_mods_avail++; r++; } @@ -252,7 +252,7 @@ int load_multiboot(void *fw_cfg, mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE; if (initrd_filename) { - char *next_initrd; + char *next_initrd, not_last; mbs.offset_mods = mbs.mb_buf_size; @@ -261,9 +261,9 @@ int load_multiboot(void *fw_cfg, int mb_mod_length; uint32_t offs = mbs.mb_buf_size; - next_initrd = strchr(initrd_filename, ','); - if (next_initrd) - *next_initrd = '\0'; + next_initrd = (char *)get_opt_value(NULL, 0, initrd_filename); + not_last = *next_initrd; + *next_initrd = '\0'; /* if a space comes after the module filename, treat everything after that as parameters */ target_phys_addr_t c = mb_add_cmdline(&mbs, initrd_filename); @@ -287,7 +287,7 @@ int load_multiboot(void *fw_cfg, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); initrd_filename = next_initrd+1; - } while (next_initrd); + } while (not_last); } /* Commandline support */ diff --git a/hw/musicpal.c b/hw/musicpal.c index 52b2931d1..63dd39117 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -3,7 +3,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "sysbus.h" diff --git a/hw/pl011.c b/hw/pl011.c index 3b94b14cb..997ce848f 100644 --- a/hw/pl011.c +++ b/hw/pl011.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl022.c b/hw/pl022.c index 00e494a0d..9a1cb710f 100644 --- a/hw/pl022.c +++ b/hw/pl022.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl050.c b/hw/pl050.c index b155cc07b..f7fa2e253 100644 --- a/hw/pl050.c +++ b/hw/pl050.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl061.c b/hw/pl061.c index 372dfc2da..79e5c53e8 100644 --- a/hw/pl061.c +++ b/hw/pl061.c @@ -5,7 +5,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl080.c b/hw/pl080.c index dd8139ba9..5ba3b0859 100644 --- a/hw/pl080.c +++ b/hw/pl080.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl110.c b/hw/pl110.c index 06d2dfada..62aba17ad 100644 --- a/hw/pl110.c +++ b/hw/pl110.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU LGPL + * This code is licensed under the GNU LGPL */ #include "sysbus.h" diff --git a/hw/pl110_template.h b/hw/pl110_template.h index b3c9077dc..d30333678 100644 --- a/hw/pl110_template.h +++ b/hw/pl110_template.h @@ -4,7 +4,7 @@ * Copyright (c) 2005 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GNU LGPL + * This code is licensed under the GNU LGPL * * Framebuffer format conversion routines. */ diff --git a/hw/pl181.c b/hw/pl181.c index 6bc79f5f7..0943c09ec 100644 --- a/hw/pl181.c +++ b/hw/pl181.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "blockdev.h" diff --git a/hw/pl190.c b/hw/pl190.c index 75f2ba196..8dc7e4286 100644 --- a/hw/pl190.c +++ b/hw/pl190.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/ptimer.c b/hw/ptimer.c index 47964a67e..6f13ce92f 100644 --- a/hw/ptimer.c +++ b/hw/ptimer.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GNU LGPL. + * This code is licensed under the GNU LGPL. */ #include "hw.h" #include "qemu-timer.h" @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski <balrog@zabor.org> * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #ifndef PXA_H # define PXA_H "pxa.h" diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index ac5d95d71..cf9311014 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski <balrog@zabor.org> * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c index a67498b2b..599581e26 100644 --- a/hw/pxa2xx_dma.c +++ b/hw/pxa2xx_dma.c @@ -5,7 +5,7 @@ * Copyright (c) 2006 Thorsten Zitterell * Written by Andrzej Zaborowski <balrog@zabor.org> * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c index e9a536102..bdd82e6bf 100644 --- a/hw/pxa2xx_pic.c +++ b/hw/pxa2xx_pic.c @@ -5,7 +5,7 @@ * Copyright (c) 2006 Thorsten Zitterell * Written by Andrzej Zaborowski <balrog@zabor.org> * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c index f777a2122..4235e4263 100644 --- a/hw/pxa2xx_timer.c +++ b/hw/pxa2xx_timer.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Copyright (c) 2006 Thorsten Zitterell * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" @@ -459,7 +459,7 @@ void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin) void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) { - qdev_prop_set_macaddr(dev, "mac", nd->macaddr); + qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); if (nd->vlan) qdev_prop_set_vlan(dev, "vlan", nd->vlan); if (nd->netdev) @@ -656,8 +656,8 @@ static void qxl_reset_state(PCIQXLDevice *d) QXLRam *ram = d->ram; QXLRom *rom = d->rom; - assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring)); - assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring)); + assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); + assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring)); d->shadow_rom.update_id = cpu_to_le32(0); *rom = d->shadow_rom; qxl_rom_set_dirty(d); diff --git a/hw/realview.c b/hw/realview.c index 82f3d82d4..94ab90051 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/realview_gic.c b/hw/realview_gic.c index db908b643..43a2a0d5e 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index ad6a730be..8b1a41221 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 a8c7372d3..f42a5d1f8 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -12,7 +12,7 @@ * 2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the * MODE SENSE response. * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * * Note that this file only handles the SCSI architecture model and device * commands. Emulation of interface/link layer protocols is handled by @@ -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 8e59c7ee8..63361b354 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -7,7 +7,7 @@ * * Written by Laurent Vivier <Laurent.Vivier@bull.net> * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * */ @@ -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; } @@ -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/sh_intc.c b/hw/sh_intc.c index 0734da90f..c43b99f81 100644 --- a/hw/sh_intc.c +++ b/hw/sh_intc.c @@ -5,7 +5,7 @@ * Based on sh_timer.c and arm_timer.c by Paul Brook * Copyright (c) 2005-2006 CodeSourcery. * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sh_intc.h" diff --git a/hw/sh_timer.c b/hw/sh_timer.c index 5eec6b7c1..5df7fb64b 100644 --- a/hw/sh_timer.c +++ b/hw/sh_timer.c @@ -5,7 +5,7 @@ * Based on arm_timer.c by Paul Brook * Copyright (c) 2005-2006 CodeSourcery. * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/smbus.c b/hw/smbus.c index e46453915..ff027c814 100644 --- a/hw/smbus.c +++ b/hw/smbus.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* TODO: Implement PEC. */ diff --git a/hw/smc91c111.c b/hw/smc91c111.c index 701baafe6..3a8a85c1f 100644 --- a/hw/smc91c111.c +++ b/hw/smc91c111.c @@ -4,7 +4,7 @@ * Copyright (c) 2005 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "sysbus.h" diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 1c901ef6e..646b1e3ca 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/ssd0303.c b/hw/ssd0303.c index b39e2596f..401fdf592 100644 --- a/hw/ssd0303.c +++ b/hw/ssd0303.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* The controller can support a variety of different displays, but we only diff --git a/hw/ssd0323.c b/hw/ssd0323.c index 864396114..1eb3823fe 100644 --- a/hw/ssd0323.c +++ b/hw/ssd0323.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* The controller can support a variety of different displays, but we only diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c index fb4b64927..18dabd64a 100644 --- a/hw/ssi-sd.c +++ b/hw/ssi-sd.c @@ -4,7 +4,7 @@ * Copyright (c) 2007-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "blockdev.h" @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "ssi.h" diff --git a/hw/stellaris.c b/hw/stellaris.c index ac9fcc1f3..a28093043 100644 --- a/hw/stellaris.c +++ b/hw/stellaris.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -1230,7 +1230,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, } } - stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr); + stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a); for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c index 6a0583a25..12919317e 100644 --- a/hw/stellaris_enet.c +++ b/hw/stellaris_enet.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" #include "net.h" diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c index 06c5f9d95..95604ecde 100644 --- a/hw/stellaris_input.c +++ b/hw/stellaris_input.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" #include "devices.h" diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index d3922998c..4dda2c483 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -1104,20 +1104,9 @@ static Answer *ccid_peek_next_answer(USBCCIDState *s) : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM]; } -static void ccid_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) -{ - CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev); - CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev->info); - - if (info->print) { - info->print(mon, card, indent); - } -} - static struct BusInfo ccid_bus_info = { .name = "ccid-bus", .size = sizeof(CCIDBus), - .print_dev = ccid_bus_dev_print, .props = (Property[]) { DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb-hid.c b/hw/usb-hid.c index d711b5c0b..b812da2a6 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 86582cc72..6391dad10 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "qemu-common.h" @@ -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-serial.c b/hw/usb-serial.c index 59cb0fb2f..c69c4374e 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -5,7 +5,7 @@ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org> * Written by Paul Brook, reused for FTDI by Samuel Thibault * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "qemu-common.h" diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 2ef4c5b74..da74c57c6 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/versatile_pci.c b/hw/versatile_pci.c index 8e75ffccf..290a9009b 100644 --- a/hw/versatile_pci.c +++ b/hw/versatile_pci.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "sysbus.h" diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 46b6a3f38..147fe29b6 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/vga_int.h b/hw/vga_int.h index ab9074672..957ad0cf2 100644 --- a/hw/vga_int.h +++ b/hw/vga_int.h @@ -106,13 +106,13 @@ typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s); typedef struct VGACommonState { uint8_t *vram_ptr; ram_addr_t vram_offset; + target_phys_addr_t lfb_addr; + target_phys_addr_t lfb_end; + target_phys_addr_t map_addr; + target_phys_addr_t map_end; uint32_t vram_size; - uint32_t lfb_addr; - uint32_t lfb_end; - uint32_t map_addr; - uint32_t map_end; - uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ uint32_t latch; + uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ uint8_t sr_index; uint8_t sr[256]; uint8_t gr_index; diff --git a/hw/virtio-console.c b/hw/virtio-console.c index 7ebfa2651..fe5e188bf 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -139,9 +139,7 @@ static VirtIOSerialPortInfo virtconsole_info = { .init = virtconsole_initfn, .exit = virtconsole_exitfn, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_STRING("name", VirtConsole, port.name), DEFINE_PROP_END_OF_LIST(), }, }; @@ -158,9 +156,7 @@ static VirtIOSerialPortInfo virtserialport_info = { .init = virtconsole_initfn, .exit = virtconsole_exitfn, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_STRING("name", VirtConsole, port.name), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 6997e02dc..a32cc019b 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 6d7338644..c5eb93109 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); @@ -668,20 +668,22 @@ static struct BusInfo virtser_bus_info = { .name = "virtio-serial-bus", .size = sizeof(VirtIOSerialBus), .print_dev = virtser_bus_dev_print, + .props = (Property[]) { + DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), + DEFINE_PROP_STRING("name", VirtIOSerialPort, name), + DEFINE_PROP_END_OF_LIST() + } }; static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) { VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); - monitor_printf(mon, "%*s dev-prop-int: id: %u\n", - indent, "", port->id); - monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n", - indent, "", port->guest_connected); - monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n", - indent, "", port->host_connected); - monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n", - indent, "", port->throttled); + monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n", + indent, "", port->id, + port->guest_connected ? "on" : "off", + port->host_connected ? "on" : "off", + port->throttled ? "on" : "off"); } /* This function is only used if a port id is not provided by the user */ diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c index 3a9215566..6926c54f4 100644 --- a/hw/xen_devconfig.c +++ b/hw/xen_devconfig.c @@ -126,8 +126,8 @@ int xen_config_dev_nic(NICInfo *nic) char mac[20]; snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", - nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], - nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2], + nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]); xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); @@ -59,7 +59,7 @@ static void check_mode_change(void) if (is_absolute != current_is_absolute || has_absolute != current_has_absolute) { - notifier_list_notify(&mouse_mode_notifiers); + notifier_list_notify(&mouse_mode_notifiers, NULL); } current_is_absolute = is_absolute; @@ -245,18 +245,25 @@ void isa_unassign_ioport(pio_addr_t start, int length) int i; for(i = start; i < start + length; i++) { - ioport_read_table[0][i] = default_ioport_readb; - ioport_read_table[1][i] = default_ioport_readw; - ioport_read_table[2][i] = default_ioport_readl; + ioport_read_table[0][i] = NULL; + ioport_read_table[1][i] = NULL; + ioport_read_table[2][i] = NULL; - ioport_write_table[0][i] = default_ioport_writeb; - ioport_write_table[1][i] = default_ioport_writew; - ioport_write_table[2][i] = default_ioport_writel; + ioport_write_table[0][i] = NULL; + ioport_write_table[1][i] = NULL; + ioport_write_table[2][i] = NULL; ioport_opaque[i] = NULL; } } +bool isa_is_ioport_assigned(pio_addr_t start) +{ + return (ioport_read_table[0][start] || ioport_write_table[0][start] || + ioport_read_table[1][start] || ioport_write_table[1][start] || + ioport_read_table[2][start] || ioport_write_table[2][start]); +} + /***********************************************************/ void cpu_outb(pio_addr_t addr, uint8_t val) @@ -43,7 +43,7 @@ int register_ioport_read(pio_addr_t start, int length, int size, int register_ioport_write(pio_addr_t start, int length, int size, IOPortWriteFunc *func, void *opaque); void isa_unassign_ioport(pio_addr_t start, int length); - +bool isa_is_ioport_assigned(pio_addr_t start); void cpu_outb(pio_addr_t addr, uint8_t val); void cpu_outw(pio_addr_t addr, uint16_t val); @@ -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; @@ -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/migration.c b/migration.c index af3a1f270..2a15b98db 100644 --- a/migration.c +++ b/migration.c @@ -124,7 +124,7 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) } current_migration = s; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); return 0; } @@ -276,7 +276,7 @@ void migrate_fd_error(FdMigrationState *s) { DPRINTF("setting error state\n"); s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); migrate_fd_cleanup(s); } @@ -334,7 +334,7 @@ ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size) monitor_resume(s->mon); } s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); } return ret; @@ -395,7 +395,7 @@ void migrate_fd_put_ready(void *opaque) state = MIG_STATE_ERROR; } s->state = state; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); } } @@ -415,7 +415,7 @@ void migrate_fd_cancel(MigrationState *mig_state) DPRINTF("cancelling migration\n"); s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); qemu_savevm_state_cancel(s->mon, s->file); migrate_fd_cleanup(s); @@ -429,7 +429,7 @@ void migrate_fd_release(MigrationState *mig_state) if (s->state == MIG_STATE_ACTIVE) { s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); migrate_fd_cleanup(s); } qemu_free(s); @@ -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); @@ -1206,6 +1206,38 @@ static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data) return -1; } +static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *fdname = qdict_get_str(qdict, "fdname"); + int skipauth = qdict_get_try_bool(qdict, "skipauth", 0); + CharDriverState *s; + + if (strcmp(protocol, "spice") == 0) { + if (!using_spice) { + /* correct one? spice isn't a device ,,, */ + qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice"); + return -1; + } + qerror_report(QERR_ADD_CLIENT_FAILED); + return -1; + } else if (strcmp(protocol, "vnc") == 0) { + int fd = monitor_get_fd(mon, fdname); + vnc_display_add_client(NULL, fd, skipauth); + return 0; + } else if ((s = qemu_chr_find(protocol)) != NULL) { + int fd = monitor_get_fd(mon, fdname); + if (qemu_chr_add_client(s, fd) < 0) { + qerror_report(QERR_ADD_CLIENT_FAILED); + return -1; + } + return 0; + } + + qerror_report(QERR_INVALID_PARAMETER, "protocol"); + return -1; +} + static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *protocol = qdict_get_str(qdict, "protocol"); @@ -776,18 +776,12 @@ static int net_init_nic(QemuOpts *opts, nd->devaddr = qemu_strdup(qemu_opt_get(opts, "addr")); } - nd->macaddr[0] = 0x52; - nd->macaddr[1] = 0x54; - nd->macaddr[2] = 0x00; - nd->macaddr[3] = 0x12; - nd->macaddr[4] = 0x34; - nd->macaddr[5] = 0x56 + idx; - if (qemu_opt_get(opts, "macaddr") && - net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) { + net_parse_macaddr(nd->macaddr.a, qemu_opt_get(opts, "macaddr")) < 0) { error_report("invalid syntax for ethernet address"); return -1; } + qemu_macaddr_default_if_unset(&nd->macaddr); nd->nvectors = qemu_opt_get_number(opts, "vectors", DEV_NVECTORS_UNSPECIFIED); @@ -830,14 +824,15 @@ static const struct { const char *type; net_client_init_func init; QemuOptDesc desc[NET_MAX_DESC]; -} net_client_types[] = { - { +} net_client_types[NET_CLIENT_TYPE_MAX] = { + [NET_CLIENT_TYPE_NONE] = { .type = "none", .desc = { NET_COMMON_PARAMS_DESC, { /* end of list */ } }, - }, { + }, + [NET_CLIENT_TYPE_NIC] = { .type = "nic", .init = net_init_nic, .desc = { @@ -866,8 +861,9 @@ static const struct { }, { /* end of list */ } }, + }, #ifdef CONFIG_SLIRP - }, { + [NET_CLIENT_TYPE_USER] = { .type = "user", .init = net_init_slirp, .desc = { @@ -927,8 +923,9 @@ static const struct { }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_TAP] = { .type = "tap", .init = net_init_tap, .desc = { @@ -975,7 +972,8 @@ static const struct { #endif /* _WIN32 */ { /* end of list */ } }, - }, { + }, + [NET_CLIENT_TYPE_SOCKET] = { .type = "socket", .init = net_init_socket, .desc = { @@ -1003,8 +1001,9 @@ static const struct { }, { /* end of list */ } }, + }, #ifdef CONFIG_VDE - }, { + [NET_CLIENT_TYPE_VDE] = { .type = "vde", .init = net_init_vde, .desc = { @@ -1028,8 +1027,9 @@ static const struct { }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_DUMP] = { .type = "dump", .init = net_init_dump, .desc = { @@ -1046,7 +1046,6 @@ static const struct { { /* end of list */ } }, }, - { /* end of list */ } }; int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) @@ -1094,8 +1093,9 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) name = qemu_opt_get(opts, "name"); } - for (i = 0; net_client_types[i].type != NULL; i++) { - if (!strcmp(net_client_types[i].type, type)) { + for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) { + if (net_client_types[i].type != NULL && + !strcmp(net_client_types[i].type, type)) { VLANState *vlan = NULL; int ret; @@ -1221,25 +1221,38 @@ int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data) return 0; } +static void print_net_client(Monitor *mon, VLANClientState *vc) +{ + monitor_printf(mon, "%s: type=%s,%s\n", vc->name, + net_client_types[vc->info->type].type, vc->info_str); +} + void do_info_network(Monitor *mon) { VLANState *vlan; - VLANClientState *vc; + VLANClientState *vc, *peer; + net_client_type type; QTAILQ_FOREACH(vlan, &vlans, next) { monitor_printf(mon, "VLAN %d devices:\n", vlan->id); QTAILQ_FOREACH(vc, &vlan->clients, next) { - monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str); + monitor_printf(mon, " "); + print_net_client(mon, vc); } } monitor_printf(mon, "Devices not on any VLAN:\n"); QTAILQ_FOREACH(vc, &non_vlan_clients, next) { - monitor_printf(mon, " %s: %s", vc->name, vc->info_str); - if (vc->peer) { - monitor_printf(mon, " peer=%s", vc->peer->name); + peer = vc->peer; + type = vc->info->type; + if (!peer || type == NET_CLIENT_TYPE_NIC) { + monitor_printf(mon, " "); + print_net_client(mon, vc); + } /* else it's a netdev connected to a NIC, printed with the NIC */ + if (peer && type == NET_CLIENT_TYPE_NIC) { + monitor_printf(mon, " \\ "); + print_net_client(mon, peer); } - monitor_printf(mon, "\n"); } } @@ -1326,7 +1339,7 @@ void net_check_clients(void) case NET_CLIENT_TYPE_NIC: has_nic = 1; break; - case NET_CLIENT_TYPE_SLIRP: + case NET_CLIENT_TYPE_USER: case NET_CLIENT_TYPE_TAP: case NET_CLIENT_TYPE_SOCKET: case NET_CLIENT_TYPE_VDE: @@ -31,11 +31,13 @@ typedef struct NICConf { typedef enum { NET_CLIENT_TYPE_NONE, NET_CLIENT_TYPE_NIC, - NET_CLIENT_TYPE_SLIRP, + NET_CLIENT_TYPE_USER, NET_CLIENT_TYPE_TAP, NET_CLIENT_TYPE_SOCKET, NET_CLIENT_TYPE_VDE, - NET_CLIENT_TYPE_DUMP + NET_CLIENT_TYPE_DUMP, + + NET_CLIENT_TYPE_MAX } net_client_type; typedef void (NetPoll)(VLANClientState *, bool enable); @@ -127,7 +129,7 @@ int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data); #define MAX_NICS 8 struct NICInfo { - uint8_t macaddr[6]; + MACAddr macaddr; char *model; char *name; char *devaddr; diff --git a/net/slirp.c b/net/slirp.c index e057a14ce..157b80a9f 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -128,7 +128,7 @@ static void net_slirp_cleanup(VLANClientState *nc) } static NetClientInfo net_slirp_info = { - .type = NET_CLIENT_TYPE_SLIRP, + .type = NET_CLIENT_TYPE_USER, .size = sizeof(SlirpState), .receive = net_slirp_receive, .cleanup = net_slirp_cleanup, @@ -240,7 +240,8 @@ static int net_slirp_init(VLANState *vlan, const char *model, nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name); snprintf(nc->info_str, sizeof(nc->info_str), - "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n'); + "net=%s,restrict=%s", inet_ntoa(net), + restricted ? "on" : "off"); s = DO_UPCAST(SlirpState, nc, nc); @@ -689,6 +690,7 @@ int net_init_slirp(QemuOpts *opts, const char *bootfile; const char *smb_export; const char *vsmbsrv; + const char *restrict_opt; char *vnet = NULL; int restricted = 0; int ret; @@ -702,6 +704,18 @@ int net_init_slirp(QemuOpts *opts, smb_export = qemu_opt_get(opts, "smb"); vsmbsrv = qemu_opt_get(opts, "smbserver"); + restrict_opt = qemu_opt_get(opts, "restrict"); + if (restrict_opt) { + if (!strcmp(restrict_opt, "on") || + !strcmp(restrict_opt, "yes") || !strcmp(restrict_opt, "y")) { + restricted = 1; + } else if (strcmp(restrict_opt, "off") && + strcmp(restrict_opt, "no") && strcmp(restrict_opt, "n")) { + error_report("invalid option: 'restrict=%s'", restrict_opt); + return -1; + } + } + if (qemu_opt_get(opts, "ip")) { const char *ip = qemu_opt_get(opts, "ip"); int l = strlen(ip) + strlen("/24") + 1; @@ -720,11 +734,6 @@ int net_init_slirp(QemuOpts *opts, vnet = qemu_strdup(qemu_opt_get(opts, "net")); } - if (qemu_opt_get(opts, "restrict") && - qemu_opt_get(opts, "restrict")[0] == 'y') { - restricted = 1; - } - qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0); ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost, @@ -29,11 +29,11 @@ void notifier_list_remove(NotifierList *list, Notifier *notifier) QTAILQ_REMOVE(&list->notifiers, notifier, node); } -void notifier_list_notify(NotifierList *list) +void notifier_list_notify(NotifierList *list, void *data) { Notifier *notifier, *next; QTAILQ_FOREACH_SAFE(notifier, &list->notifiers, node, next) { - notifier->notify(notifier); + notifier->notify(notifier, data); } } @@ -20,7 +20,7 @@ typedef struct Notifier Notifier; struct Notifier { - void (*notify)(Notifier *notifier); + void (*notify)(Notifier *notifier, void *data); QTAILQ_ENTRY(Notifier) node; }; @@ -38,6 +38,6 @@ void notifier_list_add(NotifierList *list, Notifier *notifier); void notifier_list_remove(NotifierList *list, Notifier *notifier); -void notifier_list_notify(NotifierList *list); +void notifier_list_notify(NotifierList *list, void *data); #endif diff --git a/pc-bios/README b/pc-bios/README index 40568f82a..f74b2463f 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 Binary files differindex b2dc5c5e7..ea9cc3214 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 Binary files differindex 70a223d57..7e746b452 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 @@ -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 000000000..fde5971e8 --- /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 000000000..3acedad7e --- /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 000000000..8cde4dd0d --- /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 000000000..5842bc79b --- /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 000000000..a79bc2b3c --- /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 000000000..ddef3eda1 --- /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 000000000..e850746b7 --- /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 000000000..f1c26e4b2 --- /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 000000000..558469325 --- /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 000000000..6a1adc9fc --- /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 000000000..3f798f033 --- /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 000000000..c398cac4f --- /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 000000000..4a649c250 --- /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 000000000..3fe8866d4 --- /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-char.c b/qemu-char.c index fb13b2845..dcf706592 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -168,6 +168,11 @@ int qemu_chr_get_msgfd(CharDriverState *s) return s->get_msgfd ? s->get_msgfd(s) : -1; } +int qemu_chr_add_client(CharDriverState *s, int fd) +{ + return s->chr_add_client ? s->chr_add_client(s, fd) : -1; +} + void qemu_chr_accept_input(CharDriverState *s) { if (s->chr_accept_input) @@ -219,13 +224,15 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static CharDriverState *qemu_chr_open_null(QemuOpts *opts) +static int qemu_chr_open_null(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; chr = qemu_mallocz(sizeof(CharDriverState)); chr->chr_write = null_chr_write; - return chr; + + *_chr= chr; + return 0; } /* MUX driver for serial I/O splitting */ @@ -634,18 +641,21 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) return chr; } -static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) +static int qemu_chr_open_file_out(QemuOpts *opts, CharDriverState **_chr) { int fd_out; TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"), O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); - if (fd_out < 0) - return NULL; - return qemu_chr_open_fd(-1, fd_out); + if (fd_out < 0) { + return -errno; + } + + *_chr = qemu_chr_open_fd(-1, fd_out); + return 0; } -static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) +static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) { int fd_in, fd_out; char filename_in[256], filename_out[256]; @@ -653,7 +663,7 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) if (filename == NULL) { fprintf(stderr, "chardev: pipe: no filename given\n"); - return NULL; + return -EINVAL; } snprintf(filename_in, 256, "%s.in", filename); @@ -665,11 +675,14 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) close(fd_in); if (fd_out >= 0) close(fd_out); - TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY)); - if (fd_in < 0) - return NULL; + TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY)); + if (fd_in < 0) { + return -errno; + } } - return qemu_chr_open_fd(fd_in, fd_out); + + *_chr = qemu_chr_open_fd(fd_in, fd_out); + return 0; } @@ -760,12 +773,14 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr) fd_chr_close(chr); } -static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) +static int qemu_chr_open_stdio(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; - if (stdio_nb_clients >= STDIO_MAX_CLIENTS) - return NULL; + if (stdio_nb_clients >= STDIO_MAX_CLIENTS) { + return -EBUSY; + } + if (stdio_nb_clients == 0) { old_fd0_flags = fcntl(0, F_GETFL); tcgetattr (0, &oldtty); @@ -782,7 +797,8 @@ static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) display_type != DT_NOGRAPHIC); qemu_chr_set_echo(chr, false); - return chr; + *_chr = chr; + return 0; } #ifdef __sun__ @@ -969,7 +985,7 @@ static void pty_chr_close(struct CharDriverState *chr) qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) +static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; PtyCharDriver *s; @@ -987,7 +1003,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) s = qemu_mallocz(sizeof(PtyCharDriver)); if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) { - return NULL; + return -errno; } /* Set raw attributes on the pty. */ @@ -1009,7 +1025,8 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); - return chr; + *_chr = chr; + return 0; } static void tty_serial_init(int fd, int speed, @@ -1210,30 +1227,28 @@ static void qemu_chr_close_tty(CharDriverState *chr) } } -static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) +static int qemu_chr_open_tty(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; int fd; - TFR(fd = open(filename, O_RDWR | O_NONBLOCK)); + TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK)); if (fd < 0) { - return NULL; + return -errno; } tty_serial_init(fd, 115200, 'N', 8, 1); chr = qemu_chr_open_fd(fd, fd); - if (!chr) { - close(fd); - return NULL; - } chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; - return chr; + + *_chr = chr; + return 0; } #else /* ! __linux__ && ! __sun__ */ -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) +static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) { - return NULL; + return -ENOTSUP; } #endif /* __linux__ || __sun__ */ @@ -1347,7 +1362,7 @@ static void pp_close(CharDriverState *chr) qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1355,12 +1370,13 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) int fd; TFR(fd = open(filename, O_RDWR)); - if (fd < 0) - return NULL; + if (fd < 0) { + return -errno; + } if (ioctl(fd, PPCLAIM) < 0) { close(fd); - return NULL; + return -errno; } drv = qemu_mallocz(sizeof(ParallelCharDriver)); @@ -1375,7 +1391,8 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; } #endif /* __linux__ */ @@ -1417,21 +1434,24 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; int fd; - fd = open(filename, O_RDWR); - if (fd < 0) - return NULL; + fd = qemu_open(filename, O_RDWR); + if (fd < 0) { + return -errno; + } chr = qemu_mallocz(sizeof(CharDriverState)); chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - return chr; + + *_chr = chr; + return 0; } #endif @@ -1637,7 +1657,7 @@ static int win_chr_poll(void *opaque) return 0; } -static CharDriverState *qemu_chr_open_win(QemuOpts *opts) +static int qemu_chr_open_win(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1652,10 +1672,12 @@ static CharDriverState *qemu_chr_open_win(QemuOpts *opts) if (win_chr_init(chr, filename) < 0) { free(s); free(chr); - return NULL; + return -EIO; } qemu_chr_generic_open(chr); - return chr; + + *_chr = chr; + return 0; } static int win_chr_pipe_poll(void *opaque) @@ -1737,7 +1759,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) } -static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) +static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1752,10 +1774,12 @@ static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) if (win_chr_pipe_init(chr, filename) < 0) { free(s); free(chr); - return NULL; + return -EIO; } qemu_chr_generic_open(chr); - return chr; + + *_chr = chr; + return 0; } static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) @@ -1772,22 +1796,23 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) return chr; } -static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts) +static int qemu_chr_open_win_con(QemuOpts *opts, CharDriverState **_chr) { - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); + return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), chr); } -static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) +static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr) { const char *file_out = qemu_opt_get(opts, "path"); HANDLE fd_out; fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd_out == INVALID_HANDLE_VALUE) - return NULL; + if (fd_out == INVALID_HANDLE_VALUE) { + return -EIO; + } - return qemu_chr_open_win_file(fd_out); + return qemu_chr_open_win_file(fd_out, _chr); } #endif /* !_WIN32 */ @@ -1868,11 +1893,12 @@ static void udp_chr_close(CharDriverState *chr) qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) +static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr = NULL; NetCharDriver *s = NULL; int fd = -1; + int ret; chr = qemu_mallocz(sizeof(CharDriverState)); s = qemu_mallocz(sizeof(NetCharDriver)); @@ -1880,6 +1906,7 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) fd = inet_dgram_opts(opts); if (fd < 0) { fprintf(stderr, "inet_dgram_opts failed\n"); + ret = -errno; goto return_err; } @@ -1890,16 +1917,17 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_close = udp_chr_close; - return chr; + + *_chr = chr; + return 0; return_err: - if (chr) - free(chr); - if (s) - free(s); - if (fd >= 0) + qemu_free(chr); + qemu_free(s); + if (fd >= 0) { closesocket(fd); - return NULL; + } + return ret; } /***********************************************************/ @@ -2123,6 +2151,22 @@ static void socket_set_nodelay(int fd) setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); } +static int tcp_chr_add_client(CharDriverState *chr, int fd) +{ + TCPCharDriver *s = chr->opaque; + if (s->fd != -1) + return -1; + + socket_set_nonblock(fd); + if (s->do_nodelay) + socket_set_nodelay(fd); + s->fd = fd; + qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); + tcp_chr_connect(chr); + + return 0; +} + static void tcp_chr_accept(void *opaque) { CharDriverState *chr = opaque; @@ -2155,12 +2199,8 @@ static void tcp_chr_accept(void *opaque) break; } } - socket_set_nonblock(fd); - if (s->do_nodelay) - socket_set_nodelay(fd); - s->fd = fd; - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); - tcp_chr_connect(chr); + if (tcp_chr_add_client(chr, fd) < 0) + close(fd); } static void tcp_chr_close(CharDriverState *chr) @@ -2178,7 +2218,7 @@ static void tcp_chr_close(CharDriverState *chr) qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) +static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; @@ -2188,6 +2228,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) int do_nodelay; int is_unix; int is_telnet; + int ret; is_listen = qemu_opt_get_bool(opts, "server", 0); is_waitconnect = qemu_opt_get_bool(opts, "wait", 1); @@ -2213,8 +2254,10 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) fd = inet_connect_opts(opts); } } - if (fd < 0) + if (fd < 0) { + ret = -errno; goto fail; + } if (!is_waitconnect) socket_set_nonblock(fd); @@ -2230,6 +2273,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) chr->chr_write = tcp_chr_write; chr->chr_close = tcp_chr_close; chr->get_msgfd = tcp_get_msgfd; + chr->chr_add_client = tcp_chr_add_client; if (is_listen) { s->listen_fd = fd; @@ -2266,14 +2310,16 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) tcp_chr_accept(chr); socket_set_nonblock(s->listen_fd); } - return chr; + + *_chr = chr; + return 0; fail: if (fd >= 0) closesocket(fd); qemu_free(s); qemu_free(chr); - return NULL; + return ret; } /***********************************************************/ @@ -2466,7 +2512,7 @@ fail: static const struct { const char *name; - CharDriverState *(*open)(QemuOpts *opts); + int (*open)(QemuOpts *opts, CharDriverState **chr); } backend_table[] = { { .name = "null", .open = qemu_chr_open_null }, { .name = "socket", .open = qemu_chr_open_socket }, @@ -2506,6 +2552,7 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, { CharDriverState *chr; int i; + int ret; if (qemu_opts_id(opts) == NULL) { fprintf(stderr, "chardev: no id specified\n"); @@ -2527,10 +2574,10 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, return NULL; } - chr = backend_table[i].open(opts); - if (!chr) { - fprintf(stderr, "chardev: opening backend \"%s\" failed\n", - qemu_opt_get(opts, "backend")); + ret = backend_table[i].open(opts, &chr); + if (ret < 0) { + fprintf(stderr, "chardev: opening backend \"%s\" failed: %s\n", + qemu_opt_get(opts, "backend"), strerror(-ret)); return NULL; } diff --git a/qemu-char.h b/qemu-char.h index 892c6da9a..f361c6d28 100644 --- a/qemu-char.h +++ b/qemu-char.h @@ -57,6 +57,7 @@ struct CharDriverState { void (*chr_update_read_handler)(struct CharDriverState *s); int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); int (*get_msgfd)(struct CharDriverState *s); + int (*chr_add_client)(struct CharDriverState *chr, int fd); IOEventHandler *chr_event; IOCanReadHandler *chr_can_read; IOReadHandler *chr_read; @@ -99,6 +100,7 @@ int qemu_chr_can_read(CharDriverState *s); void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len); int qemu_chr_get_msgfd(CharDriverState *s); void qemu_chr_accept_input(CharDriverState *s); +int qemu_chr_add_client(CharDriverState *s, int fd); void qemu_chr_info_print(Monitor *mon, const QObject *ret_data); void qemu_chr_info(Monitor *mon, QObject **ret_data); CharDriverState *qemu_chr_find(const char *name); diff --git a/qemu-common.h b/qemu-common.h index c2b79bd60..ba5571970 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 efa892c5d..d96e84dcb 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", },{ .name = "boot", .type = QEMU_OPT_BOOL, @@ -462,9 +468,14 @@ QemuOptsList qemu_option_rom_opts = { static QemuOptsList qemu_machine_opts = { .name = "machine", + .implied_opt_name = "type", .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head), .desc = { { + .name = "type", + .type = QEMU_OPT_STRING, + .help = "emulated machine" + }, { .name = "accel", .type = QEMU_OPT_STRING, .help = "accelerator list", diff --git a/qemu-ga.c b/qemu-ga.c new file mode 100644 index 000000000..6e2f61fe3 --- /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 2b70618c7..1299e83ef 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 54137a4e9..b205e98dd 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 526474c11..495a1b669 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. @@ -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 cb3347ecc..0900df188 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -27,14 +27,29 @@ STEXI Display version information and exit ETEXI -DEF("M", HAS_ARG, QEMU_OPTION_M, - "-M machine select emulated machine (-M ? for list)\n", QEMU_ARCH_ALL) +DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ + "-machine [type=]name[,prop[=value][,...]]\n" + " selects emulated machine (-machine ? for list)\n" + " property accel=accel1[:accel2[:...]] selects accelerator\n" + " supported accelerators are kvm, xen, tcg (default: tcg)\n", + QEMU_ARCH_ALL) STEXI -@item -M @var{machine} -@findex -M -Select the emulated @var{machine} (@code{-M ?} for list) +@item -machine [type=]@var{name}[,prop=@var{value}[,...]] +@findex -machine +Select the emulated machine by @var{name}. Use @code{-machine ?} to list +available machines. Supported machine properties are: +@table @option +@item accel=@var{accels1}[:@var{accels2}[:...]] +This is used to enable an accelerator. Depending on the target architecture, +kvm, xen, or tcg can be available. By default, tcg is used. If there is more +than one accelerator specified, the next one is used if the previous one fails +to initialize. +@end table ETEXI +HXCOMM Deprecated by -machine +DEF("M", HAS_ARG, QEMU_OPTION_M, "", QEMU_ARCH_ALL) + DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, "-cpu cpu select CPU (-cpu ? for list)\n", QEMU_ARCH_ALL) STEXI @@ -160,6 +175,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. @item boot=@var{boot} @var{boot} is "on" or "off" and allows for booting from non-traditional interfaces, such as virtio. @end table @@ -1094,7 +1117,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, "-net nic[,vlan=n][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n" " create a new Network Interface Card and connect it to VLAN 'n'\n" #ifdef CONFIG_SLIRP - "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=y|n]\n" + "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=on|off]\n" " [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f]\n" " [,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 @@ -1187,7 +1210,7 @@ either in the form a.b.c.d or as number of valid top-most bits. Default is Specify the guest-visible address of the host. Default is the 2nd IP in the guest network, i.e. x.x.x.2. -@item restrict=y|yes|n|no +@item restrict=on|off If this option is enabled, the guest will be isolated, i.e. it will not be able to contact the host and no guest IP packets will be routed over the host to the outside. This option does not affect any explicitly set forwarding rules. @@ -2068,16 +2091,6 @@ Enable KVM full virtualization support. This option is only available if KVM support is enabled when compiling. ETEXI -DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ - "-machine accel=accel1[:accel2] use an accelerator (kvm,xen,tcg), default is tcg\n", QEMU_ARCH_ALL) -STEXI -@item -machine accel=@var{accels} -@findex -machine -This is use to enable an accelerator, in kvm,xen,tcg. -By default, it use only tcg. If there a more than one accelerator -specified, the next one is used if the first don't work. -ETEXI - DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, "-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL) DEF("xen-create", 0, QEMU_OPTION_xen_create, diff --git a/qemu-timer.c b/qemu-timer.c index 72066c7c5..30e8f1272 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -150,6 +150,9 @@ struct QEMUClock { int enabled; QEMUTimer *warp_timer; + + NotifierList reset_notifiers; + int64_t last; }; struct QEMUTimer { @@ -218,6 +221,7 @@ static void win32_rearm_timer(struct qemu_alarm_timer *t); static int unix_start_timer(struct qemu_alarm_timer *t); static void unix_stop_timer(struct qemu_alarm_timer *t); +static void unix_rearm_timer(struct qemu_alarm_timer *t); #ifdef __linux__ @@ -290,7 +294,7 @@ static struct qemu_alarm_timer alarm_timers[] = { {"dynticks", dynticks_start_timer, dynticks_stop_timer, dynticks_rearm_timer}, #endif - {"unix", unix_start_timer, unix_stop_timer, NULL}, + {"unix", unix_start_timer, unix_stop_timer, unix_rearm_timer}, #else {"mmtimer", mm_start_timer, mm_stop_timer, NULL}, {"mmtimer2", mm_start_timer, mm_stop_timer, mm_rearm_timer}, @@ -375,9 +379,15 @@ static QEMUTimer *active_timers[QEMU_NUM_CLOCKS]; static QEMUClock *qemu_new_clock(int type) { QEMUClock *clock; + clock = qemu_mallocz(sizeof(QEMUClock)); clock->type = type; clock->enabled = 1; + notifier_list_init(&clock->reset_notifiers); + /* required to detect & report backward jumps */ + if (type == QEMU_CLOCK_HOST) { + clock->last = get_clock_realtime(); + } return clock; } @@ -592,6 +602,8 @@ static void qemu_run_timers(QEMUClock *clock) int64_t qemu_get_clock_ns(QEMUClock *clock) { + int64_t now, last; + switch(clock->type) { case QEMU_CLOCK_REALTIME: return get_clock(); @@ -603,10 +615,26 @@ int64_t qemu_get_clock_ns(QEMUClock *clock) return cpu_get_clock(); } case QEMU_CLOCK_HOST: - return get_clock_realtime(); + now = get_clock_realtime(); + last = clock->last; + clock->last = now; + if (now < last) { + notifier_list_notify(&clock->reset_notifiers, &now); + } + return now; } } +void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier) +{ + notifier_list_add(&clock->reset_notifiers, notifier); +} + +void qemu_unregister_clock_reset_notifier(QEMUClock *clock, Notifier *notifier) +{ + notifier_list_remove(&clock->reset_notifiers, notifier); +} + void init_clocks(void) { rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME); @@ -803,6 +831,8 @@ static int64_t qemu_next_alarm_deadline(void) #if defined(__linux__) +#include "compatfd.h" + static int dynticks_start_timer(struct qemu_alarm_timer *t) { struct sigevent ev; @@ -822,6 +852,12 @@ static int dynticks_start_timer(struct qemu_alarm_timer *t) memset(&ev, 0, sizeof(ev)); ev.sigev_value.sival_int = 0; ev.sigev_notify = SIGEV_SIGNAL; +#ifdef SIGEV_THREAD_ID + if (qemu_signalfd_available()) { + ev.sigev_notify = SIGEV_THREAD_ID; + ev._sigev_un._tid = qemu_get_thread_id(); + } +#endif /* SIGEV_THREAD_ID */ ev.sigev_signo = SIGALRM; if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { @@ -890,8 +926,6 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t) static int unix_start_timer(struct qemu_alarm_timer *t) { struct sigaction act; - struct itimerval itv; - int err; /* timer signal */ sigfillset(&act.sa_mask); @@ -899,18 +933,35 @@ static int unix_start_timer(struct qemu_alarm_timer *t) act.sa_handler = host_alarm_handler; sigaction(SIGALRM, &act, NULL); + return 0; +} - itv.it_interval.tv_sec = 0; - /* for i386 kernel 2.6 to get 1 ms */ - itv.it_interval.tv_usec = 999; - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 10 * 1000; +static void unix_rearm_timer(struct qemu_alarm_timer *t) +{ + struct itimerval itv; + int64_t nearest_delta_ns = INT64_MAX; + int err; - err = setitimer(ITIMER_REAL, &itv, NULL); - if (err) - return -1; + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; - return 0; + nearest_delta_ns = qemu_next_alarm_deadline(); + if (nearest_delta_ns < MIN_TIMER_REARM_NS) + nearest_delta_ns = MIN_TIMER_REARM_NS; + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; /* 0 for one-shot timer */ + itv.it_value.tv_sec = nearest_delta_ns / 1000000000; + itv.it_value.tv_usec = (nearest_delta_ns % 1000000000) / 1000; + err = setitimer(ITIMER_REAL, &itv, NULL); + if (err) { + perror("setitimer"); + fprintf(stderr, "Internal timer error: aborting\n"); + exit(1); + } } static void unix_stop_timer(struct qemu_alarm_timer *t) diff --git a/qemu-timer.h b/qemu-timer.h index 06cbe2091..0a4346984 100644 --- a/qemu-timer.h +++ b/qemu-timer.h @@ -2,6 +2,7 @@ #define QEMU_TIMER_H #include "qemu-common.h" +#include "notify.h" #include <time.h> #include <sys/time.h> @@ -40,6 +41,10 @@ int64_t qemu_get_clock_ns(QEMUClock *clock); void qemu_clock_enable(QEMUClock *clock, int enabled); void qemu_clock_warp(QEMUClock *clock); +void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier); +void qemu_unregister_clock_reset_notifier(QEMUClock *clock, + Notifier *notifier); + QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, QEMUTimerCB *cb, void *opaque); void qemu_free_timer(QEMUTimer *ts); @@ -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", }, @@ -193,6 +198,10 @@ static const QErrorStringTable qerror_table[] = { .desc = "Could not set password", }, { + .error_fmt = QERR_ADD_CLIENT_FAILED, + .desc = "Could not add client", + }, + { .error_fmt = QERR_TOO_MANY_FILES, .desc = "Too many open files", }, @@ -213,6 +222,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)'", + }, {} }; @@ -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 } }" @@ -163,6 +166,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_SET_PASSWD_FAILED \ "{ 'class': 'SetPasswdFailed', 'data': {} }" +#define QERR_ADD_CLIENT_FAILED \ + "{ 'class': 'AddClientFailed', 'data': {} }" + #define QERR_TOO_MANY_FILES \ "{ 'class': 'TooManyFiles', 'data': {} }" @@ -181,4 +187,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 000000000..bc6e0bd4a --- /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 000000000..624972e84 --- /dev/null +++ b/qga/guest-agent-commands.c @@ -0,0 +1,559 @@ +/* + * 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. + */ + +#if defined(__linux__) +#define CONFIG_FSFREEZE +#endif + +#include <glib.h> +#if defined(CONFIG_FSFREEZE) +#include <mntent.h> +#include <linux/fs.h> +#endif +#include <sys/types.h> +#include <sys/ioctl.h> +#include "qga/guest-agent-core.h" +#include "qga-qmp-commands.h" +#include "qerror.h" +#include "qemu-queue.h" + +static GAState *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); +} + +#if defined(CONFIG_FSFREEZE) +static void disable_logging(void) +{ + ga_disable_logging(ga_state); +} + +static void enable_logging(void) +{ + ga_enable_logging(ga_state); +} + +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"); + } + } +} +#else +/* + * Return status of freeze/thaw + */ +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} + +/* + * 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) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} + +/* + * Walk list of frozen file systems in the guest, and thaw them. + */ +int64_t qmp_guest_fsfreeze_thaw(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} +#endif + +/* register init/cleanup routines for stateful command groups */ +void ga_command_state_init(GAState *s, GACommandState *cs) +{ + ga_state = s; +#if defined(CONFIG_FSFREEZE) + ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup); +#endif + 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 000000000..e42b91d36 --- /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); @@ -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 92c5c3a31..54e313ce5 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", @@ -885,6 +919,33 @@ Example: EQMP { + .name = "add_client", + .args_type = "protocol:s,fdname:s,skipauth:b?", + .params = "protocol fdname skipauth", + .help = "add a graphics client", + .user_print = monitor_user_noop, + .mhandler.cmd_new = add_graphics_client, + }, + +SQMP +add_client +---------- + +Add a graphics client + +Arguments: + +- "protocol": protocol name (json-string) +- "fdname": file descriptor name (json-string) + +Example: + +-> { "execute": "add_client", "arguments": { "protocol": "vnc", + "fdname": "myclient" } } +<- { "return": {} } + +EQMP + { .name = "qmp_capabilities", .args_type = "", .params = "", diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 70a2111d1..3498425ff 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 000000000..7242b5060 --- /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 000000000..9ad4c5499 --- /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 000000000..cece32546 --- /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 000000000..252230ef2 --- /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 000000000..56af2329b --- /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/slirp/ip_icmp.c b/slirp/ip_icmp.c index 751a8e249..14a5312c8 100644 --- a/slirp/ip_icmp.c +++ b/slirp/ip_icmp.c @@ -60,6 +60,52 @@ static const int icmp_flush[19] = { /* ADDR MASK REPLY (18) */ 0 }; +void icmp_init(Slirp *slirp) +{ + slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp; + slirp->icmp_last_so = &slirp->icmp; +} + +static int icmp_send(struct socket *so, struct mbuf *m, int hlen) +{ + struct ip *ip = mtod(m, struct ip *); + struct sockaddr_in addr; + + so->s = qemu_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (so->s == -1) { + return -1; + } + + so->so_m = m; + so->so_faddr = ip->ip_dst; + so->so_laddr = ip->ip_src; + so->so_iptos = ip->ip_tos; + so->so_type = IPPROTO_ICMP; + so->so_state = SS_ISFCONNECTED; + so->so_expire = curtime + SO_EXPIRE; + + addr.sin_family = AF_INET; + addr.sin_addr = so->so_faddr; + + insque(so, &so->slirp->icmp); + + if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, + (struct sockaddr *)&addr, sizeof(addr)) == -1) { + DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n", + errno, strerror(errno))); + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); + icmp_detach(so); + } + + return 0; +} + +void icmp_detach(struct socket *so) +{ + closesocket(so->s); + sofree(so); +} + /* * Process a received ICMP message. */ @@ -81,7 +127,7 @@ icmp_input(struct mbuf *m, int hlen) */ if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ freeit: - m_freem(m); + m_free(m); goto end_error; } @@ -97,14 +143,18 @@ icmp_input(struct mbuf *m, int hlen) DEBUG_ARG("icmp_type = %d", icp->icmp_type); switch (icp->icmp_type) { case ICMP_ECHO: - icp->icmp_type = ICMP_ECHOREPLY; ip->ip_len += hlen; /* since ip_input subtracts this */ if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { icmp_reflect(m); + } else if (slirp->restricted) { + goto freeit; } else { struct socket *so; struct sockaddr_in addr; if ((so = socreate(slirp)) == NULL) goto freeit; + if (icmp_send(so, m, hlen) == 0) { + return; + } if(udp_attach(so) == -1) { DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", errno,strerror(errno))); @@ -153,11 +203,11 @@ icmp_input(struct mbuf *m, int hlen) case ICMP_TSTAMP: case ICMP_MASKREQ: case ICMP_REDIRECT: - m_freem(m); + m_free(m); break; default: - m_freem(m); + m_free(m); } /* swith */ end_error: @@ -319,6 +369,7 @@ icmp_reflect(struct mbuf *m) m->m_len -= hlen; icp = mtod(m, struct icmp *); + icp->icmp_type = ICMP_ECHOREPLY; icp->icmp_cksum = 0; icp->icmp_cksum = cksum(m, ip->ip_len - hlen); @@ -349,3 +400,39 @@ icmp_reflect(struct mbuf *m) (void ) ip_output((struct socket *)NULL, m); } + +void icmp_receive(struct socket *so) +{ + struct mbuf *m = so->so_m; + struct ip *ip = mtod(m, struct ip *); + int hlen = ip->ip_hl << 2; + u_char error_code; + struct icmp *icp; + int id, len; + + m->m_data += hlen; + m->m_len -= hlen; + icp = mtod(m, struct icmp *); + + id = icp->icmp_id; + len = recv(so->s, icp, m->m_len, 0); + icp->icmp_id = id; + + m->m_data -= hlen; + m->m_len += hlen; + + if (len == -1 || len == 0) { + if (errno == ENETUNREACH) { + error_code = ICMP_UNREACH_NET; + } else { + error_code = ICMP_UNREACH_HOST; + } + DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno, + strerror(errno))); + icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno)); + } else { + icmp_reflect(so->so_m); + so->so_m = NULL; /* Don't m_free() it again! */ + } + icmp_detach(so); +} diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h index 2692822f8..b3da1f269 100644 --- a/slirp/ip_icmp.h +++ b/slirp/ip_icmp.h @@ -153,9 +153,12 @@ struct icmp { (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) +void icmp_init(Slirp *slirp); void icmp_input(struct mbuf *, int); void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize, const char *message); void icmp_reflect(struct mbuf *); +void icmp_receive(struct socket *so); +void icmp_detach(struct socket *so); #endif diff --git a/slirp/ip_input.c b/slirp/ip_input.c index 768ab0cd4..5e67631ab 100644 --- a/slirp/ip_input.c +++ b/slirp/ip_input.c @@ -58,6 +58,7 @@ ip_init(Slirp *slirp) slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link; udp_init(slirp); tcp_init(slirp); + icmp_init(slirp); } /* @@ -118,27 +119,6 @@ ip_input(struct mbuf *m) goto bad; } - if (slirp->restricted) { - if ((ip->ip_dst.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p != IPPROTO_UDP) - goto bad; - } else { - uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; - struct ex_list *ex_ptr; - - if ((ip->ip_dst.s_addr & inv_mask) == inv_mask) { - goto bad; - } - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) - if (ex_ptr->ex_addr.s_addr == ip->ip_dst.s_addr) - break; - - if (!ex_ptr) - goto bad; - } - } - /* Should drop packet if mbuf too long? hmmm... */ if (m->m_len > ip->ip_len) m_adj(m, ip->ip_len - m->m_len); @@ -225,7 +205,7 @@ ip_input(struct mbuf *m) } return; bad: - m_freem(m); + m_free(m); return; } @@ -318,7 +298,7 @@ ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp) break; } q = q->ipf_next; - m_freem(dtom(slirp, q->ipf_prev)); + m_free(dtom(slirp, q->ipf_prev)); ip_deq(q->ipf_prev); } @@ -384,7 +364,7 @@ insert: return ip; dropfrag: - m_freem(m); + m_free(m); return NULL; } @@ -400,7 +380,7 @@ ip_freef(Slirp *slirp, struct ipq *fp) for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) { p = q->ipf_next; ip_deq(q); - m_freem(dtom(slirp, q)); + m_free(dtom(slirp, q)); } remque(&fp->ip_link); (void) m_free(dtom(slirp, fp)); diff --git a/slirp/ip_output.c b/slirp/ip_output.c index 542f3180b..c82830fe7 100644 --- a/slirp/ip_output.c +++ b/slirp/ip_output.c @@ -159,7 +159,7 @@ sendorfree: if (error == 0) if_output(so, m); else - m_freem(m); + m_free(m); } } @@ -167,6 +167,6 @@ done: return (error); bad: - m_freem(m0); + m_free(m0); goto done; } diff --git a/slirp/mbuf.h b/slirp/mbuf.h index 97729e24b..b74544b42 100644 --- a/slirp/mbuf.h +++ b/slirp/mbuf.h @@ -33,9 +33,6 @@ #ifndef _MBUF_H_ #define _MBUF_H_ -#define m_freem m_free - - #define MINCSIZE 4096 /* Amount to increase mbuf if too small */ /* diff --git a/slirp/misc.c b/slirp/misc.c index 08eba6adc..600255036 100644 --- a/slirp/misc.c +++ b/slirp/misc.c @@ -153,11 +153,12 @@ fork_exec(struct socket *so, const char *ex, int do_pty) return 0; case 0: + setsid(); + /* Set the DISPLAY */ if (do_pty == 2) { (void) close(master); #ifdef TIOCSCTTY /* XXXXX */ - (void) setsid(); ioctl(s, TIOCSCTTY, (char *)NULL); #endif } else { @@ -406,4 +407,17 @@ void slirp_connection_info(Slirp *slirp, Monitor *mon) inet_ntoa(dst_addr), ntohs(dst_port), so->so_rcv.sb_cc, so->so_snd.sb_cc); } + + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { + n = snprintf(buf, sizeof(buf), " ICMP[%d sec]", + (so->so_expire - curtime) / 1000); + src.sin_addr = so->so_laddr; + dst_addr = so->so_faddr; + memset(&buf[n], ' ', 19 - n); + buf[19] = 0; + monitor_printf(mon, "%s %3d %15s - ", buf, so->s, + src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*"); + monitor_printf(mon, "%15s - %5d %5d\n", inet_ntoa(dst_addr), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } } diff --git a/slirp/slirp.c b/slirp/slirp.c index 1593be177..faaa2f36c 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -373,6 +373,31 @@ void slirp_select_fill(int *pnfds, UPD_NFDS(so->s); } } + + /* + * ICMP sockets + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + so_next = so->so_next; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + icmp_detach(so); + continue; + } else { + do_slowtimo = 1; /* Let socket expire */ + } + } + + if (so->so_state & SS_ISFCONNECTED) { + FD_SET(so->s, readfds); + UPD_NFDS(so->s); + } + } } *pnfds = nfds; @@ -542,6 +567,18 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, sorecvfrom(so); } } + + /* + * Check incoming ICMP relies. + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + so_next = so->so_next; + + if (so->s != -1 && FD_ISSET(so->s, readfds)) { + icmp_receive(so); + } + } } /* diff --git a/slirp/slirp.h b/slirp/slirp.h index 954289a8c..16bb6bae4 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -152,6 +152,7 @@ int inet_aton(const char *cp, struct in_addr *ia); #include "tcp_var.h" #include "tcpip.h" #include "udp.h" +#include "ip_icmp.h" #include "mbuf.h" #include "sbuf.h" #include "socket.h" @@ -218,6 +219,10 @@ struct Slirp { struct socket udb; struct socket *udp_last_so; + /* icmp states */ + struct socket icmp; + struct socket *icmp_last_so; + /* tftp states */ char *tftp_prefix; struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; diff --git a/slirp/socket.c b/slirp/socket.c index 611923424..9b8ae13f5 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -71,6 +71,8 @@ sofree(struct socket *so) slirp->tcp_last_so = &slirp->tcb; } else if (so == slirp->udp_last_so) { slirp->udp_last_so = &slirp->udb; + } else if (so == slirp->icmp_last_so) { + slirp->icmp_last_so = &slirp->icmp; } m_free(so->so_m); diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c index e4a77310d..c1214c065 100644 --- a/slirp/tcp_input.c +++ b/slirp/tcp_input.c @@ -136,7 +136,7 @@ tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, i = q->ti_seq + q->ti_len - ti->ti_seq; if (i > 0) { if (i >= ti->ti_len) { - m_freem(m); + m_free(m); /* * Try to present any queued data * at the left window edge to the user. @@ -170,7 +170,7 @@ tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, q = tcpiphdr_next(q); m = tcpiphdr_prev(q)->ti_mbuf; remque(tcpiphdr2qlink(tcpiphdr_prev(q))); - m_freem(m); + m_free(m); } /* @@ -197,7 +197,7 @@ present: m = ti->ti_mbuf; ti = tcpiphdr_next(ti); if (so->so_state & SS_FCANTSENDMORE) - m_freem(m); + m_free(m); else { if (so->so_emu) { if (tcp_emu(so,m)) sbappend(so, m); @@ -451,7 +451,7 @@ findso: acked = ti->ti_ack - tp->snd_una; sbdrop(&so->so_snd, acked); tp->snd_una = ti->ti_ack; - m_freem(m); + m_free(m); /* * If all outstanding data are acked, stop @@ -1260,7 +1260,7 @@ dropafterack: */ if (tiflags & TH_RST) goto drop; - m_freem(m); + m_free(m); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); return; diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index b661d2623..61079b1b2 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -250,7 +250,7 @@ tcp_close(struct tcpcb *tp) t = tcpiphdr_next(t); m = tcpiphdr_prev(t)->ti_mbuf; remque(tcpiphdr2qlink(tcpiphdr_prev(t))); - m_freem(m); + m_free(m); } free(tp); so->so_tcpcb = NULL; diff --git a/slirp/udp.c b/slirp/udp.c index 02b3793e9..5b060f397 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -120,23 +120,26 @@ udp_input(register struct mbuf *m, int iphlen) /* * handle DHCP/BOOTP */ - if (ntohs(uh->uh_dport) == BOOTP_SERVER) { - bootp_input(m); - goto bad; - } - - if (slirp->restricted) { - goto bad; - } + if (ntohs(uh->uh_dport) == BOOTP_SERVER && + (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || + ip->ip_dst.s_addr == 0xffffffff)) { + bootp_input(m); + goto bad; + } /* * handle TFTP */ - if (ntohs(uh->uh_dport) == TFTP_SERVER) { + if (ntohs(uh->uh_dport) == TFTP_SERVER && + ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { tftp_input(m); goto bad; } + if (slirp->restricted) { + goto bad; + } + /* * Locate pcb for datagram. */ @@ -219,7 +222,7 @@ udp_input(register struct mbuf *m, int iphlen) return; bad: - m_freem(m); + m_free(m); return; } diff --git a/softmmu-semi.h b/softmmu-semi.h index 79278cc76..86a9f8a84 100644 --- a/softmmu-semi.h +++ b/softmmu-semi.h @@ -4,7 +4,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ static inline uint32_t softmmu_tget32(CPUState *env, uint32_t addr) diff --git a/spice-qemu-char.c b/spice-qemu-char.c index 605c24123..95bf6b65d 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -159,7 +159,7 @@ static void print_allowed_subtypes(void) fprintf(stderr, "\n"); } -CharDriverState *qemu_chr_open_spice(QemuOpts *opts) +int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; SpiceCharDriver *s; @@ -171,7 +171,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) if (name == NULL) { fprintf(stderr, "spice-qemu-char: missing name parameter\n"); print_allowed_subtypes(); - return NULL; + return -EINVAL; } for(;*psubtype != NULL; ++psubtype) { if (strcmp(name, *psubtype) == 0) { @@ -182,7 +182,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) if (subtype == NULL) { fprintf(stderr, "spice-qemu-char: unsupported name\n"); print_allowed_subtypes(); - return NULL; + return -EINVAL; } chr = qemu_mallocz(sizeof(CharDriverState)); @@ -199,5 +199,6 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; } diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index 78caa796b..919be12a3 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 51d1bd709..8f3915439 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 1022a0375..adef42785 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 f4d12aab5..ae4f334e4 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 7d5533f61..3ad1cb088 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 ebe6eb9fa..843994d8d 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 a18b7dc67..7d4fc545a 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 916551923..b51e35a30 100644 --- a/target-arm/neon_helper.c +++ b/target-arm/neon_helper.c @@ -4,13 +4,13 @@ * Copyright (c) 2007, 2008 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include <stdlib.h> #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/op_addsub.h b/target-arm/op_addsub.h index c02c92adf..ca4a1893c 100644 --- a/target-arm/op_addsub.h +++ b/target-arm/op_addsub.h @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #ifdef ARITH_GE diff --git a/target-arm/translate.c b/target-arm/translate.c index badbc5ff3..34d5e6ef1 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 51a13e38d..76f4fc4a7 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 1a0a476a6..664ffe599 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 b0ac4da5a..33be2962a 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 6b966b184..01315ef0d 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 22ee2743d..a51863cf0 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 7eea1acbd..efab885b8 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) { - (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 + if (TTE_IS_VALID(env->dtlb[i].tte)) { + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" ", %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) { - (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 + if (TTE_IS_VALID(env->itlb[i].tte)) { + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" ", %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 023f4d602..2d36af3a3 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 15af27ba1..8962e3821 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 27c2cf98e..15967c551 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 12ff9d525..a33d21fd0 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 000000000..775290439 --- /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 000000000..5133ad6b1 --- /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, <s, 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 ad11b09c0..713f04208 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/ui/qemu-spice.h b/ui/qemu-spice.h index 3c6f1fe41..f34be69f5 100644 --- a/ui/qemu-spice.h +++ b/ui/qemu-spice.h @@ -42,7 +42,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, void do_info_spice_print(Monitor *mon, const QObject *data); void do_info_spice(Monitor *mon, QObject **ret_data); -CharDriverState *qemu_chr_open_spice(QemuOpts *opts); +int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr); #else /* CONFIG_SPICE */ @@ -481,7 +481,7 @@ static void sdl_grab_end(void) sdl_update_caption(); } -static void sdl_mouse_mode_change(Notifier *notify) +static void sdl_mouse_mode_change(Notifier *notify, void *data) { if (kbd_mouse_is_absolute()) { if (!absolute_enabled) { diff --git a/ui/spice-core.c b/ui/spice-core.c index e142452bb..3d77c0144 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -416,7 +416,7 @@ void do_info_spice(Monitor *mon, QObject **ret_data) *ret_data = QOBJECT(server); } -static void migration_state_notifier(Notifier *notifier) +static void migration_state_notifier(Notifier *notifier, void *data) { int state = get_migration_state(); @@ -480,7 +480,16 @@ void qemu_spice_init(void) port = qemu_opt_get_number(opts, "port", 0); tls_port = qemu_opt_get_number(opts, "tls-port", 0); if (!port && !tls_port) { - return; + fprintf(stderr, "neither port nor tls-port specified for spice."); + exit(1); + } + if (port < 0 || port > 65535) { + fprintf(stderr, "spice port is out of range"); + exit(1); + } + if (tls_port < 0 || tls_port > 65535) { + fprintf(stderr, "spice tls-port is out of range"); + exit(1); } password = qemu_opt_get(opts, "password"); diff --git a/ui/spice-input.c b/ui/spice-input.c index 37c8578a2..75abf5fbe 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -178,7 +178,7 @@ static const SpiceTabletInterface tablet_interface = { .buttons = tablet_buttons, }; -static void mouse_mode_notifier(Notifier *notifier) +static void mouse_mode_notifier(Notifier *notifier, void *data) { QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); bool is_absolute = kbd_mouse_is_absolute(); @@ -213,5 +213,5 @@ void qemu_spice_input_init(void) pointer->absolute = false; pointer->mouse_mode.notify = mouse_mode_notifier; qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode); - mouse_mode_notifier(&pointer->mouse_mode); + mouse_mode_notifier(&pointer->mouse_mode, NULL); } diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 17a621a2b..15af49bdb 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -491,13 +491,6 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s return 0; } -#define USES_X509_AUTH(vs) \ - ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL) - - void start_auth_sasl(VncState *vs) { const char *mechlist = NULL; @@ -538,8 +531,8 @@ void start_auth_sasl(VncState *vs) #ifdef CONFIG_VNC_TLS /* Inform SASL that we've got an external SSF layer from TLS/x509 */ - if (vs->vd->auth == VNC_AUTH_VENCRYPT && - vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) { + if (vs->auth == VNC_AUTH_VENCRYPT && + vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) { gnutls_cipher_algorithm_t cipher; sasl_ssf_t ssf; @@ -570,8 +563,8 @@ void start_auth_sasl(VncState *vs) #ifdef CONFIG_VNC_TLS /* Disable SSF, if using TLS+x509+SASL only. TLS without x509 is not sufficiently strong */ - || (vs->vd->auth == VNC_AUTH_VENCRYPT && - vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) + || (vs->auth == VNC_AUTH_VENCRYPT && + vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) #endif /* CONFIG_VNC_TLS */ ) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index 07c169186..674ba97dc 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -29,7 +29,7 @@ static void start_auth_vencrypt_subauth(VncState *vs) { - switch (vs->vd->subauth) { + switch (vs->subauth) { case VNC_AUTH_VENCRYPT_TLSNONE: case VNC_AUTH_VENCRYPT_X509NONE: VNC_DEBUG("Accept TLS auth none\n"); @@ -51,7 +51,7 @@ static void start_auth_vencrypt_subauth(VncState *vs) #endif /* CONFIG_VNC_SASL */ default: /* Should not be possible, but just in case */ - VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth); + VNC_DEBUG("Reject subauth %d server bug\n", vs->auth); vnc_write_u8(vs, 1); if (vs->minor >= 8) { static const char err[] = "Unsupported authentication type"; @@ -110,17 +110,17 @@ static void vnc_tls_handshake_io(void *opaque) { #define NEED_X509_AUTH(vs) \ - ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) + ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL) static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) { int auth = read_u32(data, 0); - if (auth != vs->vd->subauth) { + if (auth != vs->subauth) { VNC_DEBUG("Rejecting auth %d\n", auth); vnc_write_u8(vs, 0); /* Reject auth */ vnc_flush(vs); @@ -153,10 +153,10 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len vnc_flush(vs); vnc_client_error(vs); } else { - VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth); + VNC_DEBUG("Sending allowed auth %d\n", vs->subauth); vnc_write_u8(vs, 0); /* Accept version */ vnc_write_u8(vs, 1); /* Number of sub-auths */ - vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */ + vnc_write_u32(vs, vs->subauth); /* The supported auth */ vnc_flush(vs); vnc_read_when(vs, protocol_client_vencrypt_auth, 4); } @@ -1346,7 +1346,7 @@ static void client_cut_text(VncState *vs, size_t len, uint8_t *text) { } -static void check_pointer_type_change(Notifier *notifier) +static void check_pointer_type_change(Notifier *notifier, void *data) { VncState *vs = container_of(notifier, VncState, mouse_mode_notifier); int absolute = kbd_mouse_is_absolute(); @@ -1769,7 +1769,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) } } vnc_desktop_resize(vs); - check_pointer_type_change(&vs->mouse_mode_notifier); + check_pointer_type_change(&vs->mouse_mode_notifier, NULL); } static void set_pixel_conversion(VncState *vs) @@ -2124,7 +2124,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) { /* We only advertise 1 auth scheme at a time, so client * must pick the one we sent. Verify this */ - if (data[0] != vs->vd->auth) { /* Reject auth */ + if (data[0] != vs->auth) { /* Reject auth */ VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]); vnc_write_u32(vs, 1); if (vs->minor >= 8) { @@ -2135,7 +2135,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) vnc_client_error(vs); } else { /* Accept requested auth */ VNC_DEBUG("Client requested auth %d\n", (int)data[0]); - switch (vs->vd->auth) { + switch (vs->auth) { case VNC_AUTH_NONE: VNC_DEBUG("Accept auth none\n"); if (vs->minor >= 8) { @@ -2165,7 +2165,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) #endif /* CONFIG_VNC_SASL */ default: /* Should not be possible, but just in case */ - VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth); + VNC_DEBUG("Reject auth %d server code bug\n", vs->auth); vnc_write_u8(vs, 1); if (vs->minor >= 8) { static const char err[] = "Authentication failed"; @@ -2210,26 +2210,26 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len) vs->minor = 3; if (vs->minor == 3) { - if (vs->vd->auth == VNC_AUTH_NONE) { + if (vs->auth == VNC_AUTH_NONE) { VNC_DEBUG("Tell client auth none\n"); - vnc_write_u32(vs, vs->vd->auth); + vnc_write_u32(vs, vs->auth); vnc_flush(vs); start_client_init(vs); - } else if (vs->vd->auth == VNC_AUTH_VNC) { + } else if (vs->auth == VNC_AUTH_VNC) { VNC_DEBUG("Tell client VNC auth\n"); - vnc_write_u32(vs, vs->vd->auth); + vnc_write_u32(vs, vs->auth); vnc_flush(vs); start_auth_vnc(vs); } else { - VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth); + VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth); vnc_write_u32(vs, VNC_AUTH_INVALID); vnc_flush(vs); vnc_client_error(vs); } } else { - VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth); + VNC_DEBUG("Telling client we support auth %d\n", vs->auth); vnc_write_u8(vs, 1); /* num auth */ - vnc_write_u8(vs, vs->vd->auth); + vnc_write_u8(vs, vs->auth); vnc_read_when(vs, protocol_client_auth, 1); vnc_flush(vs); } @@ -2494,12 +2494,25 @@ static void vnc_remove_timer(VncDisplay *vd) } } -static void vnc_connect(VncDisplay *vd, int csock) +static void vnc_connect(VncDisplay *vd, int csock, int skipauth) { VncState *vs = qemu_mallocz(sizeof(VncState)); int i; vs->csock = csock; + + if (skipauth) { + vs->auth = VNC_AUTH_NONE; +#ifdef CONFIG_VNC_TLS + vs->subauth = VNC_AUTH_INVALID; +#endif + } else { + vs->auth = vd->auth; +#ifdef CONFIG_VNC_TLS + vs->subauth = vd->subauth; +#endif + } + vs->lossy_rect = qemu_mallocz(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); for (i = 0; i < VNC_STAT_ROWS; ++i) { vs->lossy_rect[i] = qemu_mallocz(VNC_STAT_COLS * sizeof (uint8_t)); @@ -2557,7 +2570,7 @@ static void vnc_listen_read(void *opaque) int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (csock != -1) { - vnc_connect(vs, csock); + vnc_connect(vs, csock, 0); } } @@ -2887,7 +2900,7 @@ int vnc_display_open(DisplayState *ds, const char *display) } else { int csock = vs->lsock; vs->lsock = -1; - vnc_connect(vs, csock); + vnc_connect(vs, csock, 0); } return 0; @@ -2911,3 +2924,10 @@ int vnc_display_open(DisplayState *ds, const char *display) } return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); } + +void vnc_display_add_client(DisplayState *ds, int csock, int skipauth) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + return vnc_connect(vs, csock, skipauth); +} @@ -256,8 +256,10 @@ struct VncState int major; int minor; + int auth; char challenge[VNC_AUTH_CHALLENGE_SIZE]; #ifdef CONFIG_VNC_TLS + int subauth; /* Used by VeNCrypt */ VncStateTLS tls; #endif #ifdef CONFIG_VNC_SASL diff --git a/usb-linux.c b/usb-linux.c index 1a2deb35c..53cc5fc00 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1260,7 +1260,7 @@ static int usb_host_close(USBHostDevice *dev) return 0; } -static void usb_host_exit_notifier(struct Notifier* n) +static void usb_host_exit_notifier(struct Notifier *n, void *data) { USBHostDevice *s = container_of(n, USBHostDevice, exit); diff --git a/usb-redir.c b/usb-redir.c new file mode 100644 index 000000000..e2129931a --- /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); @@ -1401,7 +1401,6 @@ static void main_loop(void) monitor_protocol_event(QEVENT_SHUTDOWN, NULL); if (no_shutdown) { vm_stop(VMSTOP_SHUTDOWN); - no_shutdown = 0; } else break; } @@ -1902,6 +1901,27 @@ static int debugcon_parse(const char *devname) return 0; } +static QEMUMachine *machine_parse(const char *name) +{ + QEMUMachine *m, *machine = NULL; + + if (name) { + machine = find_machine(name); + } + if (machine) { + return machine; + } + printf("Supported machines are:\n"); + for (m = first_machine; m != NULL; m = m->next) { + if (m->alias) { + printf("%-10s %s (alias of %s)\n", m->alias, m->desc, m->name); + } + printf("%-10s %s%s\n", m->name, m->desc, + m->is_default ? " (default)" : ""); + } + exit(!name || *name != '?'); +} + static int tcg_init(void) { return 0; @@ -1992,7 +2012,7 @@ void qemu_remove_exit_notifier(Notifier *notify) static void qemu_run_exit_notifiers(void) { - notifier_list_notify(&exit_notifiers); + notifier_list_notify(&exit_notifiers, NULL); } void qemu_add_machine_init_done_notifier(Notifier *notify) @@ -2002,7 +2022,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify) static void qemu_run_machine_init_done_notifiers(void) { - notifier_list_notify(&machine_init_done_notifiers); + notifier_list_notify(&machine_init_done_notifiers, NULL); } static const QEMUOption *lookup_opt(int argc, char **argv, @@ -2158,20 +2178,7 @@ int main(int argc, char **argv, char **envp) } switch(popt->index) { case QEMU_OPTION_M: - machine = find_machine(optarg); - if (!machine) { - QEMUMachine *m; - printf("Supported machines are:\n"); - for(m = first_machine; m != NULL; m = m->next) { - if (m->alias) - printf("%-10s %s (alias of %s)\n", - m->alias, m->desc, m->name); - printf("%-10s %s%s\n", - m->name, m->desc, - m->is_default ? " (default)" : ""); - } - exit(*optarg != '?'); - } + machine = machine_parse(optarg); break; case QEMU_OPTION_cpu: /* hw initialization will check this */ @@ -2701,11 +2708,12 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_machine: olist = qemu_find_opts("machine"); qemu_opts_reset(olist); - opts = qemu_opts_parse(olist, optarg, 0); + opts = qemu_opts_parse(olist, optarg, 1); if (!opts) { fprintf(stderr, "parse error: %s\n", optarg); exit(1); } + machine = machine_parse(qemu_opt_get(opts, "type")); break; case QEMU_OPTION_no_kvm: olist = qemu_find_opts("machine"); @@ -3010,8 +3018,8 @@ int main(int argc, char **argv, char **envp) p = qemu_opt_get(QTAILQ_FIRST(&list->head), "accel"); } if (p == NULL) { - opts = qemu_opts_parse(qemu_find_opts("machine"), - machine->default_machine_opts, 0); + qemu_opts_reset(list); + opts = qemu_opts_parse(list, machine->default_machine_opts, 0); if (!opts) { fprintf(stderr, "parse error for machine %s: %s\n", machine->name, machine->default_machine_opts); @@ -3154,8 +3162,8 @@ int main(int argc, char **argv, char **envp) if (nb_numa_nodes > 0) { int i; - if (nb_numa_nodes > smp_cpus) { - nb_numa_nodes = smp_cpus; + if (nb_numa_nodes > MAX_NODES) { + nb_numa_nodes = MAX_NODES; } /* If no memory size if given for any node, assume the default case @@ -839,7 +839,7 @@ static void xen_vm_change_state_handler(void *opaque, int running, int reason) } } -static void xen_exit_notifier(Notifier *n) +static void xen_exit_notifier(Notifier *n, void *data) { XenIOState *state = container_of(n, XenIOState, exit); diff --git a/xen-mapcache.h b/xen-mapcache.h index 606b8afc5..da874ca23 100644 --- a/xen-mapcache.h +++ b/xen-mapcache.h @@ -9,6 +9,10 @@ #ifndef XEN_MAPCACHE_H #define XEN_MAPCACHE_H +#include <stdlib.h> + +#ifdef CONFIG_XEN + void xen_map_cache_init(void); uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock); @@ -16,4 +20,32 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr); void xen_invalidate_map_cache_entry(uint8_t *buffer); void xen_invalidate_map_cache(void); +#else + +static inline void xen_map_cache_init(void) +{ +} + +static inline uint8_t *xen_map_cache(target_phys_addr_t phys_addr, + target_phys_addr_t size, + uint8_t lock) +{ + abort(); +} + +static inline ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +{ + abort(); +} + +static inline void xen_invalidate_map_cache_entry(uint8_t *buffer) +{ +} + +static inline void xen_invalidate_map_cache(void) +{ +} + +#endif + #endif /* !XEN_MAPCACHE_H */ |