diff options
245 files changed, 7412 insertions, 5570 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index d128ed035a..5c3c70c89b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -638,6 +638,7 @@ Subsystems ---------- Audio M: Vassili Karpov (malc) <av1474@comtv.ru> +M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained F: audio/ F: hw/audio/ @@ -766,6 +767,12 @@ M: Blue Swirl <blauwirbel@gmail.com> S: Odd Fixes F: scripts/checkpatch.pl +Seccomp +M: Eduardo Otubo <otubo@linux.vnet.ibm.com> +S: Supported +F: qemu-seccomp.c +F: include/sysemu/seccomp.h + Usermode Emulation ------------------ BSD user @@ -842,6 +849,7 @@ TCI target M: Stefan Weil <sw@weilnetz.de> S: Maintained F: tcg/tci/ +F: tci.c Stable branches --------------- @@ -65,7 +65,7 @@ LIBS+=-lz $(LIBS_TOOLS) HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) ifdef BUILD_DOCS -DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 QMP/qmp-commands.txt +DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt ifdef CONFIG_VIRTFS DOCS+=fsdev/virtfs-proxy-helper.1 endif @@ -236,7 +236,8 @@ clean: find . -name '*.[oda]' -type f -exec rm -f {} + find . -name '*.l[oa]' -type f -exec rm -f {} + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ - rm -Rf .libs + rm -f fsdev/*.pod + rm -rf .libs */.libs rm -f qemu-img-cmds.h @# May not be present in GENERATED_HEADERS rm -f trace/generated-tracers-dtrace.dtrace* @@ -261,6 +262,7 @@ qemu-%.tar.bz2: distclean: clean rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi rm -f config-all-devices.mak config-all-disas.mak + rm -f po/*.mo rm -f roms/seabios/config.mak roms/vgabios/config.mak rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps qemu-doc.dvi rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys @@ -304,7 +306,7 @@ endif install-doc: $(DOCS) $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) qemu-doc.html qemu-tech.html "$(DESTDIR)$(qemu_docdir)" - $(INSTALL_DATA) QMP/qmp-commands.txt "$(DESTDIR)$(qemu_docdir)" + $(INSTALL_DATA) qmp-commands.txt "$(DESTDIR)$(qemu_docdir)" ifdef CONFIG_POSIX $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" $(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1" @@ -398,7 +400,7 @@ qemu-options.texi: $(SRC_PATH)/qemu-options.hx qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") -QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx +qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@") qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx diff --git a/QMP/README b/QMP/README deleted file mode 100644 index c95a08c234..0000000000 --- a/QMP/README +++ /dev/null @@ -1,88 +0,0 @@ - QEMU Monitor Protocol - ===================== - -Introduction -------------- - -The QEMU Monitor Protocol (QMP) allows applications to communicate with -QEMU's Monitor. - -QMP is JSON[1] based and currently has the following features: - -- Lightweight, text-based, easy to parse data format -- Asynchronous messages support (ie. events) -- Capabilities Negotiation - -For detailed information on QMP's usage, please, refer to the following files: - -o qmp-spec.txt QEMU Monitor Protocol current specification -o qmp-commands.txt QMP supported commands (auto-generated at build-time) -o qmp-events.txt List of available asynchronous events - -There is also a simple Python script called 'qmp-shell' available. - -IMPORTANT: It's strongly recommended to read the 'Stability Considerations' -section in the qmp-commands.txt file before making any serious use of QMP. - - -[1] http://www.json.org - -Usage ------ - -To enable QMP, you need a QEMU monitor instance in "control mode". There are -two ways of doing this. - -The simplest one is using the '-qmp' command-line option. The following -example makes QMP available on localhost port 4444: - - $ qemu [...] -qmp tcp:localhost:4444,server - -However, in order to have more complex combinations, like multiple monitors, -the '-mon' command-line option should be used along with the '-chardev' one. -For instance, the following example creates one user monitor on stdio and one -QMP monitor on localhost port 4444. - - $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \ - -chardev socket,id=mon1,host=localhost,port=4444,server \ - -mon chardev=mon1,mode=control - -Please, refer to QEMU's manpage for more information. - -Simple Testing --------------- - -To manually test QMP one can connect with telnet and issue commands by hand: - -$ telnet localhost 4444 -Trying 127.0.0.1... -Connected to localhost. -Escape character is '^]'. -{"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}} -{ "execute": "qmp_capabilities" } -{"return": {}} -{ "execute": "query-version" } -{"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}} - -Development Process -------------------- - -When changing QMP's interface (by adding new commands, events or modifying -existing ones) it's mandatory to update the relevant documentation, which is -one (or more) of the files listed in the 'Introduction' section*. - -Also, it's strongly recommended to send the documentation patch first, before -doing any code change. This is so because: - - 1. Avoids the code dictating the interface - - 2. Review can improve your interface. Letting that happen before - you implement it can save you work. - -* The qmp-commands.txt file is generated from the qmp-commands.hx one, which - is the file that should be edited. - -Homepage --------- - -http://wiki.qemu.org/QMP diff --git a/arch_init.c b/arch_init.c index 150647b6bb..7545d96739 100644 --- a/arch_init.c +++ b/arch_init.c @@ -150,10 +150,9 @@ int qemu_read_default_config_files(bool userconfig) return 0; } -static inline bool is_zero_page(uint8_t *p) +static inline bool is_zero_range(uint8_t *p, uint64_t size) { - return buffer_find_nonzero_offset(p, TARGET_PAGE_SIZE) == - TARGET_PAGE_SIZE; + return buffer_find_nonzero_offset(p, size) == size; } /* struct contains XBZRLE cache and a static page @@ -497,7 +496,7 @@ static int ram_save_block(QEMUFile *f, bool last_stage) acct_info.dup_pages++; } } - } else if (is_zero_page(p)) { + } else if (is_zero_range(p, TARGET_PAGE_SIZE)) { acct_info.dup_pages++; bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS); @@ -710,15 +709,20 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) */ ram_control_after_iterate(f, RAM_CONTROL_ROUND); + bytes_transferred += total_sent; + + /* + * Do not count these 8 bytes into total_sent, so that we can + * return 0 if no page had been dirtied. + */ + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + bytes_transferred += 8; + + ret = qemu_file_get_error(f); if (ret < 0) { - bytes_transferred += total_sent; return ret; } - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - total_sent += 8; - bytes_transferred += total_sent; - return total_sent; } @@ -844,13 +848,14 @@ static inline void *host_from_stream_offset(QEMUFile *f, */ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) { - if (ch != 0 || !is_zero_page(host)) { + if (ch != 0 || !is_zero_range(host, size)) { memset(host, ch, size); #ifndef _WIN32 - if (ch == 0 && - (!kvm_enabled() || kvm_has_sync_mmu()) && - getpagesize() <= TARGET_PAGE_SIZE) { - qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED); + if (ch == 0 && (!kvm_enabled() || kvm_has_sync_mmu())) { + size = size & ~(getpagesize() - 1); + if (size > 0) { + qemu_madvise(host, size, QEMU_MADV_DONTNEED); + } } #endif } diff --git a/audio/mixeng.c b/audio/mixeng.c index 02a9d9fb92..0e4976f271 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -348,7 +348,6 @@ void mixeng_clear (struct st_sample *buf, int len) void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) { -#ifdef CONFIG_MIXEMU if (vol->mute) { mixeng_clear (buf, len); return; @@ -364,9 +363,4 @@ void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) #endif buf += 1; } -#else - (void) buf; - (void) len; - (void) vol; -#endif } @@ -394,18 +394,26 @@ typedef struct CreateCo { char *filename; QEMUOptionParameter *options; int ret; + Error *err; } CreateCo; static void coroutine_fn bdrv_create_co_entry(void *opaque) { + Error *local_err = NULL; + int ret; + CreateCo *cco = opaque; assert(cco->drv); - cco->ret = cco->drv->bdrv_create(cco->filename, cco->options); + ret = cco->drv->bdrv_create(cco->filename, cco->options, &local_err); + if (error_is_set(&local_err)) { + error_propagate(&cco->err, local_err); + } + cco->ret = ret; } int bdrv_create(BlockDriver *drv, const char* filename, - QEMUOptionParameter *options) + QEMUOptionParameter *options, Error **errp) { int ret; @@ -415,9 +423,11 @@ int bdrv_create(BlockDriver *drv, const char* filename, .filename = g_strdup(filename), .options = options, .ret = NOT_DONE, + .err = NULL, }; if (!drv->bdrv_create) { + error_setg(errp, "Driver '%s' does not support image creation", drv->format_name); ret = -ENOTSUP; goto out; } @@ -434,22 +444,37 @@ int bdrv_create(BlockDriver *drv, const char* filename, } ret = cco.ret; + if (ret < 0) { + if (error_is_set(&cco.err)) { + error_propagate(errp, cco.err); + } else { + error_setg_errno(errp, -ret, "Could not create image"); + } + } out: g_free(cco.filename); return ret; } -int bdrv_create_file(const char* filename, QEMUOptionParameter *options) +int bdrv_create_file(const char* filename, QEMUOptionParameter *options, + Error **errp) { BlockDriver *drv; + Error *local_err = NULL; + int ret; drv = bdrv_find_protocol(filename, true); if (drv == NULL) { + error_setg(errp, "Could not find protocol for file '%s'", filename); return -ENOENT; } - return bdrv_create(drv, filename, options); + ret = bdrv_create(drv, filename, options, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } + return ret; } /* @@ -552,7 +577,7 @@ BlockDriver *bdrv_find_protocol(const char *filename, } static int find_image_format(BlockDriverState *bs, const char *filename, - BlockDriver **pdrv) + BlockDriver **pdrv, Error **errp) { int score, score_max; BlockDriver *drv1, *drv; @@ -563,6 +588,7 @@ static int find_image_format(BlockDriverState *bs, const char *filename, if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) { drv = bdrv_find_format("raw"); if (!drv) { + error_setg(errp, "Could not find raw image format"); ret = -ENOENT; } *pdrv = drv; @@ -571,6 +597,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename, ret = bdrv_pread(bs, 0, buf, sizeof(buf)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read image for determining its " + "format"); *pdrv = NULL; return ret; } @@ -587,6 +615,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename, } } if (!drv) { + error_setg(errp, "Could not determine image format: No compatible " + "driver found"); ret = -ENOENT; } *pdrv = drv; @@ -706,10 +736,11 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) * Removes all processed options from *options. */ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, - QDict *options, int flags, BlockDriver *drv) + QDict *options, int flags, BlockDriver *drv, Error **errp) { int ret, open_flags; const char *filename; + Error *local_err = NULL; assert(drv != NULL); assert(bs->file == NULL); @@ -738,6 +769,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bs->read_only = !(open_flags & BDRV_O_RDWR); if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { + error_setg(errp, "Driver '%s' is not whitelisted", drv->format_name); return -ENOTSUP; } @@ -760,26 +792,33 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, /* Open the image, either directly or using a protocol */ if (drv->bdrv_file_open) { assert(file == NULL); - assert(drv->bdrv_parse_filename || filename != NULL); - ret = drv->bdrv_file_open(bs, options, open_flags); + assert(!drv->bdrv_needs_filename || filename != NULL); + ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); } else { if (file == NULL) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't use '%s' as a " - "block driver for the protocol level", - drv->format_name); + error_setg(errp, "Can't use '%s' as a block driver for the " + "protocol level", drv->format_name); ret = -EINVAL; goto free_and_fail; } bs->file = file; - ret = drv->bdrv_open(bs, options, open_flags); + ret = drv->bdrv_open(bs, options, open_flags, &local_err); } if (ret < 0) { + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } else if (filename) { + error_setg_errno(errp, -ret, "Could not open '%s'", filename); + } else { + error_setg_errno(errp, -ret, "Could not open image"); + } goto free_and_fail; } ret = refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); goto free_and_fail; } @@ -808,12 +847,13 @@ free_and_fail: * dictionary, it needs to use QINCREF() before calling bdrv_file_open. */ int bdrv_file_open(BlockDriverState **pbs, const char *filename, - QDict *options, int flags) + QDict *options, int flags, Error **errp) { BlockDriverState *bs; BlockDriver *drv; const char *drvname; bool allow_protocol_prefix = false; + Error *local_err = NULL; int ret; /* NULL means an empty set of options */ @@ -832,8 +872,8 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, qdict_put(options, "filename", qstring_from_str(filename)); allow_protocol_prefix = true; } else { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't specify 'file' and " - "'filename' options at the same time"); + error_setg(errp, "Can't specify 'file' and 'filename' options at the " + "same time"); ret = -EINVAL; goto fail; } @@ -842,53 +882,53 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, drvname = qdict_get_try_str(options, "driver"); if (drvname) { drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR)); + if (!drv) { + error_setg(errp, "Unknown driver '%s'", drvname); + } qdict_del(options, "driver"); } else if (filename) { drv = bdrv_find_protocol(filename, allow_protocol_prefix); if (!drv) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Unknown protocol"); + error_setg(errp, "Unknown protocol"); } } else { - qerror_report(ERROR_CLASS_GENERIC_ERROR, - "Must specify either driver or file"); + error_setg(errp, "Must specify either driver or file"); drv = NULL; } if (!drv) { + /* errp has been set already */ ret = -ENOENT; goto fail; } /* Parse the filename and open it */ if (drv->bdrv_parse_filename && filename) { - Error *local_err = NULL; drv->bdrv_parse_filename(filename, options, &local_err); if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); ret = -EINVAL; goto fail; } qdict_del(options, "filename"); - } else if (!drv->bdrv_parse_filename && !filename) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, - "The '%s' block driver requires a file name", - drv->format_name); + } else if (drv->bdrv_needs_filename && !filename) { + error_setg(errp, "The '%s' block driver requires a file name", + drv->format_name); ret = -EINVAL; goto fail; } - ret = bdrv_open_common(bs, NULL, options, flags, drv); + ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err); if (ret < 0) { + error_propagate(errp, local_err); goto fail; } /* Check if any unknown options were used */ if (qdict_size(options) != 0) { const QDictEntry *entry = qdict_first(options); - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block protocol '%s' doesn't " - "support the option '%s'", - drv->format_name, entry->key); + error_setg(errp, "Block protocol '%s' doesn't support the option '%s'", + drv->format_name, entry->key); ret = -EINVAL; goto fail; } @@ -915,11 +955,12 @@ fail: * function (even on failure), so if the caller intends to reuse the dictionary, * it needs to use QINCREF() before calling bdrv_file_open. */ -int bdrv_open_backing_file(BlockDriverState *bs, QDict *options) +int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) { char backing_filename[PATH_MAX]; int back_flags, ret; BlockDriver *back_drv = NULL; + Error *local_err = NULL; if (bs->backing_hd != NULL) { QDECREF(options); @@ -937,11 +978,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options) } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { QDECREF(options); return 0; + } else { + bdrv_get_full_backing_filename(bs, backing_filename, + sizeof(backing_filename)); } bs->backing_hd = bdrv_new(""); - bdrv_get_full_backing_filename(bs, backing_filename, - sizeof(backing_filename)); if (bs->backing_format[0] != '\0') { back_drv = bdrv_find_format(bs->backing_format); @@ -952,35 +994,19 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options) ret = bdrv_open(bs->backing_hd, *backing_filename ? backing_filename : NULL, options, - back_flags, back_drv); + back_flags, back_drv, &local_err); + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + bs->backing_hd->file->filename); if (ret < 0) { bdrv_unref(bs->backing_hd); bs->backing_hd = NULL; bs->open_flags |= BDRV_O_NO_BACKING; + error_propagate(errp, local_err); return ret; } return 0; } -static void extract_subqdict(QDict *src, QDict **dst, const char *start) -{ - const QDictEntry *entry, *next; - const char *p; - - *dst = qdict_new(); - entry = qdict_first(src); - - while (entry != NULL) { - next = qdict_next(src, entry); - if (strstart(entry->key, start, &p)) { - qobject_incref(entry->value); - qdict_put_obj(*dst, p, entry->value); - qdict_del(src, entry->key); - } - entry = next; - } -} - /* * Opens a disk image (raw, qcow2, vmdk, ...) * @@ -990,7 +1016,7 @@ static void extract_subqdict(QDict *src, QDict **dst, const char *start) * dictionary, it needs to use QINCREF() before calling bdrv_open. */ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, - int flags, BlockDriver *drv) + int flags, BlockDriver *drv, Error **errp) { int ret; /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ @@ -998,6 +1024,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, BlockDriverState *file = NULL; QDict *file_options = NULL; const char *drvname; + Error *local_err = NULL; /* NULL means an empty set of options */ if (options == NULL) { @@ -1016,7 +1043,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, char backing_filename[PATH_MAX]; if (qdict_size(options) != 0) { - error_report("Can't use snapshot=on with driver-specific options"); + error_setg(errp, "Can't use snapshot=on with driver-specific options"); ret = -EINVAL; goto fail; } @@ -1027,7 +1054,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* if there is a backing file, use it */ bs1 = bdrv_new(""); - ret = bdrv_open(bs1, filename, NULL, 0, drv); + ret = bdrv_open(bs1, filename, NULL, 0, drv, &local_err); if (ret < 0) { bdrv_unref(bs1); goto fail; @@ -1038,6 +1065,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not get temporary filename"); goto fail; } @@ -1046,6 +1074,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, snprintf(backing_filename, sizeof(backing_filename), "%s", filename); } else if (!realpath(filename, backing_filename)) { + error_setg_errno(errp, errno, "Could not resolve path '%s'", filename); ret = -errno; goto fail; } @@ -1062,9 +1091,14 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, drv->format_name); } - ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options); + ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err); free_option_parameters(create_options); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not create temporary overlay " + "'%s': %s", tmp_filename, + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; goto fail; } @@ -1078,10 +1112,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, flags |= BDRV_O_ALLOW_RDWR; } - extract_subqdict(options, &file_options, "file."); + qdict_extract_subqdict(options, &file_options, "file."); ret = bdrv_file_open(&file, filename, file_options, - bdrv_open_flags(bs, flags | BDRV_O_UNMAP)); + bdrv_open_flags(bs, flags | BDRV_O_UNMAP), &local_err); if (ret < 0) { goto fail; } @@ -1094,7 +1128,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, } if (!drv) { - ret = find_image_format(file, filename, &drv); + ret = find_image_format(file, filename, &drv, &local_err); } if (!drv) { @@ -1102,7 +1136,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, } /* Open the image */ - ret = bdrv_open_common(bs, file, options, flags, drv); + ret = bdrv_open_common(bs, file, options, flags, drv, &local_err); if (ret < 0) { goto unlink_and_fail; } @@ -1116,8 +1150,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, if ((flags & BDRV_O_NO_BACKING) == 0) { QDict *backing_options; - extract_subqdict(options, &backing_options, "backing."); - ret = bdrv_open_backing_file(bs, backing_options); + qdict_extract_subqdict(options, &backing_options, "backing."); + ret = bdrv_open_backing_file(bs, backing_options, &local_err); if (ret < 0) { goto close_and_fail; } @@ -1126,9 +1160,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* Check if any unknown options were used */ if (qdict_size(options) != 0) { const QDictEntry *entry = qdict_first(options); - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by " - "device '%s' doesn't support the option '%s'", - drv->format_name, bs->device_name, entry->key); + error_setg(errp, "Block format '%s' used by device '%s' doesn't " + "support the option '%s'", drv->format_name, bs->device_name, + entry->key); ret = -EINVAL; goto close_and_fail; @@ -1152,11 +1186,17 @@ fail: QDECREF(bs->options); QDECREF(options); bs->options = NULL; + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } return ret; close_and_fail: bdrv_close(bs); QDECREF(options); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } return ret; } @@ -2613,7 +2653,7 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, goto out; } - total_sectors = len >> BDRV_SECTOR_BITS; + total_sectors = DIV_ROUND_UP(len, BDRV_SECTOR_SIZE); max_nb_sectors = MAX(0, total_sectors - sector_num); if (max_nb_sectors > 0) { ret = drv->bdrv_co_readv(bs, sector_num, @@ -3103,13 +3143,14 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum); if (ret < 0) { + *pnum = 0; return ret; } if (!(ret & BDRV_BLOCK_DATA)) { if (bdrv_has_zero_init(bs)) { ret |= BDRV_BLOCK_ZERO; - } else { + } else if (bs->backing_hd) { BlockDriverState *bs2 = bs->backing_hd; int64_t length2 = bdrv_getlength(bs2); if (length2 >= 0 && sector_num >= (length2 >> BDRV_SECTOR_BITS)) { @@ -4433,6 +4474,7 @@ void bdrv_img_create(const char *filename, const char *fmt, BlockDriverState *bs = NULL; BlockDriver *drv, *proto_drv; BlockDriver *backing_drv = NULL; + Error *local_err = NULL; int ret = 0; /* Find driver and parse its options */ @@ -4519,10 +4561,13 @@ void bdrv_img_create(const char *filename, const char *fmt, bs = bdrv_new(""); ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags, - backing_drv); + backing_drv, &local_err); if (ret < 0) { - error_setg_errno(errp, -ret, "Could not open '%s'", - backing_file->value.s); + error_setg_errno(errp, -ret, "Could not open '%s': %s", + backing_file->value.s, + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; goto out; } bdrv_get_geometry(bs, &size); @@ -4541,22 +4586,19 @@ void bdrv_img_create(const char *filename, const char *fmt, print_option_parameters(param); puts(""); } - ret = bdrv_create(drv, filename, param); - if (ret < 0) { - if (ret == -ENOTSUP) { - error_setg(errp,"Formatting or formatting option not supported for " - "file format '%s'", fmt); - } else if (ret == -EFBIG) { - const char *cluster_size_hint = ""; - if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) { - cluster_size_hint = " (try using a larger cluster size)"; - } - error_setg(errp, "The image size is too large for file format '%s'%s", - fmt, cluster_size_hint); - } else { - error_setg(errp, "%s: error while creating %s: %s", filename, fmt, - strerror(-ret)); + ret = bdrv_create(drv, filename, param, &local_err); + if (ret == -EFBIG) { + /* This is generally a better message than whatever the driver would + * deliver (especially because of the cluster_size_hint), since that + * is most probably not much different from "image too large". */ + const char *cluster_size_hint = ""; + if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) { + cluster_size_hint = " (try using a larger cluster size)"; } + error_setg(errp, "The image size is too large for file format '%s'" + "%s", fmt, cluster_size_hint); + error_free(local_err); + local_err = NULL; } out: @@ -4566,6 +4608,9 @@ out: if (bs) { bdrv_unref(bs); } + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } } AioContext *bdrv_get_aio_context(BlockDriverState *bs) @@ -4579,3 +4624,11 @@ void bdrv_add_before_write_notifier(BlockDriverState *bs, { notifier_with_return_list_add(&bs->before_write_notifiers, notifier); } + +int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options) +{ + if (bs->drv->bdrv_amend_options == NULL) { + return -ENOTSUP; + } + return bs->drv->bdrv_amend_options(bs, options); +} diff --git a/block/blkdebug.c b/block/blkdebug.c index 5d33e03608..be948b2fdd 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -350,7 +350,8 @@ static QemuOptsList runtime_opts = { }, }; -static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags) +static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVBlkdebugState *s = bs->opaque; QemuOpts *opts; @@ -386,8 +387,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags) goto fail; } - ret = bdrv_file_open(&bs->file, filename, NULL, flags); + ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto fail; } diff --git a/block/blkverify.c b/block/blkverify.c index c4e961eeb1..bff95d2a45 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -116,7 +116,8 @@ static QemuOptsList runtime_opts = { }, }; -static int blkverify_open(BlockDriverState *bs, QDict *options, int flags) +static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVBlkverifyState *s = bs->opaque; QemuOpts *opts; @@ -140,8 +141,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags) goto fail; } - ret = bdrv_file_open(&bs->file, raw, NULL, flags); + ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto fail; } @@ -153,8 +156,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags) } s->test_file = bdrv_new(""); - ret = bdrv_open(s->test_file, filename, NULL, flags, NULL); + ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); bdrv_unref(s->test_file); s->test_file = NULL; goto fail; diff --git a/block/bochs.c b/block/bochs.c index d7078c0775..51d9a90577 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -108,7 +108,8 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int bochs_open(BlockDriverState *bs, QDict *options, int flags) +static int bochs_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVBochsState *s = bs->opaque; int i; diff --git a/block/cloop.c b/block/cloop.c index 6ea7cf4046..b907023e10 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -53,7 +53,8 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int cloop_open(BlockDriverState *bs, QDict *options, int flags) +static int cloop_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVCloopState *s = bs->opaque; uint32_t offsets_size, max_compressed_block_size = 1, i; diff --git a/block/cow.c b/block/cow.c index 764b93fae0..909c3e7182 100644 --- a/block/cow.c +++ b/block/cow.c @@ -58,7 +58,8 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int cow_open(BlockDriverState *bs, QDict *options, int flags) +static int cow_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVCowState *s = bs->opaque; struct cow_header_v2 cow_header; @@ -294,12 +295,14 @@ static void cow_close(BlockDriverState *bs) { } -static int cow_create(const char *filename, QEMUOptionParameter *options) +static int cow_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { struct cow_header_v2 cow_header; struct stat st; int64_t image_sectors = 0; const char *image_filename = NULL; + Error *local_err = NULL; int ret; BlockDriverState *cow_bs; @@ -313,13 +316,17 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) options++; } - ret = bdrv_create_file(filename, options); + ret = bdrv_create_file(filename, options, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } - ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR); + ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } diff --git a/block/curl.c b/block/curl.c index ca2cedcec1..5a46f9707c 100644 --- a/block/curl.c +++ b/block/curl.c @@ -395,7 +395,8 @@ static QemuOptsList runtime_opts = { }, }; -static int curl_open(BlockDriverState *bs, QDict *options, int flags) +static int curl_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVCURLState *s = bs->opaque; CURLState *state = NULL; diff --git a/block/dmg.c b/block/dmg.c index 3141cb5b88..d5e9b1ff01 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -92,7 +92,8 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) return 0; } -static int dmg_open(BlockDriverState *bs, QDict *options, int flags) +static int dmg_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVDMGState *s = bs->opaque; uint64_t info_begin,info_end,last_in_offset,last_out_offset; diff --git a/block/gluster.c b/block/gluster.c index dbb03f4de5..877686a7fe 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -288,7 +288,7 @@ static QemuOptsList runtime_opts = { }; static int qemu_gluster_open(BlockDriverState *bs, QDict *options, - int bdrv_flags) + int bdrv_flags, Error **errp) { BDRVGlusterState *s = bs->opaque; int open_flags = O_BINARY; @@ -357,7 +357,7 @@ out: } static int qemu_gluster_create(const char *filename, - QEMUOptionParameter *options) + QEMUOptionParameter *options, Error **errp) { struct glfs *glfs; struct glfs_fd *fd; @@ -611,6 +611,7 @@ static BlockDriver bdrv_gluster = { .format_name = "gluster", .protocol_name = "gluster", .instance_size = sizeof(BDRVGlusterState), + .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, @@ -631,6 +632,7 @@ static BlockDriver bdrv_gluster_tcp = { .format_name = "gluster", .protocol_name = "gluster+tcp", .instance_size = sizeof(BDRVGlusterState), + .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, @@ -651,6 +653,7 @@ static BlockDriver bdrv_gluster_unix = { .format_name = "gluster", .protocol_name = "gluster+unix", .instance_size = sizeof(BDRVGlusterState), + .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, @@ -671,6 +674,7 @@ static BlockDriver bdrv_gluster_rdma = { .format_name = "gluster", .protocol_name = "gluster+rdma", .instance_size = sizeof(BDRVGlusterState), + .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, diff --git a/block/iscsi.c b/block/iscsi.c index 813abd8fef..6152ef1891 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -33,6 +33,8 @@ #include "trace.h" #include "block/scsi.h" #include "qemu/iov.h" +#include "sysemu/sysemu.h" +#include "qmp-commands.h" #include <iscsi/iscsi.h> #include <iscsi/scsi-lowlevel.h> @@ -50,8 +52,21 @@ typedef struct IscsiLun { uint64_t num_blocks; int events; QEMUTimer *nop_timer; + uint8_t lbpme; + uint8_t lbprz; + struct scsi_inquiry_logical_block_provisioning lbp; + struct scsi_inquiry_block_limits bl; } IscsiLun; +typedef struct IscsiTask { + int status; + int complete; + int retries; + int do_retry; + struct scsi_task *task; + Coroutine *co; +} IscsiTask; + typedef struct IscsiAIOCB { BlockDriverAIOCB common; QEMUIOVector *qiov; @@ -72,6 +87,7 @@ typedef struct IscsiAIOCB { #define NOP_INTERVAL 5000 #define MAX_NOP_FAILURES 3 #define ISCSI_CMD_RETRIES 5 +#define ISCSI_MAX_UNMAP 131072 static void iscsi_bh_cb(void *p) @@ -105,6 +121,41 @@ iscsi_schedule_bh(IscsiAIOCB *acb) qemu_bh_schedule(acb->bh); } +static void +iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, + void *command_data, void *opaque) +{ + struct IscsiTask *iTask = opaque; + struct scsi_task *task = command_data; + + iTask->complete = 1; + iTask->status = status; + iTask->do_retry = 0; + iTask->task = task; + + if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { + iTask->do_retry = 1; + goto out; + } + + if (status != SCSI_STATUS_GOOD) { + error_report("iSCSI: Failure. %s", iscsi_get_error(iscsi)); + } + +out: + if (iTask->co) { + qemu_coroutine_enter(iTask->co, NULL); + } +} + +static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) +{ + *iTask = (struct IscsiTask) { + .co = qemu_coroutine_self(), + .retries = ISCSI_CMD_RETRIES, + }; +} static void iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, @@ -568,88 +619,6 @@ iscsi_aio_flush(BlockDriverState *bs, return &acb->common; } -static int iscsi_aio_discard_acb(IscsiAIOCB *acb); - -static void -iscsi_unmap_cb(struct iscsi_context *iscsi, int status, - void *command_data, void *opaque) -{ - IscsiAIOCB *acb = opaque; - - if (acb->canceled != 0) { - return; - } - - acb->status = 0; - if (status != 0) { - if (status == SCSI_STATUS_CHECK_CONDITION - && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION - && acb->retries-- > 0) { - scsi_free_scsi_task(acb->task); - acb->task = NULL; - if (iscsi_aio_discard_acb(acb) == 0) { - iscsi_set_events(acb->iscsilun); - return; - } - } - error_report("Failed to unmap data on iSCSI lun. %s", - iscsi_get_error(iscsi)); - acb->status = -EIO; - } - - iscsi_schedule_bh(acb); -} - -static int iscsi_aio_discard_acb(IscsiAIOCB *acb) { - struct iscsi_context *iscsi = acb->iscsilun->iscsi; - struct unmap_list list[1]; - - acb->canceled = 0; - acb->bh = NULL; - acb->status = -EINPROGRESS; - acb->buf = NULL; - - list[0].lba = sector_qemu2lun(acb->sector_num, acb->iscsilun); - list[0].num = acb->nb_sectors * BDRV_SECTOR_SIZE / acb->iscsilun->block_size; - - acb->task = iscsi_unmap_task(iscsi, acb->iscsilun->lun, - 0, 0, &list[0], 1, - iscsi_unmap_cb, - acb); - if (acb->task == NULL) { - error_report("iSCSI: Failed to send unmap command. %s", - iscsi_get_error(iscsi)); - return -1; - } - - return 0; -} - -static BlockDriverAIOCB * -iscsi_aio_discard(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque) -{ - IscsiLun *iscsilun = bs->opaque; - IscsiAIOCB *acb; - - acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); - - acb->iscsilun = iscsilun; - acb->nb_sectors = nb_sectors; - acb->sector_num = sector_num; - acb->retries = ISCSI_CMD_RETRIES; - - if (iscsi_aio_discard_acb(acb) != 0) { - qemu_aio_release(acb); - return NULL; - } - - iscsi_set_events(iscsilun); - - return &acb->common; -} - #ifdef __linux__ static void iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, @@ -842,6 +811,171 @@ iscsi_getlength(BlockDriverState *bs) return len; } +#if defined(SCSI_PROVISIONING_TYPE_DEALLOCATED) + +static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + IscsiLun *iscsilun = bs->opaque; + struct scsi_get_lba_status *lbas = NULL; + struct scsi_lba_status_descriptor *lbasd = NULL; + struct IscsiTask iTask; + int64_t ret; + + iscsi_co_init_iscsitask(iscsilun, &iTask); + + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + ret = -EINVAL; + goto out; + } + + /* default to all sectors allocated */ + ret = BDRV_BLOCK_DATA; + ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID; + *pnum = nb_sectors; + + /* LUN does not support logical block provisioning */ + if (iscsilun->lbpme == 0) { + goto out; + } + +retry: + if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun, + sector_qemu2lun(sector_num, iscsilun), + 8 + 16, iscsi_co_generic_cb, + &iTask) == NULL) { + ret = -EIO; + goto out; + } + + while (!iTask.complete) { + iscsi_set_events(iscsilun); + qemu_coroutine_yield(); + } + + if (iTask.do_retry) { + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; + } + goto retry; + } + + if (iTask.status != SCSI_STATUS_GOOD) { + /* in case the get_lba_status_callout fails (i.e. + * because the device is busy or the cmd is not + * supported) we pretend all blocks are allocated + * for backwards compatiblity */ + goto out; + } + + lbas = scsi_datain_unmarshall(iTask.task); + if (lbas == NULL) { + ret = -EIO; + goto out; + } + + lbasd = &lbas->descriptors[0]; + + if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) { + ret = -EIO; + goto out; + } + + *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun); + if (*pnum > nb_sectors) { + *pnum = nb_sectors; + } + + if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || + lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { + ret &= ~BDRV_BLOCK_DATA; + if (iscsilun->lbprz) { + ret |= BDRV_BLOCK_ZERO; + } + } + +out: + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + } + return ret; +} + +#endif /* SCSI_PROVISIONING_TYPE_DEALLOCATED */ + +static int +coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + IscsiLun *iscsilun = bs->opaque; + struct IscsiTask iTask; + struct unmap_list list; + uint32_t nb_blocks; + uint32_t max_unmap; + + if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { + return -EINVAL; + } + + if (!iscsilun->lbp.lbpu) { + /* UNMAP is not supported by the target */ + return 0; + } + + list.lba = sector_qemu2lun(sector_num, iscsilun); + nb_blocks = sector_qemu2lun(nb_sectors, iscsilun); + + max_unmap = iscsilun->bl.max_unmap; + if (max_unmap == 0xffffffff) { + max_unmap = ISCSI_MAX_UNMAP; + } + + while (nb_blocks > 0) { + iscsi_co_init_iscsitask(iscsilun, &iTask); + list.num = nb_blocks; + if (list.num > max_unmap) { + list.num = max_unmap; + } +retry: + if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1, + iscsi_co_generic_cb, &iTask) == NULL) { + return -EIO; + } + + while (!iTask.complete) { + iscsi_set_events(iscsilun); + qemu_coroutine_yield(); + } + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; + } + + if (iTask.do_retry) { + goto retry; + } + + if (iTask.status == SCSI_STATUS_CHECK_CONDITION) { + /* the target might fail with a check condition if it + is not happy with the alignment of the UNMAP request + we silently fail in this case */ + return 0; + } + + if (iTask.status != SCSI_STATUS_GOOD) { + return -EIO; + } + + list.lba += list.num; + nb_blocks -= list.num; + } + + return 0; +} + static int parse_chap(struct iscsi_context *iscsi, const char *target) { QemuOptsList *list; @@ -922,8 +1056,9 @@ static char *parse_initiator_name(const char *target) { QemuOptsList *list; QemuOpts *opts; - const char *name = NULL; - const char *iscsi_name = qemu_get_vm_name(); + const char *name; + char *iscsi_name; + UuidInfo *uuid_info; list = qemu_find_opts("iscsi"); if (list) { @@ -933,16 +1068,22 @@ static char *parse_initiator_name(const char *target) } if (opts) { name = qemu_opt_get(opts, "initiator-name"); + if (name) { + return g_strdup(name); + } } } - if (name) { - return g_strdup(name); + uuid_info = qmp_query_uuid(NULL); + if (strcmp(uuid_info->UUID, UUID_NONE) == 0) { + name = qemu_get_vm_name(); } else { - return g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s", - iscsi_name ? ":" : "", - iscsi_name ? iscsi_name : ""); + name = uuid_info->UUID; } + iscsi_name = g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s", + name ? ":" : "", name ? name : ""); + qapi_free_UuidInfo(uuid_info); + return iscsi_name; } #if defined(LIBISCSI_FEATURE_NOP_COUNTER) @@ -990,6 +1131,8 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun) } else { iscsilun->block_size = rc16->block_length; iscsilun->num_blocks = rc16->returned_lba + 1; + iscsilun->lbpme = rc16->lbpme; + iscsilun->lbprz = rc16->lbprz; } } break; @@ -1042,11 +1185,43 @@ static QemuOptsList runtime_opts = { }, }; +static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, + int lun, int evpd, int pc) { + int full_size; + struct scsi_task *task = NULL; + task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + goto fail; + } + full_size = scsi_datain_getfullsize(task); + if (full_size > task->datain.size) { + scsi_free_scsi_task(task); + + /* we need more data for the full list */ + task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + goto fail; + } + } + + return task; + +fail: + error_report("iSCSI: Inquiry command failed : %s", + iscsi_get_error(iscsi)); + if (task) { + scsi_free_scsi_task(task); + return NULL; + } + return NULL; +} + /* * We support iscsi url's on the form * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun> */ -static int iscsi_open(BlockDriverState *bs, QDict *options, int flags) +static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = NULL; @@ -1171,6 +1346,46 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags) bs->sg = 1; } + if (iscsilun->lbpme) { + struct scsi_inquiry_logical_block_provisioning *inq_lbp; + task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING); + if (task == NULL) { + ret = -EINVAL; + goto out; + } + inq_lbp = scsi_datain_unmarshall(task); + if (inq_lbp == NULL) { + error_report("iSCSI: failed to unmarshall inquiry datain blob"); + ret = -EINVAL; + goto out; + } + memcpy(&iscsilun->lbp, inq_lbp, + sizeof(struct scsi_inquiry_logical_block_provisioning)); + scsi_free_scsi_task(task); + task = NULL; + } + + if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) { + struct scsi_inquiry_block_limits *inq_bl; + task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS); + if (task == NULL) { + ret = -EINVAL; + goto out; + } + inq_bl = scsi_datain_unmarshall(task); + if (inq_bl == NULL) { + error_report("iSCSI: failed to unmarshall inquiry datain blob"); + ret = -EINVAL; + goto out; + } + memcpy(&iscsilun->bl, inq_bl, + sizeof(struct scsi_inquiry_block_limits)); + scsi_free_scsi_task(task); + task = NULL; + } + #if defined(LIBISCSI_FEATURE_NOP_COUNTER) /* Set up a timer for sending out iSCSI NOPs */ iscsilun->nop_timer = timer_new_ms(QEMU_CLOCK_REALTIME, iscsi_nop_timed_event, iscsilun); @@ -1237,7 +1452,8 @@ static int iscsi_has_zero_init(BlockDriverState *bs) return 0; } -static int iscsi_create(const char *filename, QEMUOptionParameter *options) +static int iscsi_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int ret = 0; int64_t total_size = 0; @@ -1260,7 +1476,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) bs_options = qdict_new(); qdict_put(bs_options, "filename", qstring_from_str(filename)); - ret = iscsi_open(bs, bs_options, 0); + ret = iscsi_open(bs, bs_options, 0, NULL); QDECREF(bs_options); if (ret != 0) { @@ -1304,6 +1520,7 @@ static BlockDriver bdrv_iscsi = { .protocol_name = "iscsi", .instance_size = sizeof(IscsiLun), + .bdrv_needs_filename = true, .bdrv_file_open = iscsi_open, .bdrv_close = iscsi_close, .bdrv_create = iscsi_create, @@ -1312,11 +1529,15 @@ static BlockDriver bdrv_iscsi = { .bdrv_getlength = iscsi_getlength, .bdrv_truncate = iscsi_truncate, +#if defined(SCSI_PROVISIONING_TYPE_DEALLOCATED) + .bdrv_co_get_block_status = iscsi_co_get_block_status, +#endif + .bdrv_co_discard = iscsi_co_discard, + .bdrv_aio_readv = iscsi_aio_readv, .bdrv_aio_writev = iscsi_aio_writev, .bdrv_aio_flush = iscsi_aio_flush, - .bdrv_aio_discard = iscsi_aio_discard, .bdrv_has_zero_init = iscsi_has_zero_init, #ifdef __linux__ diff --git a/block/mirror.c b/block/mirror.c index f61a7799de..6e7a274e43 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -505,14 +505,15 @@ static void mirror_iostatus_reset(BlockJob *job) static void mirror_complete(BlockJob *job, Error **errp) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + Error *local_err = NULL; int ret; - ret = bdrv_open_backing_file(s->target, NULL); + ret = bdrv_open_backing_file(s->target, NULL, &local_err); if (ret < 0) { char backing_filename[PATH_MAX]; bdrv_get_full_backing_filename(s->target, backing_filename, sizeof(backing_filename)); - error_setg_file_open(errp, -ret, backing_filename); + error_propagate(errp, local_err); return; } if (!s->synced) { diff --git a/block/nbd.c b/block/nbd.c index 691066f726..c8deeee67f 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -453,7 +453,8 @@ static void nbd_teardown_connection(BlockDriverState *bs) closesocket(s->sock); } -static int nbd_open(BlockDriverState *bs, QDict *options, int flags) +static int nbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVNBDState *s = bs->opaque; int result; diff --git a/block/parallels.c b/block/parallels.c index 18b3ac0b28..2121e43204 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -68,7 +68,8 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam return 0; } -static int parallels_open(BlockDriverState *bs, QDict *options, int flags) +static int parallels_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVParallelsState *s = bs->opaque; int i; diff --git a/block/qcow.c b/block/qcow.c index 93a993bb44..c470e05f60 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -92,7 +92,8 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int qcow_open(BlockDriverState *bs, QDict *options, int flags) +static int qcow_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVQcowState *s = bs->opaque; int len, i, shift, ret; @@ -658,7 +659,8 @@ static void qcow_close(BlockDriverState *bs) error_free(s->migration_blocker); } -static int qcow_create(const char *filename, QEMUOptionParameter *options) +static int qcow_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int header_size, backing_filename_len, l1_size, shift, i; QCowHeader header; @@ -666,6 +668,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) int64_t total_size = 0; const char *backing_file = NULL; int flags = 0; + Error *local_err = NULL; int ret; BlockDriverState *qcow_bs; @@ -681,13 +684,17 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) options++; } - ret = bdrv_create_file(filename, options); + ret = bdrv_create_file(filename, options, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } - ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR); + ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 7bcae09a69..40a5a3fc39 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -202,6 +202,24 @@ void qcow2_cache_depends_on_flush(Qcow2Cache *c) c->depends_on_flush = true; } +int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) +{ + int ret, i; + + ret = qcow2_cache_flush(bs, c); + if (ret < 0) { + return ret; + } + + for (i = 0; i < c->size; i++) { + assert(c->entries[i].ref == 0); + c->entries[i].offset = 0; + c->entries[i].cache_hits = 0; + } + + return 0; +} + static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c) { int i; diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 2d5aa92962..39323ace38 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -188,7 +188,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) { BDRVQcowState *s = bs->opaque; uint64_t old_l2_offset; - uint64_t *l2_table; + uint64_t *l2_table = NULL; int64_t l2_offset; int ret; @@ -200,7 +200,8 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); if (l2_offset < 0) { - return l2_offset; + ret = l2_offset; + goto fail; } ret = qcow2_cache_flush(bs, s->refcount_block_cache); @@ -213,7 +214,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) trace_qcow2_l2_allocate_get_empty(bs, l1_index); ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); if (ret < 0) { - return ret; + goto fail; } l2_table = *table; @@ -265,7 +266,9 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) fail: trace_qcow2_l2_allocate_done(bs, l1_index, ret); - qcow2_cache_put(bs, s->l2_table_cache, (void**) table); + if (l2_table != NULL) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) table); + } s->l1_table[l1_index] = old_l2_offset; return ret; } @@ -278,23 +281,26 @@ fail: * cluster which may require a different handling) */ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, - uint64_t *l2_table, uint64_t start, uint64_t stop_flags) + uint64_t *l2_table, uint64_t stop_flags) { int i; - uint64_t mask = stop_flags | L2E_OFFSET_MASK; - uint64_t offset = be64_to_cpu(l2_table[0]) & mask; + uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW2_CLUSTER_COMPRESSED; + uint64_t first_entry = be64_to_cpu(l2_table[0]); + uint64_t offset = first_entry & mask; if (!offset) return 0; - for (i = start; i < start + nb_clusters; i++) { + assert(qcow2_get_cluster_type(first_entry) != QCOW2_CLUSTER_COMPRESSED); + + for (i = 0; i < nb_clusters; i++) { uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask; if (offset + (uint64_t) i * cluster_size != l2_entry) { break; } } - return (i - start); + return i; } static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table) @@ -487,8 +493,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, return -EIO; } c = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, - QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO); + &l2_table[l2_index], QCOW_OFLAG_ZERO); *cluster_offset = 0; break; case QCOW2_CLUSTER_UNALLOCATED: @@ -499,8 +504,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, case QCOW2_CLUSTER_NORMAL: /* how many allocated clusters ? */ c = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, - QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO); + &l2_table[l2_index], QCOW_OFLAG_ZERO); *cluster_offset &= L2E_OFFSET_MASK; break; default: @@ -716,6 +720,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) } qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + assert(l2_index + m->nb_clusters <= s->l2_size); for (i = 0; i < m->nb_clusters; i++) { /* if two concurrent writes happen to the same unallocated cluster * each write allocates separate cluster and writes data concurrently. @@ -929,7 +934,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, /* We keep all QCOW_OFLAG_COPIED clusters */ keep_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, + &l2_table[l2_index], QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); assert(keep_clusters <= nb_clusters); @@ -1338,7 +1343,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) * clusters. */ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, - unsigned int nb_clusters) + unsigned int nb_clusters, enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table; @@ -1367,7 +1372,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, l2_table[l2_index + i] = cpu_to_be64(0); /* Then decrease the refcount */ - qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); + qcow2_free_any_clusters(bs, old_offset, 1, type); } ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); @@ -1379,7 +1384,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, } int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, - int nb_sectors) + int nb_sectors, enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; uint64_t end_offset; @@ -1402,7 +1407,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, /* Each L2 table is handled by its own loop iteration */ while (nb_clusters > 0) { - ret = discard_single_l2(bs, offset, nb_clusters); + ret = discard_single_l2(bs, offset, nb_clusters, type); if (ret < 0) { goto fail; } @@ -1497,3 +1502,256 @@ fail: return ret; } + +/* + * Expands all zero clusters in a specific L1 table (or deallocates them, for + * non-backed non-pre-allocated zero clusters). + * + * expanded_clusters is a bitmap where every bit corresponds to one cluster in + * the image file; a bit gets set if the corresponding cluster has been used for + * zero expansion (i.e., has been filled with zeroes and is referenced from an + * L2 table). nb_clusters contains the total cluster count of the image file, + * i.e., the number of bits in expanded_clusters. + */ +static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, + int l1_size, uint8_t **expanded_clusters, + uint64_t *nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + bool is_active_l1 = (l1_table == s->l1_table); + uint64_t *l2_table = NULL; + int ret; + int i, j; + + if (!is_active_l1) { + /* inactive L2 tables require a buffer to be stored in when loading + * them from disk */ + l2_table = qemu_blockalign(bs, s->cluster_size); + } + + for (i = 0; i < l1_size; i++) { + uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; + bool l2_dirty = false; + + if (!l2_offset) { + /* unallocated */ + continue; + } + + if (is_active_l1) { + /* get active L2 tables from cache */ + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, + (void **)&l2_table); + } else { + /* load inactive L2 tables from disk */ + ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, + (void *)l2_table, s->cluster_sectors); + } + if (ret < 0) { + goto fail; + } + + for (j = 0; j < s->l2_size; j++) { + uint64_t l2_entry = be64_to_cpu(l2_table[j]); + int64_t offset = l2_entry & L2E_OFFSET_MASK, cluster_index; + int cluster_type = qcow2_get_cluster_type(l2_entry); + bool preallocated = offset != 0; + + if (cluster_type == QCOW2_CLUSTER_NORMAL) { + cluster_index = offset >> s->cluster_bits; + assert((cluster_index >= 0) && (cluster_index < *nb_clusters)); + if ((*expanded_clusters)[cluster_index / 8] & + (1 << (cluster_index % 8))) { + /* Probably a shared L2 table; this cluster was a zero + * cluster which has been expanded, its refcount + * therefore most likely requires an update. */ + ret = qcow2_update_cluster_refcount(bs, cluster_index, 1, + QCOW2_DISCARD_NEVER); + if (ret < 0) { + goto fail; + } + /* Since we just increased the refcount, the COPIED flag may + * no longer be set. */ + l2_table[j] = cpu_to_be64(l2_entry & ~QCOW_OFLAG_COPIED); + l2_dirty = true; + } + continue; + } + else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) { + continue; + } + + if (!preallocated) { + if (!bs->backing_hd) { + /* not backed; therefore we can simply deallocate the + * cluster */ + l2_table[j] = 0; + l2_dirty = true; + continue; + } + + offset = qcow2_alloc_clusters(bs, s->cluster_size); + if (offset < 0) { + ret = offset; + goto fail; + } + } + + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, + offset, s->cluster_size); + if (ret < 0) { + if (!preallocated) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + goto fail; + } + + ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE, + s->cluster_sectors); + if (ret < 0) { + if (!preallocated) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + goto fail; + } + + l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); + l2_dirty = true; + + cluster_index = offset >> s->cluster_bits; + + if (cluster_index >= *nb_clusters) { + uint64_t old_bitmap_size = (*nb_clusters + 7) / 8; + uint64_t new_bitmap_size; + /* The offset may lie beyond the old end of the underlying image + * file for growable files only */ + assert(bs->file->growable); + *nb_clusters = size_to_clusters(s, bs->file->total_sectors * + BDRV_SECTOR_SIZE); + new_bitmap_size = (*nb_clusters + 7) / 8; + *expanded_clusters = g_realloc(*expanded_clusters, + new_bitmap_size); + /* clear the newly allocated space */ + memset(&(*expanded_clusters)[old_bitmap_size], 0, + new_bitmap_size - old_bitmap_size); + } + + assert((cluster_index >= 0) && (cluster_index < *nb_clusters)); + (*expanded_clusters)[cluster_index / 8] |= 1 << (cluster_index % 8); + } + + if (is_active_l1) { + if (l2_dirty) { + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + qcow2_cache_depends_on_flush(s->l2_table_cache); + } + ret = qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); + if (ret < 0) { + l2_table = NULL; + goto fail; + } + } else { + if (l2_dirty) { + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT & + ~(QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2), l2_offset, + s->cluster_size); + if (ret < 0) { + goto fail; + } + + ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, + (void *)l2_table, s->cluster_sectors); + if (ret < 0) { + goto fail; + } + } + } + } + + ret = 0; + +fail: + if (l2_table) { + if (!is_active_l1) { + qemu_vfree(l2_table); + } else { + if (ret < 0) { + qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); + } else { + ret = qcow2_cache_put(bs, s->l2_table_cache, + (void **)&l2_table); + } + } + } + return ret; +} + +/* + * For backed images, expands all zero clusters on the image. For non-backed + * images, deallocates all non-pre-allocated zero clusters (and claims the + * allocation for pre-allocated ones). This is important for downgrading to a + * qcow2 version which doesn't yet support metadata zero clusters. + */ +int qcow2_expand_zero_clusters(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l1_table = NULL; + uint64_t nb_clusters; + uint8_t *expanded_clusters; + int ret; + int i, j; + + nb_clusters = size_to_clusters(s, bs->file->total_sectors * + BDRV_SECTOR_SIZE); + expanded_clusters = g_malloc0((nb_clusters + 7) / 8); + + ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, + &expanded_clusters, &nb_clusters); + if (ret < 0) { + goto fail; + } + + /* Inactive L1 tables may point to active L2 tables - therefore it is + * necessary to flush the L2 table cache before trying to access the L2 + * tables pointed to by inactive L1 entries (else we might try to expand + * zero clusters that have already been expanded); furthermore, it is also + * necessary to empty the L2 table cache, since it may contain tables which + * are now going to be modified directly on disk, bypassing the cache. + * qcow2_cache_empty() does both for us. */ + ret = qcow2_cache_empty(bs, s->l2_table_cache); + if (ret < 0) { + goto fail; + } + + for (i = 0; i < s->nb_snapshots; i++) { + int l1_sectors = (s->snapshots[i].l1_size * sizeof(uint64_t) + + BDRV_SECTOR_SIZE - 1) / BDRV_SECTOR_SIZE; + + l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); + + ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset / + BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors); + if (ret < 0) { + goto fail; + } + + for (j = 0; j < s->snapshots[i].l1_size; j++) { + be64_to_cpus(&l1_table[j]); + } + + ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size, + &expanded_clusters, &nb_clusters); + if (ret < 0) { + goto fail; + } + } + + ret = 0; + +fail: + g_free(expanded_clusters); + g_free(l1_table); + return ret; +} diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index ba129de478..d2b7064a02 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -601,10 +601,10 @@ fail: * If the return value is non-negative, it is the new refcount of the cluster. * If it is negative, it is -errno and indicates an error. */ -static int update_cluster_refcount(BlockDriverState *bs, - int64_t cluster_index, - int addend, - enum qcow2_discard_type type) +int qcow2_update_cluster_refcount(BlockDriverState *bs, + int64_t cluster_index, + int addend, + enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; int ret; @@ -733,8 +733,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) if (free_in_cluster == 0) s->free_byte_offset = 0; if ((offset & (s->cluster_size - 1)) != 0) - update_cluster_refcount(bs, offset >> s->cluster_bits, 1, - QCOW2_DISCARD_NEVER); + qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1, + QCOW2_DISCARD_NEVER); } else { offset = qcow2_alloc_clusters(bs, s->cluster_size); if (offset < 0) { @@ -744,8 +744,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) if ((cluster_offset + s->cluster_size) == offset) { /* we are lucky: contiguous data */ offset = s->free_byte_offset; - update_cluster_refcount(bs, offset >> s->cluster_bits, 1, - QCOW2_DISCARD_NEVER); + qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1, + QCOW2_DISCARD_NEVER); s->free_byte_offset += size; } else { s->free_byte_offset = offset; @@ -754,8 +754,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) } /* The cluster refcount was incremented, either by qcow2_alloc_clusters() - * or explicitly by update_cluster_refcount(). Refcount blocks must be - * flushed before the caller's L2 table updates. + * or explicitly by qcow2_update_cluster_refcount(). Refcount blocks must + * be flushed before the caller's L2 table updates. */ qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); return offset; @@ -874,7 +874,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, nb_csectors = ((offset >> s->csize_shift) & s->csize_mask) + 1; if (addend != 0) { - int ret; ret = update_refcount(bs, (offset & s->cluster_offset_mask) & ~511, nb_csectors * 512, addend, @@ -896,8 +895,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, break; } if (addend != 0) { - refcount = update_cluster_refcount(bs, cluster_index, addend, - QCOW2_DISCARD_SNAPSHOT); + refcount = qcow2_update_cluster_refcount(bs, + cluster_index, addend, + QCOW2_DISCARD_SNAPSHOT); } else { refcount = get_refcount(bs, cluster_index); } @@ -936,8 +936,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (addend != 0) { - refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend, - QCOW2_DISCARD_SNAPSHOT); + refcount = qcow2_update_cluster_refcount(bs, l2_offset >> + s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT); } else { refcount = get_refcount(bs, l2_offset >> s->cluster_bits); } diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index e7e601301a..5e8a7794f4 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -192,7 +192,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) /* The snapshot list position has not yet been updated, so these clusters * must indeed be completely free */ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, offset, - s->snapshots_size); + snapshots_size); if (ret < 0) { return ret; } @@ -297,31 +297,47 @@ static void find_new_snapshot_id(BlockDriverState *bs, snprintf(id_str, id_str_size, "%d", id_max + 1); } -static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str) +static int find_snapshot_by_id_and_name(BlockDriverState *bs, + const char *id, + const char *name) { BDRVQcowState *s = bs->opaque; int i; - for(i = 0; i < s->nb_snapshots; i++) { - if (!strcmp(s->snapshots[i].id_str, id_str)) - return i; + if (id && name) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].id_str, id) && + !strcmp(s->snapshots[i].name, name)) { + return i; + } + } + } else if (id) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].id_str, id)) { + return i; + } + } + } else if (name) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].name, name)) { + return i; + } + } } + return -1; } -static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name) +static int find_snapshot_by_id_or_name(BlockDriverState *bs, + const char *id_or_name) { - BDRVQcowState *s = bs->opaque; - int i, ret; + int ret; - ret = find_snapshot_by_id(bs, name); - if (ret >= 0) + ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL); + if (ret >= 0) { return ret; - for(i = 0; i < s->nb_snapshots; i++) { - if (!strcmp(s->snapshots[i].name, name)) - return i; } - return -1; + return find_snapshot_by_id_and_name(bs, NULL, id_or_name); } /* if no id is provided, a new one is constructed */ @@ -343,7 +359,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) } /* Check that the ID is unique */ - if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) { + if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) { return -EEXIST; } @@ -416,6 +432,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) g_free(old_snapshot_list); + /* The VM state isn't needed any more in the active L1 table; in fact, it + * hurts by causing expensive COW for the next snapshot. */ + qcow2_discard_clusters(bs, qcow2_vm_state_offset(s), + align_offset(sn->vm_state_size, s->cluster_size) + >> BDRV_SECTOR_BITS, + QCOW2_DISCARD_NEVER); + #ifdef DEBUG_ALLOC { BdrvCheckResult result = {0}; @@ -553,15 +576,19 @@ fail: return ret; } -int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +int qcow2_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) { BDRVQcowState *s = bs->opaque; QCowSnapshot sn; int snapshot_index, ret; /* Search the snapshot */ - snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); + snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); if (snapshot_index < 0) { + error_setg(errp, "Can't find the snapshot"); return -ENOENT; } sn = s->snapshots[snapshot_index]; @@ -573,6 +600,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) s->nb_snapshots--; ret = qcow2_write_snapshots(bs); if (ret < 0) { + error_setg(errp, "Failed to remove snapshot from snapshot list"); return ret; } @@ -590,6 +618,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset, sn.l1_size, -1); if (ret < 0) { + error_setg(errp, "Failed to free the cluster and L1 table"); return ret; } qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t), @@ -598,6 +627,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) /* must update the copied flag on the current cluster offsets */ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); if (ret < 0) { + error_setg(errp, "Failed to update snapshot status in disk"); return ret; } diff --git a/block/qcow2.c b/block/qcow2.c index 578792f0a3..4a9888cc7f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -52,7 +52,7 @@ typedef struct { uint32_t magic; uint32_t len; -} QCowExtension; +} QEMU_PACKED QCowExtension; #define QCOW2_EXT_MAGIC_END 0 #define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA @@ -79,7 +79,8 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) * return 0 upon success, non-0 otherwise */ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, - uint64_t end_offset, void **p_feature_table) + uint64_t end_offset, void **p_feature_table, + Error **errp) { BDRVQcowState *s = bs->opaque; QCowExtension ext; @@ -100,10 +101,10 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, printf("attempting to read extended header in offset %lu\n", offset); #endif - if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) { - fprintf(stderr, "qcow2_read_extension: ERROR: " - "pread fail from offset %" PRIu64 "\n", - offset); + ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext)); + if (ret < 0) { + error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " + "pread fail from offset %" PRIu64, offset); return 1; } be32_to_cpus(&ext.magic); @@ -113,7 +114,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, printf("ext.magic = 0x%x\n", ext.magic); #endif if (ext.len > end_offset - offset) { - error_report("Header extension too large"); + error_setg(errp, "Header extension too large"); return -EINVAL; } @@ -123,14 +124,16 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_BACKING_FORMAT: if (ext.len >= sizeof(bs->backing_format)) { - fprintf(stderr, "ERROR: ext_backing_format: len=%u too large" - " (>=%zu)\n", - ext.len, sizeof(bs->backing_format)); + error_setg(errp, "ERROR: ext_backing_format: len=%u too large" + " (>=%zu)", ext.len, sizeof(bs->backing_format)); return 2; } - if (bdrv_pread(bs->file, offset , bs->backing_format, - ext.len) != ext.len) + ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " + "Could not read format name"); return 3; + } bs->backing_format[ext.len] = '\0'; #ifdef DEBUG_EXT printf("Qcow2: Got format extension %s\n", bs->backing_format); @@ -142,6 +145,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); ret = bdrv_pread(bs->file, offset , feature_table, ext.len); if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " + "Could not read table"); return ret; } @@ -161,6 +166,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, ret = bdrv_pread(bs->file, offset , uext->data, uext->len); if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: unknown extension: " + "Could not read data"); return ret; } } @@ -184,8 +191,8 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs) } } -static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs, - const char *fmt, ...) +static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs, + Error **errp, const char *fmt, ...) { char msg[64]; va_list ap; @@ -194,17 +201,17 @@ static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs, vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); - qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bs->device_name, "qcow2", msg); + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "qcow2", + msg); } static void report_unsupported_feature(BlockDriverState *bs, - Qcow2Feature *table, uint64_t mask) + Error **errp, Qcow2Feature *table, uint64_t mask) { while (table && table->name[0] != '\0') { if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { if (mask & (1 << table->bit)) { - report_unsupported(bs, "%.46s",table->name); + report_unsupported(bs, errp, "%.46s", table->name); mask &= ~(1 << table->bit); } } @@ -212,7 +219,8 @@ static void report_unsupported_feature(BlockDriverState *bs, } if (mask) { - report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask); + report_unsupported(bs, errp, "Unknown incompatible feature: %" PRIx64, + mask); } } @@ -350,7 +358,8 @@ static QemuOptsList qcow2_runtime_opts = { }, }; -static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) +static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVQcowState *s = bs->opaque; int len, i, ret = 0; @@ -362,6 +371,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read qcow2 header"); goto fail; } be32_to_cpus(&header.magic); @@ -379,11 +389,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) be32_to_cpus(&header.nb_snapshots); if (header.magic != QCOW_MAGIC) { + error_setg(errp, "Image is not in qcow2 format"); ret = -EMEDIUMTYPE; goto fail; } if (header.version < 2 || header.version > 3) { - report_unsupported(bs, "QCOW version %d", header.version); + report_unsupported(bs, errp, "QCOW version %d", header.version); ret = -ENOTSUP; goto fail; } @@ -411,6 +422,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, s->unknown_header_fields_size); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read unknown qcow2 header " + "fields"); goto fail; } } @@ -429,8 +442,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) { void *feature_table = NULL; qcow2_read_extensions(bs, header.header_length, ext_end, - &feature_table); - report_unsupported_feature(bs, feature_table, + &feature_table, NULL); + report_unsupported_feature(bs, errp, feature_table, s->incompatible_features & ~QCOW2_INCOMPAT_MASK); ret = -ENOTSUP; @@ -441,8 +454,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) /* Corrupt images may not be written to unless they are being repaired */ if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { - error_report("qcow2: Image is corrupt; cannot be opened " - "read/write."); + error_setg(errp, "qcow2: Image is corrupt; cannot be opened " + "read/write"); ret = -EACCES; goto fail; } @@ -450,18 +463,22 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) /* Check support for various header values */ if (header.refcount_order != 4) { - report_unsupported(bs, "%d bit reference counts", + report_unsupported(bs, errp, "%d bit reference counts", 1 << header.refcount_order); ret = -ENOTSUP; goto fail; } + s->refcount_order = header.refcount_order; if (header.cluster_bits < MIN_CLUSTER_BITS || header.cluster_bits > MAX_CLUSTER_BITS) { + error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits); ret = -EINVAL; goto fail; } if (header.crypt_method > QCOW_CRYPT_AES) { + error_setg(errp, "Unsupported encryption method: %i", + header.crypt_method); ret = -EINVAL; goto fail; } @@ -490,6 +507,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) l1_vm_state_index = size_to_l1(s, header.size); if (l1_vm_state_index > INT_MAX) { + error_setg(errp, "Image is too big"); ret = -EFBIG; goto fail; } @@ -498,6 +516,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) /* the L1 table must contain at least enough entries to put header.size bytes */ if (s->l1_size < s->l1_vm_state_index) { + error_setg(errp, "L1 table is too small"); ret = -EINVAL; goto fail; } @@ -508,6 +527,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read L1 table"); goto fail; } for(i = 0;i < s->l1_size; i++) { @@ -528,6 +548,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) ret = qcow2_refcount_init(bs); if (ret != 0) { + error_setg_errno(errp, -ret, "Could not initialize refcount handling"); goto fail; } @@ -535,7 +556,9 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) QTAILQ_INIT(&s->discards); /* read qcow2 extensions */ - if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) { + if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL, + &local_err)) { + error_propagate(errp, local_err); ret = -EINVAL; goto fail; } @@ -549,6 +572,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) ret = bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read backing file name"); goto fail; } bs->backing_file[len] = '\0'; @@ -556,6 +580,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) ret = qcow2_read_snapshots(bs); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read snapshots"); goto fail; } @@ -564,6 +589,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) s->autoclear_features = 0; ret = qcow2_update_header(bs); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not update qcow2 header"); goto fail; } } @@ -578,6 +604,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not repair dirty image"); goto fail; } } @@ -586,8 +613,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) opts = qemu_opts_create_nofail(&qcow2_runtime_opts); qemu_opts_absorb_qdict(opts, options, &local_err); if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); ret = -EINVAL; goto fail; } @@ -608,8 +634,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) qemu_opts_del(opts); if (s->use_lazy_refcounts && s->qcow_version < 3) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Lazy refcounts require " - "a qcow2 image with at least qemu 1.1 compatibility level"); + error_setg(errp, "Lazy refcounts require a qcow2 image with at least " + "qemu 1.1 compatibility level"); ret = -EINVAL; goto fail; } @@ -1059,7 +1085,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs) qbool_from_int(s->use_lazy_refcounts)); memset(s, 0, sizeof(BDRVQcowState)); - qcow2_open(bs, options, flags); + qcow2_open(bs, options, flags, NULL); QDECREF(options); @@ -1143,7 +1169,7 @@ int qcow2_update_header(BlockDriverState *bs) .incompatible_features = cpu_to_be64(s->incompatible_features), .compatible_features = cpu_to_be64(s->compatible_features), .autoclear_features = cpu_to_be64(s->autoclear_features), - .refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT), + .refcount_order = cpu_to_be32(s->refcount_order), .header_length = cpu_to_be32(header_length), }; @@ -1332,7 +1358,8 @@ static int preallocate(BlockDriverState *bs) static int qcow2_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, int flags, size_t cluster_size, int prealloc, - QEMUOptionParameter *options, int version) + QEMUOptionParameter *options, int version, + Error **errp) { /* Calculate cluster_bits */ int cluster_bits; @@ -1340,9 +1367,8 @@ static int qcow2_create2(const char *filename, int64_t total_size, if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || (1 << cluster_bits) != cluster_size) { - error_report( - "Cluster size must be a power of two between %d and %dk", - 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); + error_setg(errp, "Cluster size must be a power of two between %d and " + "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); return -EINVAL; } @@ -1361,15 +1387,18 @@ static int qcow2_create2(const char *filename, int64_t total_size, BlockDriverState* bs; QCowHeader header; uint8_t* refcount_table; + Error *local_err = NULL; int ret; - ret = bdrv_create_file(filename, options); + ret = bdrv_create_file(filename, options, &local_err); if (ret < 0) { + error_propagate(errp, local_err); return ret; } - ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR); + ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err); if (ret < 0) { + error_propagate(errp, local_err); return ret; } @@ -1399,6 +1428,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, ret = bdrv_pwrite(bs, 0, &header, sizeof(header)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write qcow2 header"); goto out; } @@ -1408,6 +1438,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, g_free(refcount_table); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write refcount table"); goto out; } @@ -1421,13 +1452,16 @@ static int qcow2_create2(const char *filename, int64_t total_size, BlockDriver* drv = bdrv_find_format("qcow2"); assert(drv != NULL); ret = bdrv_open(bs, filename, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv); + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err); if (ret < 0) { + error_propagate(errp, local_err); goto out; } ret = qcow2_alloc_clusters(bs, 2 * cluster_size); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 " + "header and refcount table"); goto out; } else if (ret != 0) { @@ -1438,6 +1472,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Okay, now that we have a valid image, let's give it the right size */ ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not resize image"); goto out; } @@ -1445,6 +1480,8 @@ static int qcow2_create2(const char *filename, int64_t total_size, if (backing_file) { ret = bdrv_change_backing_file(bs, backing_file, backing_format); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not assign backing file '%s' " + "with format '%s'", backing_file, backing_format); goto out; } } @@ -1456,6 +1493,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, ret = preallocate(bs); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not preallocate metadata"); goto out; } } @@ -1466,7 +1504,8 @@ out: return ret; } -static int qcow2_create(const char *filename, QEMUOptionParameter *options) +static int qcow2_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { const char *backing_file = NULL; const char *backing_fmt = NULL; @@ -1475,6 +1514,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) size_t cluster_size = DEFAULT_CLUSTER_SIZE; int prealloc = 0; int version = 3; + Error *local_err = NULL; + int ret; /* Read out options */ while (options && options->name) { @@ -1496,8 +1537,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) } else if (!strcmp(options->value.s, "metadata")) { prealloc = 1; } else { - fprintf(stderr, "Invalid preallocation mode: '%s'\n", - options->value.s); + error_setg(errp, "Invalid preallocation mode: '%s'", + options->value.s); return -EINVAL; } } else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) { @@ -1508,8 +1549,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) } else if (!strcmp(options->value.s, "1.1")) { version = 3; } else { - fprintf(stderr, "Invalid compatibility level: '%s'\n", - options->value.s); + error_setg(errp, "Invalid compatibility level: '%s'", + options->value.s); return -EINVAL; } } else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) { @@ -1519,19 +1560,23 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) } if (backing_file && prealloc) { - fprintf(stderr, "Backing file and preallocation cannot be used at " - "the same time\n"); + error_setg(errp, "Backing file and preallocation cannot be used at " + "the same time"); return -EINVAL; } if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) { - fprintf(stderr, "Lazy refcounts only supported with compatibility " - "level 1.1 and above (use compat=1.1 or greater)\n"); + error_setg(errp, "Lazy refcounts only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)"); return -EINVAL; } - return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, - cluster_size, prealloc, options, version); + ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, + cluster_size, prealloc, options, version, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } + return ret; } static int qcow2_make_empty(BlockDriverState *bs) @@ -1582,7 +1627,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, qemu_co_mutex_lock(&s->lock); ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, - nb_sectors); + nb_sectors, QCOW2_DISCARD_REQUEST); qemu_co_mutex_unlock(&s->lock); return ret; } @@ -1757,11 +1802,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) return 0; } -static int64_t qcow2_vm_state_offset(BDRVQcowState *s) -{ - return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); -} - static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcowState *s = bs->opaque; @@ -1824,6 +1864,199 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, return ret; } +/* + * Downgrades an image's version. To achieve this, any incompatible features + * have to be removed. + */ +static int qcow2_downgrade(BlockDriverState *bs, int target_version) +{ + BDRVQcowState *s = bs->opaque; + int current_version = s->qcow_version; + int ret; + + if (target_version == current_version) { + return 0; + } else if (target_version > current_version) { + return -EINVAL; + } else if (target_version != 2) { + return -EINVAL; + } + + if (s->refcount_order != 4) { + /* we would have to convert the image to a refcount_order == 4 image + * here; however, since qemu (at the time of writing this) does not + * support anything different than 4 anyway, there is no point in doing + * so right now; however, we should error out (if qemu supports this in + * the future and this code has not been adapted) */ + error_report("qcow2_downgrade: Image refcount orders other than 4 are" + "currently not supported."); + return -ENOTSUP; + } + + /* clear incompatible features */ + if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { + ret = qcow2_mark_clean(bs); + if (ret < 0) { + return ret; + } + } + + /* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in + * the first place; if that happens nonetheless, returning -ENOTSUP is the + * best thing to do anyway */ + + if (s->incompatible_features) { + return -ENOTSUP; + } + + /* since we can ignore compatible features, we can set them to 0 as well */ + s->compatible_features = 0; + /* if lazy refcounts have been used, they have already been fixed through + * clearing the dirty flag */ + + /* clearing autoclear features is trivial */ + s->autoclear_features = 0; + + ret = qcow2_expand_zero_clusters(bs); + if (ret < 0) { + return ret; + } + + s->qcow_version = target_version; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->qcow_version = current_version; + return ret; + } + return 0; +} + +static int qcow2_amend_options(BlockDriverState *bs, + QEMUOptionParameter *options) +{ + BDRVQcowState *s = bs->opaque; + int old_version = s->qcow_version, new_version = old_version; + uint64_t new_size = 0; + const char *backing_file = NULL, *backing_format = NULL; + bool lazy_refcounts = s->use_lazy_refcounts; + int ret; + int i; + + for (i = 0; options[i].name; i++) + { + if (!options[i].assigned) { + /* only change explicitly defined options */ + continue; + } + + if (!strcmp(options[i].name, "compat")) { + if (!options[i].value.s) { + /* preserve default */ + } else if (!strcmp(options[i].value.s, "0.10")) { + new_version = 2; + } else if (!strcmp(options[i].value.s, "1.1")) { + new_version = 3; + } else { + fprintf(stderr, "Unknown compatibility level %s.\n", + options[i].value.s); + return -EINVAL; + } + } else if (!strcmp(options[i].name, "preallocation")) { + fprintf(stderr, "Cannot change preallocation mode.\n"); + return -ENOTSUP; + } else if (!strcmp(options[i].name, "size")) { + new_size = options[i].value.n; + } else if (!strcmp(options[i].name, "backing_file")) { + backing_file = options[i].value.s; + } else if (!strcmp(options[i].name, "backing_fmt")) { + backing_format = options[i].value.s; + } else if (!strcmp(options[i].name, "encryption")) { + if ((options[i].value.n != !!s->crypt_method)) { + fprintf(stderr, "Changing the encryption flag is not " + "supported.\n"); + return -ENOTSUP; + } + } else if (!strcmp(options[i].name, "cluster_size")) { + if (options[i].value.n != s->cluster_size) { + fprintf(stderr, "Changing the cluster size is not " + "supported.\n"); + return -ENOTSUP; + } + } else if (!strcmp(options[i].name, "lazy_refcounts")) { + lazy_refcounts = options[i].value.n; + } else { + /* if this assertion fails, this probably means a new option was + * added without having it covered here */ + assert(false); + } + } + + if (new_version != old_version) { + if (new_version > old_version) { + /* Upgrade */ + s->qcow_version = new_version; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->qcow_version = old_version; + return ret; + } + } else { + ret = qcow2_downgrade(bs, new_version); + if (ret < 0) { + return ret; + } + } + } + + if (backing_file || backing_format) { + ret = qcow2_change_backing_file(bs, backing_file ?: bs->backing_file, + backing_format ?: bs->backing_format); + if (ret < 0) { + return ret; + } + } + + if (s->use_lazy_refcounts != lazy_refcounts) { + if (lazy_refcounts) { + if (s->qcow_version < 3) { + fprintf(stderr, "Lazy refcounts only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)\n"); + return -EINVAL; + } + s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; + return ret; + } + s->use_lazy_refcounts = true; + } else { + /* make image clean first */ + ret = qcow2_mark_clean(bs); + if (ret < 0) { + return ret; + } + /* now disallow lazy refcounts */ + s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; + return ret; + } + s->use_lazy_refcounts = false; + } + } + + if (new_size) { + ret = bdrv_truncate(bs, new_size); + if (ret < 0) { + return ret; + } + } + + return 0; +} + static QEMUOptionParameter qcow2_create_options[] = { { .name = BLOCK_OPT_SIZE, @@ -1907,6 +2140,7 @@ static BlockDriver bdrv_qcow2 = { .create_options = qcow2_create_options, .bdrv_check = qcow2_check, + .bdrv_amend_options = qcow2_amend_options, }; static void bdrv_qcow2_init(void) diff --git a/block/qcow2.h b/block/qcow2.h index 1000239e4c..455e38de64 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -86,7 +86,7 @@ typedef struct QCowHeader { uint32_t refcount_order; uint32_t header_length; -} QCowHeader; +} QEMU_PACKED QCowHeader; typedef struct QCowSnapshot { uint64_t l1_table_offset; @@ -199,6 +199,7 @@ typedef struct BDRVQcowState { int flags; int qcow_version; bool use_lazy_refcounts; + int refcount_order; bool discard_passthrough[QCOW2_DISCARD_MAX]; @@ -361,6 +362,11 @@ static inline int64_t align_offset(int64_t offset, int n) return offset; } +static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s) +{ + return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); +} + static inline int qcow2_get_cluster_type(uint64_t l2_entry) { if (l2_entry & QCOW_OFLAG_COMPRESSED) { @@ -406,6 +412,9 @@ int qcow2_update_header(BlockDriverState *bs); int qcow2_refcount_init(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs); +int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, + int addend, enum qcow2_discard_type type); + int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size); int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, int nb_clusters); @@ -450,13 +459,18 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, - int nb_sectors); + int nb_sectors, enum qcow2_discard_type type); int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); +int qcow2_expand_zero_clusters(BlockDriverState *bs); + /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); -int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); +int qcow2_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp); int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name); @@ -473,6 +487,8 @@ int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, Qcow2Cache *dependency); void qcow2_cache_depends_on_flush(Qcow2Cache *c); +int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); + int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, void **table); int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, diff --git a/block/qed.c b/block/qed.c index 49b3a37ed5..6c0cba04f3 100644 --- a/block/qed.c +++ b/block/qed.c @@ -373,7 +373,8 @@ static void bdrv_qed_rebind(BlockDriverState *bs) s->bs = bs; } -static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags) +static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVQEDState *s = bs->opaque; QEDHeader le_header; @@ -550,16 +551,22 @@ static int qed_create(const char *filename, uint32_t cluster_size, QEDHeader le_header; uint8_t *l1_table = NULL; size_t l1_size = header.cluster_size * header.table_size; + Error *local_err = NULL; int ret = 0; BlockDriverState *bs = NULL; - ret = bdrv_create_file(filename, NULL); + ret = bdrv_create_file(filename, NULL, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } - ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB); + ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB, + &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } @@ -603,7 +610,8 @@ out: return ret; } -static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options) +static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { uint64_t image_size = 0; uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE; @@ -1547,7 +1555,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs) bdrv_qed_close(bs); memset(s, 0, sizeof(BDRVQEDState)); - bdrv_qed_open(bs, NULL, bs->open_flags); + bdrv_qed_open(bs, NULL, bs->open_flags, NULL); } static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result, diff --git a/block/qed.h b/block/qed.h index 2b4ddedf31..5d65bea075 100644 --- a/block/qed.h +++ b/block/qed.h @@ -100,7 +100,7 @@ typedef struct { /* if (features & QED_F_BACKING_FILE) */ uint32_t backing_filename_offset; /* in bytes from start of header */ uint32_t backing_filename_size; /* in bytes */ -} QEDHeader; +} QEMU_PACKED QEDHeader; typedef struct { uint64_t offsets[0]; /* in bytes */ diff --git a/block/raw-posix.c b/block/raw-posix.c index 1b41ea3356..f7f102d2e2 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -335,7 +335,8 @@ fail: return ret; } -static int raw_open(BlockDriverState *bs, QDict *options, int flags) +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; @@ -1040,7 +1041,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) return (int64_t)st.st_blocks * 512; } -static int raw_create(const char *filename, QEMUOptionParameter *options) +static int raw_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd; int result = 0; @@ -1198,6 +1200,7 @@ static BlockDriver bdrv_file = { .format_name = "file", .protocol_name = "file", .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, .bdrv_probe = NULL, /* no probe for protocols */ .bdrv_file_open = raw_open, .bdrv_reopen_prepare = raw_reopen_prepare, @@ -1331,7 +1334,8 @@ static int check_hdev_writable(BDRVRawState *s) return 0; } -static int hdev_open(BlockDriverState *bs, QDict *options, int flags) +static int hdev_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; int ret; @@ -1504,7 +1508,8 @@ static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs, cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); } -static int hdev_create(const char *filename, QEMUOptionParameter *options) +static int hdev_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd; int ret = 0; @@ -1538,6 +1543,7 @@ static BlockDriver bdrv_host_device = { .format_name = "host_device", .protocol_name = "host_device", .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, .bdrv_probe_device = hdev_probe_device, .bdrv_file_open = hdev_open, .bdrv_close = raw_close, @@ -1565,7 +1571,8 @@ static BlockDriver bdrv_host_device = { }; #ifdef __linux__ -static int floppy_open(BlockDriverState *bs, QDict *options, int flags) +static int floppy_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; int ret; @@ -1662,6 +1669,7 @@ static BlockDriver bdrv_host_floppy = { .format_name = "host_floppy", .protocol_name = "host_floppy", .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, .bdrv_probe_device = floppy_probe_device, .bdrv_file_open = floppy_open, .bdrv_close = raw_close, @@ -1686,7 +1694,8 @@ static BlockDriver bdrv_host_floppy = { .bdrv_eject = floppy_eject, }; -static int cdrom_open(BlockDriverState *bs, QDict *options, int flags) +static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; @@ -1763,6 +1772,7 @@ static BlockDriver bdrv_host_cdrom = { .format_name = "host_cdrom", .protocol_name = "host_cdrom", .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, .bdrv_probe_device = cdrom_probe_device, .bdrv_file_open = cdrom_open, .bdrv_close = raw_close, @@ -1884,6 +1894,7 @@ static BlockDriver bdrv_host_cdrom = { .format_name = "host_cdrom", .protocol_name = "host_cdrom", .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, .bdrv_probe_device = cdrom_probe_device, .bdrv_file_open = cdrom_open, .bdrv_close = raw_close, diff --git a/block/raw-win32.c b/block/raw-win32.c index d2d2d9f4d4..6ef320f16a 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -85,6 +85,7 @@ static size_t handle_aiocb_rw(RawWin32AIOData *aiocb) ret_count = 0; } if (ret_count != len) { + offset += ret_count; break; } offset += len; @@ -234,7 +235,8 @@ static QemuOptsList raw_runtime_opts = { }, }; -static int raw_open(BlockDriverState *bs, QDict *options, int flags) +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; int access_flags; @@ -420,7 +422,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) return st.st_size; } -static int raw_create(const char *filename, QEMUOptionParameter *options) +static int raw_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd; int64_t total_size = 0; @@ -456,6 +459,7 @@ static BlockDriver bdrv_file = { .format_name = "file", .protocol_name = "file", .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, .bdrv_file_open = raw_open, .bdrv_close = raw_close, .bdrv_create = raw_create, @@ -531,7 +535,8 @@ static int hdev_probe_device(const char *filename) return 0; } -static int hdev_open(BlockDriverState *bs, QDict *options, int flags) +static int hdev_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; int access_flags, create_flags; @@ -597,6 +602,7 @@ static BlockDriver bdrv_host_device = { .format_name = "host_device", .protocol_name = "host_device", .instance_size = sizeof(BDRVRawState), + .bdrv_needs_filename = true, .bdrv_probe_device = hdev_probe_device, .bdrv_file_open = hdev_open, .bdrv_close = raw_close, diff --git a/block/raw_bsd.c b/block/raw_bsd.c index a9060caec4..d4ace6020b 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -130,12 +130,22 @@ static int raw_has_zero_init(BlockDriverState *bs) return bdrv_has_zero_init(bs->file); } -static int raw_create(const char *filename, QEMUOptionParameter *options) +static int raw_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { - return bdrv_create_file(filename, options); + Error *local_err = NULL; + int ret; + + ret = bdrv_create_file(filename, options, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + } + return ret; } -static int raw_open(BlockDriverState *bs, QDict *options, int flags) +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { bs->sg = bs->file->sg; return 0; diff --git a/block/rbd.c b/block/rbd.c index e798e19f81..4a1ea5b5ce 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -287,7 +287,8 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf) return ret; } -static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options) +static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int64_t bytes = 0; int64_t objsize; @@ -446,7 +447,8 @@ static QemuOptsList runtime_opts = { }, }; -static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags) +static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRBDState *s = bs->opaque; char pool[RBD_MAX_POOL_NAME_SIZE]; @@ -891,12 +893,31 @@ static int qemu_rbd_snap_create(BlockDriverState *bs, } static int qemu_rbd_snap_remove(BlockDriverState *bs, - const char *snapshot_name) + const char *snapshot_id, + const char *snapshot_name, + Error **errp) { BDRVRBDState *s = bs->opaque; int r; + if (!snapshot_name) { + error_setg(errp, "rbd need a valid snapshot name"); + return -EINVAL; + } + + /* If snapshot_id is specified, it must be equal to name, see + qemu_rbd_snap_list() */ + if (snapshot_id && strcmp(snapshot_id, snapshot_name)) { + error_setg(errp, + "rbd do not support snapshot id, it should be NULL or " + "equal to snapshot name"); + return -EINVAL; + } + r = rbd_snap_remove(s->image, snapshot_name); + if (r < 0) { + error_setg_errno(errp, -r, "Failed to remove the snapshot"); + } return r; } @@ -922,7 +943,7 @@ static int qemu_rbd_snap_list(BlockDriverState *bs, do { snaps = g_malloc(sizeof(*snaps) * max_snaps); snap_count = rbd_snap_list(s->image, snaps, &max_snaps); - if (snap_count < 0) { + if (snap_count <= 0) { g_free(snaps); } } while (snap_count == -ERANGE); @@ -946,6 +967,7 @@ static int qemu_rbd_snap_list(BlockDriverState *bs, sn_info->vm_clock_nsec = 0; } rbd_snap_list_end(snaps); + g_free(snaps); done: *psn_tab = sn_tab; @@ -981,6 +1003,7 @@ static QEMUOptionParameter qemu_rbd_create_options[] = { static BlockDriver bdrv_rbd = { .format_name = "rbd", .instance_size = sizeof(BDRVRBDState), + .bdrv_needs_filename = true, .bdrv_file_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, .bdrv_create = qemu_rbd_create, diff --git a/block/sheepdog.c b/block/sheepdog.c index f9988d35ba..5f81c93ee3 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1242,7 +1242,8 @@ static QemuOptsList runtime_opts = { }, }; -static int sd_open(BlockDriverState *bs, QDict *options, int flags) +static int sd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { int ret, fd; uint32_t vid = 0; @@ -1400,10 +1401,13 @@ static int sd_prealloc(const char *filename) uint32_t idx, max_idx; int64_t vdi_size; void *buf = g_malloc0(SD_DATA_OBJ_SIZE); + Error *local_err = NULL; int ret; - ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR); + ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto out; } @@ -1437,7 +1441,8 @@ out: return ret; } -static int sd_create(const char *filename, QEMUOptionParameter *options) +static int sd_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int ret = 0; uint32_t vid = 0, base_vid = 0; @@ -1447,6 +1452,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; uint32_t snapid; bool prealloc = false; + Error *local_err = NULL; s = g_malloc0(sizeof(BDRVSheepdogState)); @@ -1500,8 +1506,10 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) goto out; } - ret = bdrv_file_open(&bs, backing_file, NULL, 0); + ret = bdrv_file_open(&bs, backing_file, NULL, 0, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto out; } @@ -2072,7 +2080,10 @@ out: return ret; } -static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +static int sd_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) { /* FIXME: Delete specified snapshot id. */ return 0; @@ -2327,6 +2338,7 @@ static BlockDriver bdrv_sheepdog = { .format_name = "sheepdog", .protocol_name = "sheepdog", .instance_size = sizeof(BDRVSheepdogState), + .bdrv_needs_filename = true, .bdrv_file_open = sd_open, .bdrv_close = sd_close, .bdrv_create = sd_create, @@ -2355,6 +2367,7 @@ static BlockDriver bdrv_sheepdog_tcp = { .format_name = "sheepdog", .protocol_name = "sheepdog+tcp", .instance_size = sizeof(BDRVSheepdogState), + .bdrv_needs_filename = true, .bdrv_file_open = sd_open, .bdrv_close = sd_close, .bdrv_create = sd_create, @@ -2383,6 +2396,7 @@ static BlockDriver bdrv_sheepdog_unix = { .format_name = "sheepdog", .protocol_name = "sheepdog+unix", .instance_size = sizeof(BDRVSheepdogState), + .bdrv_needs_filename = true, .bdrv_file_open = sd_open, .bdrv_close = sd_close, .bdrv_create = sd_create, diff --git a/block/snapshot.c b/block/snapshot.c index 8f61cc0745..a05c0c0be0 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -48,6 +48,79 @@ int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, return ret; } +/** + * Look up an internal snapshot by @id and @name. + * @bs: block device to search + * @id: unique snapshot ID, or NULL + * @name: snapshot name, or NULL + * @sn_info: location to store information on the snapshot found + * @errp: location to store error, will be set only for exception + * + * This function will traverse snapshot list in @bs to search the matching + * one, @id and @name are the matching condition: + * If both @id and @name are specified, find the first one with id @id and + * name @name. + * If only @id is specified, find the first one with id @id. + * If only @name is specified, find the first one with name @name. + * if none is specified, abort(). + * + * Returns: true when a snapshot is found and @sn_info will be filled, false + * when error or not found. If all operation succeed but no matching one is + * found, @errp will NOT be set. + */ +bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, + const char *id, + const char *name, + QEMUSnapshotInfo *sn_info, + Error **errp) +{ + QEMUSnapshotInfo *sn_tab, *sn; + int nb_sns, i; + bool ret = false; + + assert(id || name); + + nb_sns = bdrv_snapshot_list(bs, &sn_tab); + if (nb_sns < 0) { + error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list"); + return false; + } else if (nb_sns == 0) { + return false; + } + + if (id && name) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) { + *sn_info = *sn; + ret = true; + break; + } + } + } else if (id) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->id_str, id)) { + *sn_info = *sn; + ret = true; + break; + } + } + } else if (name) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->name, name)) { + *sn_info = *sn; + ret = true; + break; + } + } + } + + g_free(sn_tab); + return ret; +} + int bdrv_can_snapshot(BlockDriverState *bs) { BlockDriver *drv = bs->drv; @@ -97,7 +170,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, if (bs->file) { drv->bdrv_close(bs); ret = bdrv_snapshot_goto(bs->file, snapshot_id); - open_ret = drv->bdrv_open(bs, NULL, bs->open_flags); + open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL); if (open_ret < 0) { bdrv_unref(bs->file); bs->drv = NULL; @@ -109,21 +182,73 @@ int bdrv_snapshot_goto(BlockDriverState *bs, return -ENOTSUP; } -int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +/** + * Delete an internal snapshot by @snapshot_id and @name. + * @bs: block device used in the operation + * @snapshot_id: unique snapshot ID, or NULL + * @name: snapshot name, or NULL + * @errp: location to store error + * + * If both @snapshot_id and @name are specified, delete the first one with + * id @snapshot_id and name @name. + * If only @snapshot_id is specified, delete the first one with id + * @snapshot_id. + * If only @name is specified, delete the first one with name @name. + * if none is specified, return -ENINVAL. + * + * Returns: 0 on success, -errno on failure. If @bs is not inserted, return + * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs + * does not support internal snapshot deletion, return -ENOTSUP. If @bs does + * not support parameter @snapshot_id or @name, or one of them is not correctly + * specified, return -EINVAL. If @bs can't find one matching @id and @name, + * return -ENOENT. If @errp != NULL, it will always be filled with error + * message on failure. + */ +int bdrv_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) { BlockDriver *drv = bs->drv; if (!drv) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; } + if (!snapshot_id && !name) { + error_setg(errp, "snapshot_id and name are both NULL"); + return -EINVAL; + } if (drv->bdrv_snapshot_delete) { - return drv->bdrv_snapshot_delete(bs, snapshot_id); + return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); } if (bs->file) { - return bdrv_snapshot_delete(bs->file, snapshot_id); + return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp); } + error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + drv->format_name, bdrv_get_device_name(bs), + "internal snapshot deletion"); return -ENOTSUP; } +void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, + const char *id_or_name, + Error **errp) +{ + int ret; + Error *local_err = NULL; + + ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err); + if (ret == -ENOENT || ret == -EINVAL) { + error_free(local_err); + local_err = NULL; + ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err); + } + + if (ret < 0) { + error_propagate(errp, local_err); + } +} + int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info) { diff --git a/block/ssh.c b/block/ssh.c index 27691b4ad5..aa63c9d20e 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -608,7 +608,8 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, return ret; } -static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags) +static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, + Error **errp) { BDRVSSHState *s = bs->opaque; int ret; @@ -650,7 +651,8 @@ static QEMUOptionParameter ssh_create_options[] = { { NULL } }; -static int ssh_create(const char *filename, QEMUOptionParameter *options) +static int ssh_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int r, ret; Error *local_err = NULL; diff --git a/block/stream.c b/block/stream.c index 078ce4aa6a..45837f4476 100644 --- a/block/stream.c +++ b/block/stream.c @@ -119,11 +119,12 @@ wait: break; } + copy = false; + ret = bdrv_is_allocated(bs, sector_num, STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n); if (ret == 1) { /* Allocated in the top, no need to copy. */ - copy = false; } else if (ret >= 0) { /* Copy if allocated in the intermediate images. Limit to the * known-unallocated area [sector_num, sector_num+n). */ @@ -138,7 +139,7 @@ wait: copy = (ret == 1); } trace_stream_one_iteration(s, sector_num, n, ret); - if (ret >= 0 && copy) { + if (copy) { if (s->common.speed) { delay_ns = ratelimit_calculate_delay(&s->limit, n); if (delay_ns > 0) { diff --git a/block/vdi.c b/block/vdi.c index 1bf7dc575a..b6ec0020dc 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -165,7 +165,7 @@ typedef struct { uuid_t uuid_link; uuid_t uuid_parent; uint64_t unused2[7]; -} VdiHeader; +} QEMU_PACKED VdiHeader; typedef struct { /* The block map entries are little endian (even in memory). */ @@ -364,7 +364,8 @@ static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename) return result; } -static int vdi_open(BlockDriverState *bs, QDict *options, int flags) +static int vdi_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVVdiState *s = bs->opaque; VdiHeader header; @@ -644,7 +645,8 @@ static int vdi_co_write(BlockDriverState *bs, return ret; } -static int vdi_create(const char *filename, QEMUOptionParameter *options) +static int vdi_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd; int result = 0; diff --git a/block/vhdx.c b/block/vhdx.c index e9704b1fdc..b8aa49ce4e 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -715,7 +715,8 @@ exit: } -static int vhdx_open(BlockDriverState *bs, QDict *options, int flags) +static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVVHDXState *s = bs->opaque; int ret = 0; diff --git a/block/vmdk.c b/block/vmdk.c index fb5b5297ce..5d56e316f1 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -105,7 +105,7 @@ typedef struct VmdkExtent { uint32_t l2_cache_offsets[L2_CACHE_SIZE]; uint32_t l2_cache_counts[L2_CACHE_SIZE]; - unsigned int cluster_sectors; + int64_t cluster_sectors; } VmdkExtent; typedef struct BDRVVmdkState { @@ -424,7 +424,7 @@ static int vmdk_add_extent(BlockDriverState *bs, extent->l1_size = l1_size; extent->l1_entry_sectors = l2_size * cluster_sectors; extent->l2_size = l2_size; - extent->cluster_sectors = cluster_sectors; + extent->cluster_sectors = flat ? sectors : cluster_sectors; if (s->num_extents > 1) { extent->end_sector = (*(extent - 1)).end_sector + extent->sectors; @@ -697,6 +697,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, int64_t flat_offset; char extent_path[PATH_MAX]; BlockDriverState *extent_file; + Error *local_err = NULL; while (*p) { /* parse extent line: @@ -726,8 +727,11 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, path_combine(extent_path, sizeof(extent_path), desc_file_path, fname); - ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags); + ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags, + &local_err); if (ret) { + qerror_report_err(local_err); + error_free(local_err); return ret; } @@ -737,7 +741,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, VmdkExtent *extent; ret = vmdk_add_extent(bs, extent_file, true, sectors, - 0, 0, 0, 0, sectors, &extent); + 0, 0, 0, 0, 0, &extent); if (ret < 0) { return ret; } @@ -806,7 +810,8 @@ exit: return ret; } -static int vmdk_open(BlockDriverState *bs, QDict *options, int flags) +static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { int ret; BDRVVmdkState *s = bs->opaque; @@ -1551,7 +1556,8 @@ static int filename_decompose(const char *filename, char *path, char *prefix, return VMDK_OK; } -static int vmdk_create(const char *filename, QEMUOptionParameter *options) +static int vmdk_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd, idx = 0; char desc[BUF_SIZE]; @@ -1589,6 +1595,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) "ddb.geometry.heads = \"%d\"\n" "ddb.geometry.sectors = \"63\"\n" "ddb.adapterType = \"%s\"\n"; + Error *local_err = NULL; if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { return -EINVAL; @@ -1651,8 +1658,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) } if (backing_file) { BlockDriverState *bs = bdrv_new(""); - ret = bdrv_open(bs, backing_file, NULL, 0, NULL); + ret = bdrv_open(bs, backing_file, NULL, 0, NULL, &local_err); if (ret != 0) { + qerror_report_err(local_err); + error_free(local_err); bdrv_unref(bs); return ret; } diff --git a/block/vpc.c b/block/vpc.c index fe4f311d50..b5dca3961e 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -46,7 +46,7 @@ enum vhd_type { #define VHD_TIMESTAMP_BASE 946684800 // always big-endian -struct vhd_footer { +typedef struct vhd_footer { char creator[8]; // "conectix" uint32_t features; uint32_t version; @@ -79,9 +79,9 @@ struct vhd_footer { uint8_t uuid[16]; uint8_t in_saved_state; -}; +} QEMU_PACKED VHDFooter; -struct vhd_dyndisk_header { +typedef struct vhd_dyndisk_header { char magic[8]; // "cxsparse" // Offset of next header structure, 0xFFFFFFFF if none @@ -111,7 +111,7 @@ struct vhd_dyndisk_header { uint32_t reserved; uint64_t data_offset; } parent_locator[8]; -}; +} QEMU_PACKED VHDDynDiskHeader; typedef struct BDRVVPCState { CoMutex lock; @@ -155,12 +155,13 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int vpc_open(BlockDriverState *bs, QDict *options, int flags) +static int vpc_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVVPCState *s = bs->opaque; int i; - struct vhd_footer* footer; - struct vhd_dyndisk_header* dyndisk_header; + VHDFooter *footer; + VHDDynDiskHeader *dyndisk_header; uint8_t buf[HEADER_SIZE]; uint32_t checksum; int disk_type = VHD_DYNAMIC; @@ -171,7 +172,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags) goto fail; } - footer = (struct vhd_footer*) s->footer_buf; + footer = (VHDFooter *) s->footer_buf; if (strncmp(footer->creator, "conectix", 8)) { int64_t offset = bdrv_getlength(bs->file); if (offset < 0) { @@ -223,7 +224,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags) goto fail; } - dyndisk_header = (struct vhd_dyndisk_header *) buf; + dyndisk_header = (VHDDynDiskHeader *) buf; if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { ret = -EINVAL; @@ -445,7 +446,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, int ret; int64_t offset; int64_t sectors, sectors_per_block; - struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf; + VHDFooter *footer = (VHDFooter *) s->footer_buf; if (cpu_to_be32(footer->type) == VHD_FIXED) { return bdrv_read(bs->file, sector_num, buf, nb_sectors); @@ -494,7 +495,7 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num, int64_t offset; int64_t sectors, sectors_per_block; int ret; - struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf; + VHDFooter *footer = (VHDFooter *) s->footer_buf; if (cpu_to_be32(footer->type) == VHD_FIXED) { return bdrv_write(bs->file, sector_num, buf, nb_sectors); @@ -596,8 +597,8 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors) { - struct vhd_dyndisk_header* dyndisk_header = - (struct vhd_dyndisk_header*) buf; + VHDDynDiskHeader *dyndisk_header = + (VHDDynDiskHeader *) buf; size_t block_size, num_bat_entries; int i; int ret = -EIO; @@ -683,10 +684,11 @@ static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size) return ret; } -static int vpc_create(const char *filename, QEMUOptionParameter *options) +static int vpc_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { uint8_t buf[1024]; - struct vhd_footer *footer = (struct vhd_footer *) buf; + VHDFooter *footer = (VHDFooter *) buf; QEMUOptionParameter *disk_type_param; int fd, i; uint16_t cyls = 0; @@ -789,7 +791,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) static int vpc_has_zero_init(BlockDriverState *bs) { BDRVVPCState *s = bs->opaque; - struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf; + VHDFooter *footer = (VHDFooter *) s->footer_buf; if (cpu_to_be32(footer->type) == VHD_FIXED) { return bdrv_has_zero_init(bs->file); diff --git a/block/vvfat.c b/block/vvfat.c index 0129195e29..3ddaa0bcce 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1065,7 +1065,8 @@ static void vvfat_parse_filename(const char *filename, QDict *options, qdict_put(options, "rw", qbool_from_int(rw)); } -static int vvfat_open(BlockDriverState *bs, QDict *options, int flags) +static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVVVFATState *s = bs->opaque; int cyls, heads, secs; @@ -2909,6 +2910,7 @@ static int enable_write_target(BDRVVVFATState *s) { BlockDriver *bdrv_qcow; QEMUOptionParameter *options; + Error *local_err = NULL; int ret; int size = sector2cluster(s, s->sector_count); s->used_clusters = calloc(size, 1); @@ -2926,16 +2928,21 @@ static int enable_write_target(BDRVVVFATState *s) set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512); set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:"); - ret = bdrv_create(bdrv_qcow, s->qcow_filename, options); + ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto err; } s->qcow = bdrv_new(""); ret = bdrv_open(s->qcow, s->qcow_filename, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow); + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow, + &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); bdrv_unref(s->qcow); goto err; } diff --git a/blockdev.c b/blockdev.c index 07dac05a2c..8aa66a949c 100644 --- a/blockdev.c +++ b/blockdev.c @@ -443,7 +443,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, if (qemu_opt_get_bool(opts, "cache.direct", false)) { bdrv_flags |= BDRV_O_NOCACHE; } - if (qemu_opt_get_bool(opts, "cache.no-flush", true)) { + if (qemu_opt_get_bool(opts, "cache.no-flush", false)) { bdrv_flags |= BDRV_O_NO_FLUSH; } @@ -710,17 +710,11 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, } QINCREF(bs_opts); - ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv); + ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error); if (ret < 0) { - if (ret == -EMEDIUMTYPE) { - error_report("could not open disk image %s: not in %s format", - file ?: dinfo->id, drv ? drv->format_name : - qdict_get_str(bs_opts, "driver")); - } else { - error_report("could not open disk image %s: %s", - file ?: dinfo->id, strerror(-ret)); - } + error_report("could not open disk image %s: %s", + file ?: dinfo->id, error_get_pretty(error)); goto err; } @@ -858,6 +852,80 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file, &snapshot, errp); } +void qmp_blockdev_snapshot_internal_sync(const char *device, + const char *name, + Error **errp) +{ + BlockdevSnapshotInternal snapshot = { + .device = (char *) device, + .name = (char *) name + }; + + blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC, + &snapshot, errp); +} + +SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, + bool has_id, + const char *id, + bool has_name, + const char *name, + Error **errp) +{ + BlockDriverState *bs = bdrv_find(device); + QEMUSnapshotInfo sn; + Error *local_err = NULL; + SnapshotInfo *info = NULL; + int ret; + + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return NULL; + } + + if (!has_id) { + id = NULL; + } + + if (!has_name) { + name = NULL; + } + + if (!id && !name) { + error_setg(errp, "Name or id must be provided"); + return NULL; + } + + ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return NULL; + } + if (!ret) { + error_setg(errp, + "Snapshot with id '%s' and name '%s' does not exist on " + "device '%s'", + STR_OR_NULL(id), STR_OR_NULL(name), device); + return NULL; + } + + bdrv_snapshot_delete(bs, id, name, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return NULL; + } + + info = g_malloc0(sizeof(SnapshotInfo)); + info->id = g_strdup(sn.id_str); + info->name = g_strdup(sn.name); + info->date_nsec = sn.date_nsec; + info->date_sec = sn.date_sec; + info->vm_state_size = sn.vm_state_size; + info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000; + info->vm_clock_sec = sn.vm_clock_nsec / 1000000000; + + return info; +} /* New and old BlockDriverState structs for group snapshots */ @@ -889,6 +957,117 @@ struct BlkTransactionState { QSIMPLEQ_ENTRY(BlkTransactionState) entry; }; +/* internal snapshot private data */ +typedef struct InternalSnapshotState { + BlkTransactionState common; + BlockDriverState *bs; + QEMUSnapshotInfo sn; +} InternalSnapshotState; + +static void internal_snapshot_prepare(BlkTransactionState *common, + Error **errp) +{ + const char *device; + const char *name; + BlockDriverState *bs; + QEMUSnapshotInfo old_sn, *sn; + bool ret; + qemu_timeval tv; + BlockdevSnapshotInternal *internal; + InternalSnapshotState *state; + int ret1; + + g_assert(common->action->kind == + TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC); + internal = common->action->blockdev_snapshot_internal_sync; + state = DO_UPCAST(InternalSnapshotState, common, common); + + /* 1. parse input */ + device = internal->device; + name = internal->name; + + /* 2. check for validation */ + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + if (!bdrv_is_inserted(bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + return; + } + + if (bdrv_is_read_only(bs)) { + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); + return; + } + + if (!bdrv_can_snapshot(bs)) { + error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + bs->drv->format_name, device, "internal snapshot"); + return; + } + + if (!strlen(name)) { + error_setg(errp, "Name is empty"); + return; + } + + /* check whether a snapshot with name exist */ + ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, errp); + if (error_is_set(errp)) { + return; + } else if (ret) { + error_setg(errp, + "Snapshot with name '%s' already exists on device '%s'", + name, device); + return; + } + + /* 3. take the snapshot */ + sn = &state->sn; + pstrcpy(sn->name, sizeof(sn->name), name); + qemu_gettimeofday(&tv); + sn->date_sec = tv.tv_sec; + sn->date_nsec = tv.tv_usec * 1000; + sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + ret1 = bdrv_snapshot_create(bs, sn); + if (ret1 < 0) { + error_setg_errno(errp, -ret1, + "Failed to create snapshot '%s' on device '%s'", + name, device); + return; + } + + /* 4. succeed, mark a snapshot is created */ + state->bs = bs; +} + +static void internal_snapshot_abort(BlkTransactionState *common) +{ + InternalSnapshotState *state = + DO_UPCAST(InternalSnapshotState, common, common); + BlockDriverState *bs = state->bs; + QEMUSnapshotInfo *sn = &state->sn; + Error *local_error = NULL; + + if (!bs) { + return; + } + + if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { + error_report("Failed to delete snapshot with id '%s' and name '%s' on " + "device '%s' in abort: %s", + sn->id_str, + sn->name, + bdrv_get_device_name(bs), + error_get_pretty(local_error)); + error_free(local_error); + } +} + /* external snapshot private data */ typedef struct ExternalSnapshotState { BlkTransactionState common; @@ -971,9 +1150,9 @@ static void external_snapshot_prepare(BlkTransactionState *common, /* TODO Inherit bs->options or only take explicit options with an * extended QMP command? */ ret = bdrv_open(state->new_bs, new_image_file, NULL, - flags | BDRV_O_NO_BACKING, drv); + flags | BDRV_O_NO_BACKING, drv, &local_err); if (ret != 0) { - error_setg_file_open(errp, -ret, new_image_file); + error_propagate(errp, local_err); } } @@ -1072,6 +1251,11 @@ static const BdrvActionOps actions[] = { .prepare = abort_prepare, .commit = abort_commit, }, + [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = { + .instance_size = sizeof(InternalSnapshotState), + .prepare = internal_snapshot_prepare, + .abort = internal_snapshot_abort, + }, }; /* @@ -1102,6 +1286,8 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp) assert(dev_info->kind < ARRAY_SIZE(actions)); ops = &actions[dev_info->kind]; + assert(ops->instance_size > 0); + state = g_malloc0(ops->instance_size); state->ops = ops; state->action = dev_info; @@ -1203,11 +1389,12 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, int bdrv_flags, BlockDriver *drv, const char *password, Error **errp) { + Error *local_err = NULL; int ret; - ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv); + ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err); if (ret < 0) { - error_setg_file_open(errp, -ret, filename); + error_propagate(errp, local_err); return; } @@ -1627,10 +1814,10 @@ void qmp_drive_backup(const char *device, const char *target, } target_bs = bdrv_new(""); - ret = bdrv_open(target_bs, target, NULL, flags, drv); + ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err); if (ret < 0) { bdrv_unref(target_bs); - error_setg_file_open(errp, -ret, target); + error_propagate(errp, local_err); return; } @@ -1762,10 +1949,11 @@ void qmp_drive_mirror(const char *device, const char *target, * file. */ target_bs = bdrv_new(""); - ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv); + ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv, + &local_err); if (ret < 0) { bdrv_unref(target_bs); - error_setg_file_open(errp, -ret, target); + error_propagate(errp, local_err); return; } diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 325f564f80..ddc74ed0d7 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -323,9 +323,9 @@ abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len); abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len); /* Functions for accessing guest memory. The tget and tput functions - read/write single values, byteswapping as necessary. The lock_user + read/write single values, byteswapping as necessary. The lock_user function gets a pointer to a contiguous area of guest memory, but does not perform - and byteswapping. lock_user may return either a pointer to the guest + any byteswapping. lock_user may return either a pointer to the guest memory, or a temporary buffer. */ /* Lock an area of guest memory into the host. If copy is true then the @@ -381,7 +381,7 @@ static inline void *lock_user_string(abi_ulong guest_addr) return lock_user(VERIFY_READ, guest_addr, (long)(len + 1), 1); } -/* Helper macros for locking/ulocking a target struct. */ +/* Helper macros for locking/unlocking a target struct. */ #define lock_user_struct(type, host_ptr, guest_addr, copy) \ (host_ptr = lock_user(type, guest_addr, sizeof(*host_ptr), copy)) #define unlock_user_struct(host_ptr, guest_addr, copy) \ @@ -215,7 +215,6 @@ linux_user="no" bsd_user="no" guest_base="yes" uname_release="" -mixemu="no" aix="no" blobs="yes" pkgversion="" @@ -238,6 +237,7 @@ win_sdk="no" want_tools="yes" libiscsi="" coroutine="" +coroutine_pool="" seccomp="" glusterfs="" glusterfs_discard="no" @@ -561,7 +561,6 @@ Haiku) audio_possible_drivers="oss alsa sdl esd pa" linux="yes" linux_user="yes" - usb="linux" kvm="yes" vhost_net="yes" vhost_scsi="yes" @@ -574,9 +573,6 @@ esac if [ "$bsd" = "yes" ] ; then if [ "$darwin" != "yes" ] ; then - if [ "$targetos" != "FreeBSD" ]; then - usb="bsd" - fi bsd_user="yes" fi fi @@ -872,8 +868,6 @@ for opt do ;; --enable-fdt) fdt="yes" ;; - --enable-mixemu) mixemu="yes" - ;; --disable-linux-aio) linux_aio="no" ;; --enable-linux-aio) linux_aio="yes" @@ -888,6 +882,10 @@ for opt do ;; --with-coroutine=*) coroutine="$optarg" ;; + --disable-coroutine-pool) coroutine_pool="no" + ;; + --enable-coroutine-pool) coroutine_pool="yes" + ;; --disable-docs) docs="no" ;; --enable-docs) docs="yes" @@ -980,6 +978,14 @@ for opt do done case "$cpu" in + ppc) + CPU_CFLAGS="-m32" + LDFLAGS="-m32 $LDFLAGS" + ;; + ppc64) + CPU_CFLAGS="-m64" + LDFLAGS="-m64 $LDFLAGS" + ;; sparc) LDFLAGS="-m32 $LDFLAGS" CPU_CFLAGS="-m32 -mcpu=ultrasparc" @@ -1102,7 +1108,6 @@ echo " (affects only QEMU, not qemu-img)" echo " --block-drv-ro-whitelist=L" echo " set block driver read-only whitelist" echo " (affects only QEMU, not qemu-img)" -echo " --enable-mixemu enable mixer emulation" echo " --disable-xen disable xen backend driver support" echo " --enable-xen enable xen backend driver support" echo " --disable-xen-pci-passthrough" @@ -1189,6 +1194,8 @@ echo " --disable-seccomp disable seccomp support" echo " --enable-seccomp enables seccomp support" echo " --with-coroutine=BACKEND coroutine backend. Supported options:" echo " gthread, ucontext, sigaltstack, windows" +echo " --disable-coroutine-pool disable coroutine freelist (worse performance)" +echo " --enable-coroutine-pool enable coroutine freelist (better performance)" echo " --enable-glusterfs enable GlusterFS backend" echo " --disable-glusterfs disable GlusterFS backend" echo " --enable-gcov enable test coverage analysis with gcov" @@ -3119,7 +3126,6 @@ fi if test "$libusb" != "no" ; then if $pkg_config --atleast-version=1.0.13 libusb-1.0; then libusb="yes" - usb="libusb" libusb_cflags=$($pkg_config --cflags libusb-1.0) libusb_libs=$($pkg_config --libs libusb-1.0) QEMU_CFLAGS="$QEMU_CFLAGS $libusb_cflags" @@ -3362,6 +3368,17 @@ else esac fi +if test "$coroutine_pool" = ""; then + if test "$coroutine" = "gthread"; then + coroutine_pool=no + else + coroutine_pool=yes + fi +fi +if test "$coroutine" = "gthread" -a "$coroutine_pool" = "yes"; then + error_exit "'gthread' coroutine backend does not support pool (use --disable-coroutine-pool)" +fi + ########################################## # check if we have open_by_handle_at @@ -3502,7 +3519,7 @@ if test "$gcov" = "yes" ; then CFLAGS="-fprofile-arcs -ftest-coverage -g $CFLAGS" LDFLAGS="-fprofile-arcs -ftest-coverage $LDFLAGS" elif test "$debug" = "no" ; then - CFLAGS="-O2 -D_FORTIFY_SOURCE=2 $CFLAGS" + CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS" fi @@ -3681,7 +3698,6 @@ echo "mingw32 support $mingw32" echo "Audio drivers $audio_drv_list" echo "Block whitelist (rw) $block_drv_rw_whitelist" echo "Block whitelist (ro) $block_drv_ro_whitelist" -echo "Mixer emulation $mixemu" echo "VirtFS support $virtfs" echo "VNC support $vnc" if test "$vnc" = "yes" ; then @@ -3733,6 +3749,7 @@ echo "build guest agent $guest_agent" echo "QGA VSS support $guest_agent_with_vss" echo "seccomp support $seccomp" echo "coroutine backend $coroutine" +echo "coroutine pool $coroutine_pool" echo "GlusterFS support $glusterfs" echo "virtio-blk-data-plane $virtio_blk_data_plane" echo "gcov $gcov_tool" @@ -3778,7 +3795,7 @@ echo "libs_softmmu=$libs_softmmu" >> $config_host_mak echo "ARCH=$ARCH" >> $config_host_mak case "$cpu" in - arm|i386|x86_64|x32|ppc|aarch64) + aarch64 | arm | i386 | x86_64 | x32 | ppc*) # The TCG interpreter currently does not support ld/st optimization. if test "$tcg_interpreter" = "no" ; then echo "CONFIG_QEMU_LDST_OPTIMIZATION=y" >> $config_host_mak @@ -3867,9 +3884,6 @@ if test "$audio_win_int" = "yes" ; then fi echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak -if test "$mixemu" = "yes" ; then - echo "CONFIG_MIXEMU=y" >> $config_host_mak -fi if test "$vnc" = "yes" ; then echo "CONFIG_VNC=y" >> $config_host_mak fi @@ -4092,6 +4106,11 @@ if test "$rbd" = "yes" ; then fi echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak +if test "$coroutine_pool" = "yes" ; then + echo "CONFIG_COROUTINE_POOL=1" >> $config_host_mak +else + echo "CONFIG_COROUTINE_POOL=0" >> $config_host_mak +fi if test "$open_by_handle_at" = "yes" ; then echo "CONFIG_OPEN_BY_HANDLE=y" >> $config_host_mak @@ -4142,24 +4161,11 @@ if test "$virtio_blk_data_plane" = "yes" ; then fi # USB host support -case "$usb" in -linux) - echo "HOST_USB=linux legacy" >> $config_host_mak -;; -bsd) - echo "HOST_USB=bsd" >> $config_host_mak -;; -libusb) - if test "$linux" = "yes"; then - echo "HOST_USB=libusb linux legacy" >> $config_host_mak - else - echo "HOST_USB=libusb legacy" >> $config_host_mak - fi -;; -*) +if test "$libusb" = "yes"; then + echo "HOST_USB=libusb legacy" >> $config_host_mak +else echo "HOST_USB=stub" >> $config_host_mak -;; -esac +fi # TPM passthrough support? if test "$tpm" = "yes"; then @@ -4651,7 +4657,7 @@ if [ "$dtc_internal" = "yes" ]; then fi # build tree in object directory in case the source is not in the current directory -DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa" +DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests" DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS qapi-generated" diff --git a/docs/qmp/README b/docs/qmp/README new file mode 100644 index 0000000000..85c4bc17bf --- /dev/null +++ b/docs/qmp/README @@ -0,0 +1,87 @@ + QEMU Machine Protocol + ===================== + +Introduction +------------ + +The QEMU Machine Protocol (QMP) allows applications to operate a +QEMU instance. + +QMP is JSON[1] based and features the following: + +- Lightweight, text-based, easy to parse data format +- Asynchronous messages support (ie. events) +- Capabilities Negotiation + +For detailed information on QMP's usage, please, refer to the following files: + +o qmp-spec.txt QEMU Machine Protocol current specification +o qmp-commands.txt QMP supported commands (auto-generated at build-time) +o qmp-events.txt List of available asynchronous events + +[1] http://www.json.org + +Usage +----- + +You can use the -qmp option to enable QMP. For example, the following +makes QMP available on localhost port 4444: + +$ qemu [...] -qmp tcp:localhost:4444,server,nowait + +However, for more flexibility and to make use of more options, the -mon +command-line option should be used. For instance, the following example +creates one HMP instance (human monitor) on stdio and one QMP instance +on localhost port 4444: + +$ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \ + -chardev socket,id=mon1,host=localhost,port=4444,server,nowait \ + -mon chardev=mon1,mode=control,pretty=on + +Please, refer to QEMU's manpage for more information. + +Simple Testing +-------------- + +To manually test QMP one can connect with telnet and issue commands by hand: + +$ telnet localhost 4444 +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. +{ + "QMP": { + "version": { + "qemu": { + "micro": 50, + "minor": 6, + "major": 1 + }, + "package": "" + }, + "capabilities": [ + ] + } +} + +{ "execute": "qmp_capabilities" } +{ + "return": { + } +} + +{ "execute": "query-status" } +{ + "return": { + "status": "prelaunch", + "singlestep": false, + "running": false + } +} + +Please, refer to the qapi-schema.json file for a complete command reference. + +QMP wiki page +------------- + +http://wiki.qemu.org/QMP diff --git a/QMP/qmp-events.txt b/docs/qmp/qmp-events.txt index 4b24ec900d..6b87e9786a 100644 --- a/QMP/qmp-events.txt +++ b/docs/qmp/qmp-events.txt @@ -1,4 +1,4 @@ - QEMU Monitor Protocol Events + QEMU Machine Protocol Events ============================ BALLOON_CHANGE @@ -159,7 +159,7 @@ Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR event. DEVICE_DELETED ------------------ +-------------- Emitted whenever the device removal completion is acknowledged by the guest. @@ -194,8 +194,22 @@ Data: }, "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +GUEST_PANICKED +-------------- + +Emitted when guest OS panic is detected. + +Data: + +- "action": Action that has been taken (json-string, currently always "pause"). + +Example: + +{ "event": "GUEST_PANICKED", + "data": { "action": "pause" } } + NIC_RX_FILTER_CHANGED ------------------ +--------------------- The event is emitted once until the query command is executed, the first event will always be emitted. @@ -486,17 +500,3 @@ Example: Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is followed respectively by the RESET, SHUTDOWN, or STOP events. - -GUEST_PANICKED --------------- - -Emitted when guest OS panic is detected. - -Data: - -- "action": Action that has been taken (json-string, currently always "pause"). - -Example: - -{ "event": "GUEST_PANICKED", - "data": { "action": "pause" } } diff --git a/QMP/qmp-spec.txt b/docs/qmp/qmp-spec.txt index a27789692b..22568c644e 100644 --- a/QMP/qmp-spec.txt +++ b/docs/qmp/qmp-spec.txt @@ -1,21 +1,17 @@ - QEMU Monitor Protocol Specification - Version 0.1 + QEMU Machine Protocol Specification 1. Introduction =============== -This document specifies the QEMU Monitor Protocol (QMP), a JSON-based protocol -which is available for applications to control QEMU at the machine-level. - -To enable QMP support, QEMU has to be run in "control mode". This is done by -starting QEMU with the appropriate command-line options. Please, refer to the -QEMU manual page for more information. +This document specifies the QEMU Machine Protocol (QMP), a JSON-based protocol +which is available for applications to operate QEMU at the machine-level. 2. Protocol Specification ========================= This section details the protocol format. For the purpose of this document -"Client" is any application which is communicating with QEMU in control mode, -and "Server" is QEMU itself. +"Client" is any application which is using QMP to communicate with QEMU and +"Server" is QEMU itself. JSON data structures, when mentioned in this document, are always in the following format: @@ -47,14 +43,14 @@ that the connection has been successfully established and that the Server is ready for capabilities negotiation (for more information refer to section '4. Capabilities Negotiation'). -The format is: +The greeting message format is: { "QMP": { "version": json-object, "capabilities": json-array } } Where, - The "version" member contains the Server's version information (the format - is the same of the 'query-version' command) + is the same of the query-version command) - The "capabilities" member specify the availability of features beyond the baseline specification @@ -83,10 +79,7 @@ of a command execution: success or error. 2.4.1 success ------------- -The success response is issued when the command execution has finished -without errors. - -The format is: +The format of a success response is: { "return": json-object, "id": json-value } @@ -96,15 +89,12 @@ The format is: in a per-command basis or an empty json-object if the command does not return data - The "id" member contains the transaction identification associated - with the command execution (if issued by the Client) + with the command execution if issued by the Client 2.4.2 error ----------- -The error response is issued when the command execution could not be -completed because of an error condition. - -The format is: +The format of an error response is: { "error": { "class": json-string, "desc": json-string }, "id": json-value } @@ -114,7 +104,7 @@ The format is: - The "desc" member is a human-readable error message. Clients should not attempt to parse this message. - The "id" member contains the transaction identification associated with - the command execution (if issued by the Client) + the command execution if issued by the Client NOTE: Some errors can occur before the Server is able to read the "id" member, in these cases the "id" member will not be part of the error response, even @@ -124,9 +114,9 @@ if provided by the client. ----------------------- As a result of state changes, the Server may send messages unilaterally -to the Client at any time. They are called 'asynchronous events'. +to the Client at any time. They are called "asynchronous events". -The format is: +The format of asynchronous events is: { "event": json-string, "data": json-object, "timestamp": { "seconds": json-number, "microseconds": json-number } } @@ -147,36 +137,37 @@ qmp-events.txt file. =============== This section provides some examples of real QMP usage, in all of them -'C' stands for 'Client' and 'S' stands for 'Server'. +"C" stands for "Client" and "S" stands for "Server". 3.1 Server greeting ------------------- -S: {"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}} +S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 }, + "package": ""}, "capabilities": []}} 3.2 Simple 'stop' execution --------------------------- C: { "execute": "stop" } -S: {"return": {}} +S: { "return": {} } 3.3 KVM information ------------------- C: { "execute": "query-kvm", "id": "example" } -S: {"return": {"enabled": true, "present": true}, "id": "example"} +S: { "return": { "enabled": true, "present": true }, "id": "example"} 3.4 Parsing error ------------------ C: { "execute": } -S: {"error": {"class": "GenericError", "desc": "Invalid JSON syntax" } } +S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } } 3.5 Powerdown event ------------------- -S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event": -"POWERDOWN"} +S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, + "event": "POWERDOWN" } 4. Capabilities Negotiation ---------------------------- @@ -184,17 +175,17 @@ S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event": When a Client successfully establishes a connection, the Server is in Capabilities Negotiation mode. -In this mode only the 'qmp_capabilities' command is allowed to run, all -other commands will return the CommandNotFound error. Asynchronous messages -are not delivered either. +In this mode only the qmp_capabilities command is allowed to run, all +other commands will return the CommandNotFound error. Asynchronous +messages are not delivered either. -Clients should use the 'qmp_capabilities' command to enable capabilities +Clients should use the qmp_capabilities command to enable capabilities advertised in the Server's greeting (section '2.2 Server Greeting') they support. -When the 'qmp_capabilities' command is issued, and if it does not return an +When the qmp_capabilities command is issued, and if it does not return an error, the Server enters in Command mode where capabilities changes take -effect, all commands (except 'qmp_capabilities') are allowed and asynchronous +effect, all commands (except qmp_capabilities) are allowed and asynchronous messages are delivered. 5 Compatibility Considerations @@ -245,7 +236,7 @@ arguments, errors, asynchronous events, and so forth. Any new names downstream wishes to add must begin with '__'. To ensure compatibility with other downstreams, it is strongly -recommended that you prefix your downstram names with '__RFQDN_' where +recommended that you prefix your downstream names with '__RFQDN_' where RFQDN is a valid, reverse fully qualified domain name which you control. For example, a qemu-kvm specific monitor command would be: diff --git a/docs/specs/acpi_cpu_hotplug.txt b/docs/specs/acpi_cpu_hotplug.txt index 5dec0c5010..f6f577457d 100644 --- a/docs/specs/acpi_cpu_hotplug.txt +++ b/docs/specs/acpi_cpu_hotplug.txt @@ -10,7 +10,7 @@ ACPI GPE block (IO ports 0xafe0-0xafe3, byte access): Generic ACPI GPE block. Bit 2 (GPE.2) used to notify CPU hot-add/remove event to ACPI BIOS, via SCI interrupt. -CPU present bitmap (IO port 0xaf00-0xae1f, 1-byte access): +CPU present bitmap (IO port 0xaf00-0xaf1f, 1-byte access): --------------------------------------------------------------- One bit per CPU. Bit position reflects corresponding CPU APIC ID. Read-only. @@ -749,6 +749,18 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, uint16_t section); static subpage_t *subpage_init(AddressSpace *as, hwaddr base); +static void *(*phys_mem_alloc)(ram_addr_t size) = qemu_anon_ram_alloc; + +/* + * Set a custom physical guest memory alloator. + * Accelerators with unusual needs may need this. Hopefully, we can + * get rid of it eventually. + */ +void phys_mem_set_alloc(void *(*alloc)(ram_addr_t)) +{ + phys_mem_alloc = alloc; +} + static uint16_t phys_section_add(MemoryRegionSection *section) { /* The physical section number is ORed with a page-aligned @@ -880,7 +892,7 @@ void qemu_mutex_unlock_ramlist(void) qemu_mutex_unlock(&ram_list.mutex); } -#if defined(__linux__) && !defined(TARGET_S390X) +#ifdef __linux__ #include <sys/vfs.h> @@ -983,6 +995,14 @@ static void *file_ram_alloc(RAMBlock *block, block->fd = fd; return area; } +#else +static void *file_ram_alloc(RAMBlock *block, + ram_addr_t memory, + const char *path) +{ + fprintf(stderr, "-mem-path not supported on this host\n"); + exit(1); +} #endif static ram_addr_t find_ram_offset(ram_addr_t size) @@ -1099,6 +1119,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, size = TARGET_PAGE_ALIGN(size); new_block = g_malloc0(sizeof(*new_block)); + new_block->fd = -1; /* This assumes the iothread lock is taken here too. */ qemu_mutex_lock_ramlist(); @@ -1107,26 +1128,32 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, if (host) { new_block->host = host; new_block->flags |= RAM_PREALLOC_MASK; + } else if (xen_enabled()) { + if (mem_path) { + fprintf(stderr, "-mem-path not supported with Xen\n"); + exit(1); + } + xen_ram_alloc(new_block->offset, size, mr); } else { if (mem_path) { -#if defined (__linux__) && !defined(TARGET_S390X) + if (phys_mem_alloc != qemu_anon_ram_alloc) { + /* + * file_ram_alloc() needs to allocate just like + * phys_mem_alloc, but we haven't bothered to provide + * a hook there. + */ + fprintf(stderr, + "-mem-path not supported with this accelerator\n"); + exit(1); + } new_block->host = file_ram_alloc(new_block, size, mem_path); + } + if (!new_block->host) { + new_block->host = phys_mem_alloc(size); if (!new_block->host) { - new_block->host = qemu_anon_ram_alloc(size); - memory_try_enable_merging(new_block->host, size); - } -#else - fprintf(stderr, "-mem-path option unsupported\n"); - exit(1); -#endif - } else { - if (xen_enabled()) { - xen_ram_alloc(new_block->offset, size, mr); - } else if (kvm_enabled()) { - /* some s390/kvm configurations have special constraints */ - new_block->host = kvm_ram_alloc(size); - } else { - new_block->host = qemu_anon_ram_alloc(size); + fprintf(stderr, "Cannot set up guest memory '%s': %s\n", + new_block->mr->name, strerror(errno)); + exit(1); } memory_try_enable_merging(new_block->host, size); } @@ -1157,6 +1184,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, qemu_ram_setup_dump(new_block->host, size); qemu_madvise(new_block->host, size, QEMU_MADV_HUGEPAGE); + qemu_madvise(new_block->host, size, QEMU_MADV_DONTFORK); if (kvm_enabled()) kvm_setup_guest_memory(new_block->host, size); @@ -1200,23 +1228,15 @@ void qemu_ram_free(ram_addr_t addr) ram_list.version++; if (block->flags & RAM_PREALLOC_MASK) { ; - } else if (mem_path) { -#if defined (__linux__) && !defined(TARGET_S390X) - if (block->fd) { - munmap(block->host, block->length); - close(block->fd); - } else { - qemu_anon_ram_free(block->host, block->length); - } -#else - abort(); + } else if (xen_enabled()) { + xen_invalidate_map_cache_entry(block->host); +#ifndef _WIN32 + } else if (block->fd >= 0) { + munmap(block->host, block->length); + close(block->fd); #endif } else { - if (xen_enabled()) { - xen_invalidate_map_cache_entry(block->host); - } else { - qemu_anon_ram_free(block->host, block->length); - } + qemu_anon_ram_free(block->host, block->length); } g_free(block); break; @@ -1240,38 +1260,31 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) vaddr = block->host + offset; if (block->flags & RAM_PREALLOC_MASK) { ; + } else if (xen_enabled()) { + abort(); } else { flags = MAP_FIXED; munmap(vaddr, length); - if (mem_path) { -#if defined(__linux__) && !defined(TARGET_S390X) - if (block->fd) { + if (block->fd >= 0) { #ifdef MAP_POPULATE - flags |= mem_prealloc ? MAP_POPULATE | MAP_SHARED : - MAP_PRIVATE; + flags |= mem_prealloc ? MAP_POPULATE | MAP_SHARED : + MAP_PRIVATE; #else - flags |= MAP_PRIVATE; -#endif - area = mmap(vaddr, length, PROT_READ | PROT_WRITE, - flags, block->fd, offset); - } else { - flags |= MAP_PRIVATE | MAP_ANONYMOUS; - area = mmap(vaddr, length, PROT_READ | PROT_WRITE, - flags, -1, 0); - } -#else - abort(); + flags |= MAP_PRIVATE; #endif + area = mmap(vaddr, length, PROT_READ | PROT_WRITE, + flags, block->fd, offset); } else { -#if defined(TARGET_S390X) && defined(CONFIG_KVM) - flags |= MAP_SHARED | MAP_ANONYMOUS; - area = mmap(vaddr, length, PROT_EXEC|PROT_READ|PROT_WRITE, - flags, -1, 0); -#else + /* + * Remap needs to match alloc. Accelerators that + * set phys_mem_alloc never remap. If they did, + * we'd need a remap hook here. + */ + assert(phys_mem_alloc == qemu_anon_ram_alloc); + flags |= MAP_PRIVATE | MAP_ANONYMOUS; area = mmap(vaddr, length, PROT_READ | PROT_WRITE, flags, -1, 0); -#endif } if (area != vaddr) { fprintf(stderr, "Could not remap addr: " diff --git a/hmp-commands.hx b/hmp-commands.hx index 65b7f6076c..caae5ad9e9 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1023,8 +1023,7 @@ ETEXI "of device. If a new image file is specified, the\n\t\t\t" "new image file will become the new root image.\n\t\t\t" "If format is specified, the snapshot file will\n\t\t\t" - "be created in that format. Otherwise the\n\t\t\t" - "snapshot will be internal! (currently unsupported).\n\t\t\t" + "be created in that format.\n\t\t\t" "The default format is qcow2. The -n flag requests QEMU\n\t\t\t" "to reuse the image found in new-image-file, instead of\n\t\t\t" "recreating it from scratch.", @@ -1038,6 +1037,40 @@ Snapshot device, using snapshot file as target if provided ETEXI { + .name = "snapshot_blkdev_internal", + .args_type = "device:B,name:s", + .params = "device name", + .help = "take an internal snapshot of device.\n\t\t\t" + "The format of the image used by device must\n\t\t\t" + "support it, such as qcow2.\n\t\t\t", + .mhandler.cmd = hmp_snapshot_blkdev_internal, + }, + +STEXI +@item snapshot_blkdev_internal +@findex snapshot_blkdev_internal +Take an internal snapshot on device if it support +ETEXI + + { + .name = "snapshot_delete_blkdev_internal", + .args_type = "device:B,name:s,id:s?", + .params = "device name [id]", + .help = "delete an internal snapshot of device.\n\t\t\t" + "If id is specified, qemu will try delete\n\t\t\t" + "the snapshot matching both id and name.\n\t\t\t" + "The format of the image used by device must\n\t\t\t" + "support it, such as qcow2.\n\t\t\t", + .mhandler.cmd = hmp_snapshot_delete_blkdev_internal, + }, + +STEXI +@item snapshot_delete_blkdev_internal +@findex snapshot_delete_blkdev_internal +Delete an internal snapshot on device if it support +ETEXI + + { .name = "drive_mirror", .args_type = "reuse:-n,full:-f,device:B,target:s,format:s?", .params = "[-n] [-f] device target [format]", @@ -978,6 +978,28 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &errp); } +void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_str(qdict, "name"); + Error *errp = NULL; + + qmp_blockdev_snapshot_internal_sync(device, name, &errp); + hmp_handle_error(mon, &errp); +} + +void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_str(qdict, "name"); + const char *id = qdict_get_try_str(qdict, "id"); + Error *errp = NULL; + + qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id, + true, name, &errp); + hmp_handle_error(mon, &errp); +} + void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) { qmp_migrate_cancel(NULL); @@ -1141,7 +1163,7 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) qmp_block_stream(device, base != NULL, base, qdict_haskey(qdict, "speed"), speed, - BLOCKDEV_ON_ERROR_REPORT, true, &error); + true, BLOCKDEV_ON_ERROR_REPORT, &error); hmp_handle_error(mon, &error); } @@ -54,6 +54,8 @@ void hmp_block_passwd(Monitor *mon, const QDict *qdict); void hmp_balloon(Monitor *mon, const QDict *qdict); void hmp_block_resize(Monitor *mon, const QDict *qdict); void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict); +void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict); +void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict); void hmp_drive_mirror(Monitor *mon, const QDict *qdict); void hmp_drive_backup(Monitor *mon, const QDict *qdict); void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 7467b88e27..7138139d32 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -324,12 +324,13 @@ static void acpi_notify_wakeup(Notifier *notifier, void *data) (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS); break; case QEMU_WAKEUP_REASON_OTHER: - default: /* ACPI_BITMASK_WAKE_STATUS should be set on resume. Pretend that resume was caused by power button */ ar->pm1.evt.sts |= (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS); break; + default: + break; } } diff --git a/hw/audio/hda-codec-common.h b/hw/audio/hda-codec-common.h new file mode 100644 index 0000000000..b4fdb51e8b --- /dev/null +++ b/hw/audio/hda-codec-common.h @@ -0,0 +1,456 @@ +/* + * Common code to disable/enable mixer emulation at run time + * + * Copyright (C) 2013 Red Hat, Inc. + * + * Written by Bandan Das <bsd@redhat.com> + * with important bits picked up from hda-codec.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * HDA codec descriptions + */ + +#ifdef HDA_MIXER +#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x12) +#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x22) +#define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x32) +#define QEMU_HDA_AMP_CAPS \ + (AC_AMPCAP_MUTE | \ + (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \ + (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \ + (3 << AC_AMPCAP_STEP_SIZE_SHIFT)) +#else +#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x11) +#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x21) +#define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x31) +#define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE +#endif + + +/* common: audio output widget */ +static const desc_param glue(common_params_audio_dac_, PARAM)[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_FORMAT_OVRD | + AC_WCAP_AMP_OVRD | + AC_WCAP_OUT_AMP | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_CAPS, + }, +}; + +/* common: audio input widget */ +static const desc_param glue(common_params_audio_adc_, PARAM)[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_CONN_LIST | + AC_WCAP_FORMAT_OVRD | + AC_WCAP_AMP_OVRD | + AC_WCAP_IN_AMP | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_CONNLIST_LEN, + .val = 1, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_CAPS, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* common: pin widget (line-out) */ +static const desc_param glue(common_params_audio_lineout_, PARAM)[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_CONN_LIST | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PIN_CAP, + .val = AC_PINCAP_OUT, + },{ + .id = AC_PAR_CONNLIST_LEN, + .val = 1, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* common: pin widget (line-in) */ +static const desc_param glue(common_params_audio_linein_, PARAM)[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PIN_CAP, + .val = AC_PINCAP_IN, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* output: root node */ +static const desc_param glue(output_params_root_, PARAM)[] = { + { + .id = AC_PAR_VENDOR_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_REV_ID, + .val = 0x00100101, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00010001, + }, +}; + +/* output: audio function */ +static const desc_param glue(output_params_audio_func_, PARAM)[] = { + { + .id = AC_PAR_FUNCTION_TYPE, + .val = AC_GRP_AUDIO_FUNCTION, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00020002, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_GPIO_CAP, + .val = 0, + },{ + .id = AC_PAR_AUDIO_FG_CAP, + .val = 0x00000808, + },{ + .id = AC_PAR_POWER_STATE, + .val = 0, + }, +}; + +/* output: nodes */ +static const desc_node glue(output_nodes_, PARAM)[] = { + { + .nid = AC_NODE_ROOT, + .name = "root", + .params = glue(output_params_root_, PARAM), + .nparams = ARRAY_SIZE(glue(output_params_root_, PARAM)), + },{ + .nid = 1, + .name = "func", + .params = glue(output_params_audio_func_, PARAM), + .nparams = ARRAY_SIZE(glue(output_params_audio_func_, PARAM)), + },{ + .nid = 2, + .name = "dac", + .params = glue(common_params_audio_dac_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), + .stindex = 0, + },{ + .nid = 3, + .name = "out", + .params = glue(common_params_audio_lineout_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | + 0x10), + .pinctl = AC_PINCTL_OUT_EN, + .conn = (uint32_t[]) { 2 }, + } +}; + +/* output: codec */ +static const desc_codec glue(output_, PARAM) = { + .name = "output", + .iid = QEMU_HDA_ID_OUTPUT, + .nodes = glue(output_nodes_, PARAM), + .nnodes = ARRAY_SIZE(glue(output_nodes_, PARAM)), +}; + +/* duplex: root node */ +static const desc_param glue(duplex_params_root_, PARAM)[] = { + { + .id = AC_PAR_VENDOR_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_REV_ID, + .val = 0x00100101, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00010001, + }, +}; + +/* duplex: audio function */ +static const desc_param glue(duplex_params_audio_func_, PARAM)[] = { + { + .id = AC_PAR_FUNCTION_TYPE, + .val = AC_GRP_AUDIO_FUNCTION, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00020004, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_GPIO_CAP, + .val = 0, + },{ + .id = AC_PAR_AUDIO_FG_CAP, + .val = 0x00000808, + },{ + .id = AC_PAR_POWER_STATE, + .val = 0, + }, +}; + +/* duplex: nodes */ +static const desc_node glue(duplex_nodes_, PARAM)[] = { + { + .nid = AC_NODE_ROOT, + .name = "root", + .params = glue(duplex_params_root_, PARAM), + .nparams = ARRAY_SIZE(glue(duplex_params_root_, PARAM)), + },{ + .nid = 1, + .name = "func", + .params = glue(duplex_params_audio_func_, PARAM), + .nparams = ARRAY_SIZE(glue(duplex_params_audio_func_, PARAM)), + },{ + .nid = 2, + .name = "dac", + .params = glue(common_params_audio_dac_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), + .stindex = 0, + },{ + .nid = 3, + .name = "out", + .params = glue(common_params_audio_lineout_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | + 0x10), + .pinctl = AC_PINCTL_OUT_EN, + .conn = (uint32_t[]) { 2 }, + },{ + .nid = 4, + .name = "adc", + .params = glue(common_params_audio_adc_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_adc_, PARAM)), + .stindex = 1, + .conn = (uint32_t[]) { 5 }, + },{ + .nid = 5, + .name = "in", + .params = glue(common_params_audio_linein_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_linein_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | + 0x20), + .pinctl = AC_PINCTL_IN_EN, + } +}; + +/* duplex: codec */ +static const desc_codec glue(duplex_, PARAM) = { + .name = "duplex", + .iid = QEMU_HDA_ID_DUPLEX, + .nodes = glue(duplex_nodes_, PARAM), + .nnodes = ARRAY_SIZE(glue(duplex_nodes_, PARAM)), +}; + +/* micro: root node */ +static const desc_param glue(micro_params_root_, PARAM)[] = { + { + .id = AC_PAR_VENDOR_ID, + .val = QEMU_HDA_ID_MICRO, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_MICRO, + },{ + .id = AC_PAR_REV_ID, + .val = 0x00100101, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00010001, + }, +}; + +/* micro: audio function */ +static const desc_param glue(micro_params_audio_func_, PARAM)[] = { + { + .id = AC_PAR_FUNCTION_TYPE, + .val = AC_GRP_AUDIO_FUNCTION, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_MICRO, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00020004, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_GPIO_CAP, + .val = 0, + },{ + .id = AC_PAR_AUDIO_FG_CAP, + .val = 0x00000808, + },{ + .id = AC_PAR_POWER_STATE, + .val = 0, + }, +}; + +/* micro: nodes */ +static const desc_node glue(micro_nodes_, PARAM)[] = { + { + .nid = AC_NODE_ROOT, + .name = "root", + .params = glue(micro_params_root_, PARAM), + .nparams = ARRAY_SIZE(glue(micro_params_root_, PARAM)), + },{ + .nid = 1, + .name = "func", + .params = glue(micro_params_audio_func_, PARAM), + .nparams = ARRAY_SIZE(glue(micro_params_audio_func_, PARAM)), + },{ + .nid = 2, + .name = "dac", + .params = glue(common_params_audio_dac_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), + .stindex = 0, + },{ + .nid = 3, + .name = "out", + .params = glue(common_params_audio_lineout_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_SPEAKER << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | + 0x10), + .pinctl = AC_PINCTL_OUT_EN, + .conn = (uint32_t[]) { 2 }, + },{ + .nid = 4, + .name = "adc", + .params = glue(common_params_audio_adc_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_adc_, PARAM)), + .stindex = 1, + .conn = (uint32_t[]) { 5 }, + },{ + .nid = 5, + .name = "in", + .params = glue(common_params_audio_linein_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_linein_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | + 0x20), + .pinctl = AC_PINCTL_IN_EN, + } +}; + +/* micro: codec */ +static const desc_codec glue(micro_, PARAM) = { + .name = "micro", + .iid = QEMU_HDA_ID_MICRO, + .nodes = glue(micro_nodes_, PARAM), + .nnodes = ARRAY_SIZE(glue(micro_nodes_, PARAM)), +}; + +#undef PARAM +#undef HDA_MIXER +#undef QEMU_HDA_ID_OUTPUT +#undef QEMU_HDA_ID_DUPLEX +#undef QEMU_HDA_ID_MICRO +#undef QEMU_HDA_AMP_CAPS diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 9550c97e65..07a43bfe89 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -118,428 +118,12 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) #define QEMU_HDA_AMP_NONE (0) #define QEMU_HDA_AMP_STEPS 0x4a -#ifdef CONFIG_MIXEMU -# define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x12) -# define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x22) -# define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x32) -# define QEMU_HDA_AMP_CAPS \ - (AC_AMPCAP_MUTE | \ - (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \ - (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \ - (3 << AC_AMPCAP_STEP_SIZE_SHIFT)) -#else -# define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x11) -# define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x21) -# define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x31) -# define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE -#endif - -/* common: audio output widget */ -static const desc_param common_params_audio_dac[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_FORMAT_OVRD | - AC_WCAP_AMP_OVRD | - AC_WCAP_OUT_AMP | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_CAPS, - }, -}; - -/* common: audio input widget */ -static const desc_param common_params_audio_adc[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_CONN_LIST | - AC_WCAP_FORMAT_OVRD | - AC_WCAP_AMP_OVRD | - AC_WCAP_IN_AMP | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_CONNLIST_LEN, - .val = 1, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_CAPS, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* common: pin widget (line-out) */ -static const desc_param common_params_audio_lineout[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_CONN_LIST | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PIN_CAP, - .val = AC_PINCAP_OUT, - },{ - .id = AC_PAR_CONNLIST_LEN, - .val = 1, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* common: pin widget (line-in) */ -static const desc_param common_params_audio_linein[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PIN_CAP, - .val = AC_PINCAP_IN, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* output: root node */ -static const desc_param output_params_root[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; +#define PARAM mixemu +#define HDA_MIXER +#include "hda-codec-common.h" -/* output: audio function */ -static const desc_param output_params_audio_func[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020002, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* output: nodes */ -static const desc_node output_nodes[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = output_params_root, - .nparams = ARRAY_SIZE(output_params_root), - },{ - .nid = 1, - .name = "func", - .params = output_params_audio_func, - .nparams = ARRAY_SIZE(output_params_audio_func), - },{ - .nid = 2, - .name = "dac", - .params = common_params_audio_dac, - .nparams = ARRAY_SIZE(common_params_audio_dac), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = common_params_audio_lineout, - .nparams = ARRAY_SIZE(common_params_audio_lineout), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - } -}; - -/* output: codec */ -static const desc_codec output = { - .name = "output", - .iid = QEMU_HDA_ID_OUTPUT, - .nodes = output_nodes, - .nnodes = ARRAY_SIZE(output_nodes), -}; - -/* duplex: root node */ -static const desc_param duplex_params_root[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; - -/* duplex: audio function */ -static const desc_param duplex_params_audio_func[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020004, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* duplex: nodes */ -static const desc_node duplex_nodes[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = duplex_params_root, - .nparams = ARRAY_SIZE(duplex_params_root), - },{ - .nid = 1, - .name = "func", - .params = duplex_params_audio_func, - .nparams = ARRAY_SIZE(duplex_params_audio_func), - },{ - .nid = 2, - .name = "dac", - .params = common_params_audio_dac, - .nparams = ARRAY_SIZE(common_params_audio_dac), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = common_params_audio_lineout, - .nparams = ARRAY_SIZE(common_params_audio_lineout), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - },{ - .nid = 4, - .name = "adc", - .params = common_params_audio_adc, - .nparams = ARRAY_SIZE(common_params_audio_adc), - .stindex = 1, - .conn = (uint32_t[]) { 5 }, - },{ - .nid = 5, - .name = "in", - .params = common_params_audio_linein, - .nparams = ARRAY_SIZE(common_params_audio_linein), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | - 0x20), - .pinctl = AC_PINCTL_IN_EN, - } -}; - -/* duplex: codec */ -static const desc_codec duplex = { - .name = "duplex", - .iid = QEMU_HDA_ID_DUPLEX, - .nodes = duplex_nodes, - .nnodes = ARRAY_SIZE(duplex_nodes), -}; - -/* micro: root node */ -static const desc_param micro_params_root[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; - -/* micro: audio function */ -static const desc_param micro_params_audio_func[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020004, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* micro: nodes */ -static const desc_node micro_nodes[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = micro_params_root, - .nparams = ARRAY_SIZE(micro_params_root), - },{ - .nid = 1, - .name = "func", - .params = micro_params_audio_func, - .nparams = ARRAY_SIZE(micro_params_audio_func), - },{ - .nid = 2, - .name = "dac", - .params = common_params_audio_dac, - .nparams = ARRAY_SIZE(common_params_audio_dac), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = common_params_audio_lineout, - .nparams = ARRAY_SIZE(common_params_audio_lineout), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_SPEAKER << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - },{ - .nid = 4, - .name = "adc", - .params = common_params_audio_adc, - .nparams = ARRAY_SIZE(common_params_audio_adc), - .stindex = 1, - .conn = (uint32_t[]) { 5 }, - },{ - .nid = 5, - .name = "in", - .params = common_params_audio_linein, - .nparams = ARRAY_SIZE(common_params_audio_linein), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | - 0x20), - .pinctl = AC_PINCTL_IN_EN, - } -}; - -/* micro: codec */ -static const desc_codec micro = { - .name = "micro", - .iid = QEMU_HDA_ID_MICRO, - .nodes = micro_nodes, - .nnodes = ARRAY_SIZE(micro_nodes), -}; +#define PARAM nomixemu +#include "hda-codec-common.h" /* -------------------------------------------------------------------------- */ @@ -585,6 +169,7 @@ struct HDAAudioState { /* properties */ uint32_t debug; + bool mixer; }; static void hda_audio_input_cb(void *opaque, int avail) @@ -1006,23 +591,42 @@ static const VMStateDescription vmstate_hda_audio = { }; static Property hda_audio_properties[] = { - DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), + DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), + DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), DEFINE_PROP_END_OF_LIST(), }; static int hda_audio_init_output(HDACodecDevice *hda) { - return hda_audio_init(hda, &output); + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + + if (!a->mixer) { + return hda_audio_init(hda, &output_nomixemu); + } else { + return hda_audio_init(hda, &output_mixemu); + } } static int hda_audio_init_duplex(HDACodecDevice *hda) { - return hda_audio_init(hda, &duplex); + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + + if (!a->mixer) { + return hda_audio_init(hda, &duplex_nomixemu); + } else { + return hda_audio_init(hda, &duplex_mixemu); + } } static int hda_audio_init_micro(HDACodecDevice *hda) { - return hda_audio_init(hda, µ); + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + + if (!a->mixer) { + return hda_audio_init(hda, µ_nomixemu); + } else { + return hda_audio_init(hda, µ_mixemu); + } } static void hda_audio_output_class_init(ObjectClass *klass, void *data) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index e2f55cc946..49a23c33f7 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -460,9 +460,9 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, static void virtio_blk_reset(VirtIODevice *vdev) { -#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE VirtIOBlock *s = VIRTIO_BLK(vdev); +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE if (s->dataplane) { virtio_blk_data_plane_stop(s->dataplane); } @@ -473,6 +473,7 @@ static void virtio_blk_reset(VirtIODevice *vdev) * are per-device request lists. */ bdrv_drain_all(); + bdrv_set_enable_write_cache(s->bs, s->original_wce); } /* coalesce internal state, copy to pci i/o region 0 @@ -564,7 +565,25 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) } features = vdev->guest_features; - bdrv_set_enable_write_cache(s->bs, !!(features & (1 << VIRTIO_BLK_F_WCE))); + + /* A guest that supports VIRTIO_BLK_F_CONFIG_WCE must be able to send + * cache flushes. Thus, the "auto writethrough" behavior is never + * necessary for guests that support the VIRTIO_BLK_F_CONFIG_WCE feature. + * Leaving it enabled would break the following sequence: + * + * Guest started with "-drive cache=writethrough" + * Guest sets status to 0 + * Guest sets DRIVER bit in status field + * Guest reads host features (WCE=0, CONFIG_WCE=1) + * Guest writes guest features (WCE=0, CONFIG_WCE=1) + * Guest writes 1 to the WCE configuration field (writeback mode) + * Guest sets DRIVER_OK bit in status field + * + * s->bs would erroneously be placed in writethrough mode. + */ + if (!(features & (1 << VIRTIO_BLK_F_CONFIG_WCE))) { + bdrv_set_enable_write_cache(s->bs, !!(features & (1 << VIRTIO_BLK_F_WCE))); + } } static void virtio_blk_save(QEMUFile *f, void *opaque) @@ -674,6 +693,7 @@ static int virtio_blk_device_init(VirtIODevice *vdev) } blkconf_serial(&blk->conf, &blk->serial); + s->original_wce = bdrv_enable_write_cache(blk->conf.bs); if (blkconf_geometry(&blk->conf, NULL, 65535, 255, 255) < 0) { return -1; } diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 668cc069ff..f35fc5944a 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -809,10 +809,15 @@ static int blk_connect(struct XenDevice *xendev) xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->bs = bdrv_new(blkdev->dev); if (blkdev->bs) { + Error *local_err = NULL; BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto, readonly); if (bdrv_open(blkdev->bs, - blkdev->filename, NULL, qflags, drv) != 0) { + blkdev->filename, NULL, qflags, drv, &local_err) != 0) + { + xen_be_printf(&blkdev->xendev, 0, "error: %s\n", + error_get_pretty(local_err)); + error_free(local_err); bdrv_unref(blkdev->bs); blkdev->bs = NULL; } diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index f8f3dbca3e..cbd6a006f4 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -22,6 +22,6 @@ common-obj-$(CONFIG_IMX) += imx_serial.o common-obj-$(CONFIG_LM32) += lm32_juart.o common-obj-$(CONFIG_LM32) += lm32_uart.o common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o -common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o +common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o sclpconsole-lm.o obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c new file mode 100644 index 0000000000..93390675d6 --- /dev/null +++ b/hw/char/sclpconsole-lm.c @@ -0,0 +1,398 @@ +/* + * SCLP event types + * Operations Command - Line Mode input + * Message - Line Mode output + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Heinz Graalfs <graalfs@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include "hw/qdev.h" +#include "qemu/thread.h" +#include "qemu/error-report.h" +#include "sysemu/char.h" + +#include "hw/s390x/sclp.h" +#include "hw/s390x/event-facility.h" +#include "hw/s390x/ebcdic.h" + +#define SIZE_BUFFER 4096 +#define NEWLINE "\n" + +typedef struct OprtnsCommand { + EventBufferHeader header; + MDMSU message_unit; + char data[0]; +} QEMU_PACKED OprtnsCommand; + +/* max size for line-mode data in 4K SCCB page */ +#define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand)) + +typedef struct SCLPConsoleLM { + SCLPEvent event; + CharDriverState *chr; + bool echo; /* immediate echo of input if true */ + uint32_t write_errors; /* errors writing to char layer */ + uint32_t length; /* length of byte stream in buffer */ + uint8_t buf[SIZE_CONSOLE_BUFFER]; + qemu_irq irq_console_read; +} SCLPConsoleLM; + +/* +* Character layer call-back functions + * + * Allow 1 character at a time + * + * Accumulate bytes from character layer in console buffer, + * event_pending is set when a newline character is encountered + * + * The maximum command line length is limited by the maximum + * space available in an SCCB + */ + +static int chr_can_read(void *opaque) +{ + SCLPConsoleLM *scon = opaque; + + if (scon->event.event_pending) { + return 0; + } else if (SIZE_CONSOLE_BUFFER - scon->length) { + return 1; + } + return 0; +} + +static void receive_from_chr_layer(SCLPConsoleLM *scon, const uint8_t *buf, + int size) +{ + assert(size == 1); + + if (*buf == '\r' || *buf == '\n') { + scon->event.event_pending = true; + return; + } + scon->buf[scon->length] = *buf; + scon->length += 1; + if (scon->echo) { + qemu_chr_fe_write(scon->chr, buf, size); + } +} + +/* + * Send data from a char device over to the guest + */ +static void chr_read(void *opaque, const uint8_t *buf, int size) +{ + SCLPConsoleLM *scon = opaque; + + receive_from_chr_layer(scon, buf, size); + if (scon->event.event_pending) { + /* trigger SCLP read operation */ + qemu_irq_raise(scon->irq_console_read); + } +} + +/* functions to be called by event facility */ + +static bool can_handle_event(uint8_t type) +{ + return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD; +} + +static unsigned int send_mask(void) +{ + return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD; +} + +static unsigned int receive_mask(void) +{ + return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD; +} + +/* + * Triggered by SCLP's read_event_data + * - convert ASCII byte stream to EBCDIC and + * - copy converted data into provided (SCLP) buffer + */ +static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, + int avail) +{ + int len; + + SCLPConsoleLM *cons = DO_UPCAST(SCLPConsoleLM, event, event); + + len = cons->length; + /* data need to fit into provided SCLP buffer */ + if (len > avail) { + return 1; + } + + ebcdic_put(buf, (char *)&cons->buf, len); + *size = len; + cons->length = 0; + /* data provided and no more data pending */ + event->event_pending = false; + return 0; +} + +static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, + int *slen) +{ + int avail, rc; + size_t src_len; + uint8_t *to; + OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr; + + if (!event->event_pending) { + /* no data pending */ + return 0; + } + + to = (uint8_t *)&oc->data; + avail = *slen - sizeof(OprtnsCommand); + rc = get_console_data(event, to, &src_len, avail); + if (rc) { + /* data didn't fit, try next SCCB */ + return 1; + } + + oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU; + oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU)); + + oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU; + oc->message_unit.cpmsu.length = + cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector)); + + oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD; + oc->message_unit.text_command.length = + cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector))); + + oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG; + oc->message_unit.self_def_text_message.length = + cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector))); + + oc->message_unit.text_message.key = GDS_KEY_TEXTMSG; + oc->message_unit.text_message.length = + cpu_to_be16(sizeof(GdsSubvector) + src_len); + + oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len); + oc->header.type = SCLP_EVENT_OPRTNS_COMMAND; + *slen = avail - src_len; + + return 1; +} + +/* + * Triggered by SCLP's write_event_data + * - write console data to character layer + * returns < 0 if an error occurred + */ +static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) +{ + int ret = 0; + const uint8_t *buf_offset; + + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + + if (!scon->chr) { + /* If there's no backend, we can just say we consumed all data. */ + return len; + } + + buf_offset = buf; + while (len > 0) { + ret = qemu_chr_fe_write(scon->chr, buf, len); + if (ret == 0) { + /* a pty doesn't seem to be connected - no error */ + len = 0; + } else if (ret == -EAGAIN || (ret > 0 && ret < len)) { + len -= ret; + buf_offset += ret; + } else { + len = 0; + } + } + + return ret; +} + +static int process_mdb(SCLPEvent *event, MDBO *mdbo) +{ + int rc; + int len; + uint8_t buffer[SIZE_BUFFER]; + + len = be16_to_cpu(mdbo->length); + len -= sizeof(mdbo->length) + sizeof(mdbo->type) + + sizeof(mdbo->mto.line_type_flags) + + sizeof(mdbo->mto.alarm_control) + + sizeof(mdbo->mto._reserved); + + assert(len <= SIZE_BUFFER); + + /* convert EBCDIC SCLP contents to ASCII console message */ + ascii_put(buffer, mdbo->mto.message, len); + rc = write_console_data(event, (uint8_t *)NEWLINE, 1); + if (rc < 0) { + return rc; + } + return write_console_data(event, buffer, len); +} + +static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) +{ + int len; + int written; + int errors = 0; + MDBO *mdbo; + SclpMsg *data = (SclpMsg *) ebh; + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + + len = be16_to_cpu(data->mdb.header.length); + if (len < sizeof(data->mdb.header)) { + return SCLP_RC_INCONSISTENT_LENGTHS; + } + len -= sizeof(data->mdb.header); + + /* first check message buffers */ + mdbo = data->mdb.mdbo; + while (len > 0) { + if (be16_to_cpu(mdbo->length) > len + || be16_to_cpu(mdbo->length) == 0) { + return SCLP_RC_INCONSISTENT_LENGTHS; + } + len -= be16_to_cpu(mdbo->length); + mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); + } + + /* then execute */ + len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header); + mdbo = data->mdb.mdbo; + while (len > 0) { + switch (be16_to_cpu(mdbo->type)) { + case MESSAGE_TEXT: + /* message text object */ + written = process_mdb(event, mdbo); + if (written < 0) { + /* character layer error */ + errors++; + } + break; + default: /* ignore */ + break; + } + len -= be16_to_cpu(mdbo->length); + mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); + } + if (errors) { + scon->write_errors += errors; + } + data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED; + + return SCLP_RC_NORMAL_COMPLETION; +} + +static void trigger_console_data(void *opaque, int n, int level) +{ + sclp_service_interrupt(0); +} + +/* functions for live migration */ + +static const VMStateDescription vmstate_sclplmconsole = { + .name = "sclplmconsole", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_BOOL(event.event_pending, SCLPConsoleLM), + VMSTATE_UINT32(write_errors, SCLPConsoleLM), + VMSTATE_UINT32(length, SCLPConsoleLM), + VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER), + VMSTATE_END_OF_LIST() + } +}; + +/* qemu object creation and initialization functions */ + +/* tell character layer our call-back functions */ + +static int console_init(SCLPEvent *event) +{ + static bool console_available; + + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + + if (console_available) { + error_report("Multiple line-mode operator consoles are not supported"); + return -1; + } + console_available = true; + + if (scon->chr) { + qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon); + } + scon->irq_console_read = *qemu_allocate_irqs(trigger_console_data, NULL, 1); + + return 0; +} + +static int console_exit(SCLPEvent *event) +{ + return 0; +} + +static void console_reset(DeviceState *dev) +{ + SCLPEvent *event = SCLP_EVENT(dev); + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + + event->event_pending = false; + scon->length = 0; + scon->write_errors = 0; +} + +static Property console_properties[] = { + DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), + DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), + DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void console_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); + + dc->props = console_properties; + dc->reset = console_reset; + dc->vmsd = &vmstate_sclplmconsole; + ec->init = console_init; + ec->exit = console_exit; + ec->get_send_mask = send_mask; + ec->get_receive_mask = receive_mask; + ec->can_handle_event = can_handle_event; + ec->read_event_data = read_event_data; + ec->write_event_data = write_event_data; +} + +static const TypeInfo sclp_console_info = { + .name = "sclplmconsole", + .parent = TYPE_SCLP_EVENT, + .instance_size = sizeof(SCLPConsoleLM), + .class_init = console_class_init, + .class_size = sizeof(SCLPEventClass), +}; + +static void register_types(void) +{ + type_register_static(&sclp_console_info); +} + +type_init(register_types) diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index eb3988c2e4..16d77c5e27 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -31,12 +31,11 @@ typedef struct ASCIIConsoleData { typedef struct SCLPConsole { SCLPEvent event; CharDriverState *chr; - /* io vector */ - uint8_t *iov; /* iov buffer pointer */ - uint8_t *iov_sclp; /* pointer to SCLP read offset */ - uint8_t *iov_bs; /* pointer byte stream read offset */ - uint32_t iov_data_len; /* length of byte stream in buffer */ - uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ + uint8_t iov[SIZE_BUFFER_VT220]; + uint32_t iov_sclp; /* offset in buf for SCLP read operation */ + uint32_t iov_bs; /* offset in buf for char layer read operation */ + uint32_t iov_data_len; /* length of byte stream in buffer */ + uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ qemu_irq irq_read_vt220; } SCLPConsole; @@ -47,7 +46,7 @@ static int chr_can_read(void *opaque) { SCLPConsole *scon = opaque; - return scon->iov ? SIZE_BUFFER_VT220 - scon->iov_data_len : 0; + return SIZE_BUFFER_VT220 - scon->iov_data_len; } /* Receive n bytes from character layer, save in iov buffer, @@ -55,13 +54,11 @@ static int chr_can_read(void *opaque) static void receive_from_chr_layer(SCLPConsole *scon, const uint8_t *buf, int size) { - assert(scon->iov); - /* read data must fit into current buffer */ assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len); /* put byte-stream from character layer into buffer */ - memcpy(scon->iov_bs, buf, size); + memcpy(&scon->iov[scon->iov_bs], buf, size); scon->iov_data_len += size; scon->iov_sclp_rest += size; scon->iov_bs += size; @@ -80,34 +77,11 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) qemu_irq_raise(scon->irq_read_vt220); } -static void chr_event(void *opaque, int event) -{ - SCLPConsole *scon = opaque; - - switch (event) { - case CHR_EVENT_OPENED: - if (!scon->iov) { - scon->iov = g_malloc0(SIZE_BUFFER_VT220); - scon->iov_sclp = scon->iov; - scon->iov_bs = scon->iov; - scon->iov_data_len = 0; - scon->iov_sclp_rest = 0; - } - break; - case CHR_EVENT_CLOSED: - if (scon->iov) { - g_free(scon->iov); - scon->iov = NULL; - } - break; - } -} - /* functions to be called by event facility */ -static int event_type(void) +static bool can_handle_event(uint8_t type) { - return SCLP_EVENT_ASCII_CONSOLE_DATA; + return type == SCLP_EVENT_ASCII_CONSOLE_DATA; } static unsigned int send_mask(void) @@ -134,17 +108,17 @@ static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, /* if all data fit into provided SCLP buffer */ if (avail >= cons->iov_sclp_rest) { /* copy character byte-stream to SCLP buffer */ - memcpy(buf, cons->iov_sclp, cons->iov_sclp_rest); + memcpy(buf, &cons->iov[cons->iov_sclp], cons->iov_sclp_rest); *size = cons->iov_sclp_rest + 1; - cons->iov_sclp = cons->iov; - cons->iov_bs = cons->iov; + cons->iov_sclp = 0; + cons->iov_bs = 0; cons->iov_data_len = 0; cons->iov_sclp_rest = 0; event->event_pending = false; /* data provided and no more data pending */ } else { /* if provided buffer is too small, just copy part */ - memcpy(buf, cons->iov_sclp, avail); + memcpy(buf, &cons->iov[cons->iov_sclp], avail); *size = avail + 1; cons->iov_sclp_rest -= avail; cons->iov_sclp += avail; @@ -223,9 +197,26 @@ static void trigger_ascii_console_data(void *opaque, int n, int level) sclp_service_interrupt(0); } +static const VMStateDescription vmstate_sclpconsole = { + .name = "sclpconsole", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_BOOL(event.event_pending, SCLPConsole), + VMSTATE_UINT8_ARRAY(iov, SCLPConsole, SIZE_BUFFER_VT220), + VMSTATE_UINT32(iov_sclp, SCLPConsole), + VMSTATE_UINT32(iov_bs, SCLPConsole), + VMSTATE_UINT32(iov_data_len, SCLPConsole), + VMSTATE_UINT32(iov_sclp_rest, SCLPConsole), + VMSTATE_END_OF_LIST() + } +}; + /* qemu object creation and initialization functions */ /* tell character layer our call-back functions */ + static int console_init(SCLPEvent *event) { static bool console_available; @@ -237,10 +228,9 @@ static int console_init(SCLPEvent *event) return -1; } console_available = true; - event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA; if (scon->chr) { qemu_chr_add_handlers(scon->chr, chr_can_read, - chr_read, chr_event, scon); + chr_read, NULL, scon); } scon->irq_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data, NULL, 1); @@ -248,6 +238,18 @@ static int console_init(SCLPEvent *event) return 0; } +static void console_reset(DeviceState *dev) +{ + SCLPEvent *event = SCLP_EVENT(dev); + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + + event->event_pending = false; + scon->iov_sclp = 0; + scon->iov_bs = 0; + scon->iov_data_len = 0; + scon->iov_sclp_rest = 0; +} + static int console_exit(SCLPEvent *event) { return 0; @@ -264,11 +266,13 @@ static void console_class_init(ObjectClass *klass, void *data) SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); dc->props = console_properties; + dc->reset = console_reset; + dc->vmsd = &vmstate_sclpconsole; ec->init = console_init; ec->exit = console_exit; ec->get_send_mask = send_mask; ec->get_receive_mask = receive_mask; - ec->event_type = event_type; + ec->can_handle_event = can_handle_event; ec->read_event_data = read_event_data; ec->write_event_data = write_event_data; } diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 6e9fb3b695..540df82600 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -31,5 +31,4 @@ obj-$(CONFIG_TCX) += tcx.o obj-$(CONFIG_VGA) += vga.o -common-obj-$(CONFIG_QXL) += qxl-logger.o qxl-render.o -obj-$(CONFIG_QXL) += qxl.o +common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o diff --git a/hw/display/qxl.c b/hw/display/qxl.c index c50e285e21..ee2db0da1a 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -313,9 +313,7 @@ static ram_addr_t qxl_rom_size(void) sizeof(qxl_modes); uint32_t rom_size = 8192; /* two pages */ - required_rom_size = MAX(required_rom_size, TARGET_PAGE_SIZE); - required_rom_size = msb_mask(required_rom_size * 2 - 1); - assert(required_rom_size <= rom_size); + QEMU_BUILD_BUG_ON(required_rom_size > rom_size); return rom_size; } @@ -364,7 +362,7 @@ static void init_qxl_rom(PCIQXLDevice *d) num_pages = d->vga.vram_size; num_pages -= ram_header_size; num_pages -= surface0_area_size; - num_pages = num_pages / TARGET_PAGE_SIZE; + num_pages = num_pages / QXL_PAGE_SIZE; rom->draw_area_offset = cpu_to_le32(0); rom->surface0_area_size = cpu_to_le32(surface0_area_size); @@ -416,9 +414,8 @@ static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr) intptr_t offset; offset = ptr - base; - offset &= ~(TARGET_PAGE_SIZE-1); assert(offset < qxl->vga.vram_size); - qxl_set_dirty(&qxl->vga.vram, offset, offset + TARGET_PAGE_SIZE); + qxl_set_dirty(&qxl->vga.vram, offset, offset + 3); } /* can be called from spice server thread context */ @@ -528,7 +525,8 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) info->num_memslots = NUM_MEMSLOTS; info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; info->internal_groupslot_id = 0; - info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS; + info->qxl_ram_size = + le32_to_cpu(qxl->shadow_rom.num_pages) << QXL_PAGE_BITS; info->n_surfaces = qxl->ssd.num_surfaces; } diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 8e9b0c299e..84f0182383 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -27,6 +27,9 @@ enum qxl_mode { #define QXL_NUM_DIRTY_RECTS 64 +#define QXL_PAGE_BITS 12 +#define QXL_PAGE_SIZE (1 << QXL_PAGE_BITS); + typedef struct PCIQXLDevice { PCIDevice pci; SimpleSpiceDisplay ssd; diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 92aabb83b5..383938d1bc 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -39,7 +39,7 @@ static void kvmclock_vm_state_change(void *opaque, int running, RunState state) { KVMClockState *s = opaque; - CPUState *cpu = first_cpu; + CPUState *cpu; int cap_clock_ctrl = kvm_check_extension(kvm_state, KVM_CAP_KVMCLOCK_CTRL); int ret; diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index d3a6fbe1f9..1c2dbf59cf 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -510,9 +510,8 @@ static void vapic_reset(DeviceState *dev) { VAPICROMState *s = VAPIC(dev); - if (s->state == VAPIC_ACTIVE) { - s->state = VAPIC_STANDBY; - } + s->state = VAPIC_INACTIVE; + s->rom_state_paddr = 0; vapic_enable_tpr_reporting(false); } @@ -578,7 +577,7 @@ static int patch_hypercalls(VAPICROMState *s) * enable write access to the option ROM so that variables can be updated by * the guest. */ -static void vapic_map_rom_writable(VAPICROMState *s) +static int vapic_map_rom_writable(VAPICROMState *s) { hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; MemoryRegionSection section; @@ -599,6 +598,9 @@ static void vapic_map_rom_writable(VAPICROMState *s) /* read ROM size from RAM region */ ram = memory_region_get_ram_ptr(section.mr); rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE; + if (rom_size == 0) { + return -1; + } s->rom_size = rom_size; /* We need to round to avoid creating subpages @@ -612,11 +614,15 @@ static void vapic_map_rom_writable(VAPICROMState *s) memory_region_add_subregion_overlap(as, rom_paddr, &s->rom, 1000); s->rom_mapped_writable = true; memory_region_unref(section.mr); + + return 0; } static int vapic_prepare(VAPICROMState *s) { - vapic_map_rom_writable(s); + if (vapic_map_rom_writable(s) < 0) { + return -1; + } if (patch_hypercalls(s) < 0) { return -1; @@ -659,6 +665,7 @@ static void vapic_write(void *opaque, hwaddr addr, uint64_t data, } if (vapic_prepare(s) < 0) { s->state = VAPIC_INACTIVE; + s->rom_state_paddr = 0; break; } break; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 907792b721..c6042c7e23 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -741,6 +741,7 @@ static QEMUMachine xenfv_machine = { .init = pc_xen_hvm_init, .max_cpus = HVM_MAX_VCPUS, .default_machine_opts = "accel=xen", + .hot_add_cpu = pc_hot_add_cpu, }; #endif diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 8246a1bdd4..e917c83540 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -53,10 +53,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, flash_size = memory_region_size(flash_mem); /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = flash_size; - if (isa_bios_size > (128 * 1024)) { - isa_bios_size = 128 * 1024; - } + isa_bios_size = MIN(flash_size, 128 * 1024); isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size); vmstate_register_ram_global(isa_bios); diff --git a/hw/net/e1000.c b/hw/net/e1000.c index d3f274cc28..151d25e0b7 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -32,6 +32,7 @@ #include "hw/loader.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" +#include "qemu/iov.h" #include "e1000_regs.h" @@ -64,6 +65,8 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL); /* this is the size past which hardware will drop packets when setting LPE=1 */ #define MAXIMUM_ETHERNET_LPE_SIZE 16384 +#define MAXIMUM_ETHERNET_HDR_LEN (14+4) + /* * HW models: * E1000_DEV_ID_82540EM works with Windows and Linux @@ -899,7 +902,7 @@ static uint64_t rx_desc_base(E1000State *s) } static ssize_t -e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) +e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) { E1000State *s = qemu_get_nic_opaque(nc); PCIDevice *d = PCI_DEVICE(s); @@ -908,8 +911,12 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) unsigned int n, rdt; uint32_t rdh_start; uint16_t vlan_special = 0; - uint8_t vlan_status = 0, vlan_offset = 0; + uint8_t vlan_status = 0; uint8_t min_buf[MIN_BUF_SIZE]; + struct iovec min_iov; + uint8_t *filter_buf = iov->iov_base; + size_t size = iov_size(iov, iovcnt); + size_t iov_ofs = 0; size_t desc_offset; size_t desc_size; size_t total_size; @@ -924,10 +931,16 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* Pad to minimum Ethernet frame length */ if (size < sizeof(min_buf)) { - memcpy(min_buf, buf, size); + iov_to_buf(iov, iovcnt, 0, min_buf, size); memset(&min_buf[size], 0, sizeof(min_buf) - size); - buf = min_buf; - size = sizeof(min_buf); + min_iov.iov_base = filter_buf = min_buf; + min_iov.iov_len = size = sizeof(min_buf); + iovcnt = 1; + iov = &min_iov; + } else if (iov->iov_len < MAXIMUM_ETHERNET_HDR_LEN) { + /* This is very unlikely, but may happen. */ + iov_to_buf(iov, iovcnt, 0, min_buf, MAXIMUM_ETHERNET_HDR_LEN); + filter_buf = min_buf; } /* Discard oversized packets if !LPE and !SBP. */ @@ -938,14 +951,24 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) return size; } - if (!receive_filter(s, buf, size)) + if (!receive_filter(s, filter_buf, size)) { return size; + } - if (vlan_enabled(s) && is_vlan_packet(s, buf)) { - vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(buf + 14))); - memmove((uint8_t *)buf + 4, buf, 12); + if (vlan_enabled(s) && is_vlan_packet(s, filter_buf)) { + vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(filter_buf + + 14))); + iov_ofs = 4; + if (filter_buf == iov->iov_base) { + memmove(filter_buf + 4, filter_buf, 12); + } else { + iov_from_buf(iov, iovcnt, 4, filter_buf, 12); + while (iov->iov_len <= iov_ofs) { + iov_ofs -= iov->iov_len; + iov++; + } + } vlan_status = E1000_RXD_STAT_VP; - vlan_offset = 4; size -= 4; } @@ -967,12 +990,23 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) desc.status |= (vlan_status | E1000_RXD_STAT_DD); if (desc.buffer_addr) { if (desc_offset < size) { + size_t iov_copy; + hwaddr ba = le64_to_cpu(desc.buffer_addr); size_t copy_size = size - desc_offset; if (copy_size > s->rxbuf_size) { copy_size = s->rxbuf_size; } - pci_dma_write(d, le64_to_cpu(desc.buffer_addr), - buf + desc_offset + vlan_offset, copy_size); + do { + iov_copy = MIN(copy_size, iov->iov_len - iov_ofs); + pci_dma_write(d, ba, iov->iov_base + iov_ofs, iov_copy); + copy_size -= iov_copy; + ba += iov_copy; + iov_ofs += iov_copy; + if (iov_ofs == iov->iov_len) { + iov++; + iov_ofs = 0; + } + } while (copy_size); } desc_offset += desc_size; desc.length = cpu_to_le16(desc_size); @@ -1022,6 +1056,17 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) return size; } +static ssize_t +e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) +{ + const struct iovec iov = { + .iov_base = (uint8_t *)buf, + .iov_len = size + }; + + return e1000_receive_iov(nc, &iov, 1); +} + static uint32_t mac_readreg(E1000State *s, int index) { @@ -1448,6 +1493,7 @@ static NetClientInfo net_e1000_info = { .size = sizeof(NICState), .can_receive = e1000_can_receive, .receive = e1000_receive, + .receive_iov = e1000_receive_iov, .cleanup = e1000_cleanup, .link_status_changed = e1000_set_link_status, }; diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index a8931652b3..865f2f0c59 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -134,7 +134,7 @@ static void pcnet_ioport_write(void *opaque, hwaddr addr, static const MemoryRegionOps pcnet_io_ops = { .read = pcnet_ioport_read, .write = pcnet_ioport_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void pcnet_mmio_writeb(void *opaque, hwaddr addr, uint32_t val) @@ -256,7 +256,7 @@ static const MemoryRegionOps pcnet_mmio_ops = { .read = { pcnet_mmio_readb, pcnet_mmio_readw, pcnet_mmio_readl }, .write = { pcnet_mmio_writeb, pcnet_mmio_writew, pcnet_mmio_writel }, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void pci_physical_memory_write(void *dma_opaque, hwaddr addr, diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 23dbeea14f..ad703a4bf7 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -388,7 +388,7 @@ static void mch_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_mch; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_Q35_MCH; - k->revision = MCH_HOST_BRIDGE_REVISION_DEFUALT; + k->revision = MCH_HOST_BRIDGE_REVISION_DEFAULT; k->class_id = PCI_CLASS_BRIDGE_HOST; } diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index a3aceef8f5..25951a020a 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -120,7 +120,7 @@ static uint16_t handle_write_event_buf(SCLPEventFacility *ef, ec = SCLP_EVENT_GET_CLASS(event); if (ec->write_event_data && - ec->event_type() == event_buf->type) { + ec->can_handle_event(event_buf->type)) { rc = ec->write_event_data(event, event_buf); break; } @@ -183,7 +183,7 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, { uint16_t rc; int slen; - unsigned elen = 0; + unsigned elen; BusChild *kid; SCLPEvent *event; SCLPEventClass *ec; @@ -203,11 +203,11 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, if (mask & ec->get_send_mask()) { if (ec->read_event_data(event, event_buf, &slen)) { + elen = be16_to_cpu(event_buf->length); + event_buf = (EventBufferHeader *) ((char *)event_buf + elen); rc = SCLP_RC_NORMAL_COMPLETION; } } - elen = be16_to_cpu(event_buf->length); - event_buf = (void *) event_buf + elen; } if (sccb->h.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE) { @@ -338,10 +338,19 @@ static int init_event_facility(S390SCLPDevice *sdev) return 0; } +static void reset_event_facility(DeviceState *dev) +{ + S390SCLPDevice *sdev = SCLP_S390_DEVICE(dev); + + sdev->ef->receive_mask = 0; +} + static void init_event_facility_class(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); S390SCLPDeviceClass *k = SCLP_S390_DEVICE_CLASS(klass); + dc->reset = reset_event_facility; k->init = init_event_facility; } diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index 5fadc86d42..a3c4bd6272 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -22,9 +22,9 @@ typedef struct SignalQuiesce { uint8_t unit; } QEMU_PACKED SignalQuiesce; -static int event_type(void) +static bool can_handle_event(uint8_t type) { - return SCLP_EVENT_SIGNAL_QUIESCE; + return type == SCLP_EVENT_SIGNAL_QUIESCE; } static unsigned int send_mask(void) @@ -65,6 +65,17 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, return 1; } +static const VMStateDescription vmstate_sclpquiesce = { + .name = "sclpquiesce", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_BOOL(event_pending, SCLPEvent), + VMSTATE_END_OF_LIST() + } +}; + typedef struct QuiesceNotifier QuiesceNotifier; static struct QuiesceNotifier { @@ -84,8 +95,6 @@ static void quiesce_powerdown_req(Notifier *n, void *opaque) static int quiesce_init(SCLPEvent *event) { - event->event_type = SCLP_EVENT_SIGNAL_QUIESCE; - qn.notifier.notify = quiesce_powerdown_req; qn.event = event; @@ -94,15 +103,25 @@ static int quiesce_init(SCLPEvent *event) return 0; } +static void quiesce_reset(DeviceState *dev) +{ + SCLPEvent *event = SCLP_EVENT(dev); + + event->event_pending = false; +} + static void quiesce_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *k = SCLP_EVENT_CLASS(klass); + dc->reset = quiesce_reset; + dc->vmsd = &vmstate_sclpquiesce; k->init = quiesce_init; k->get_send_mask = send_mask; k->get_receive_mask = receive_mask; - k->event_type = event_type; + k->can_handle_event = can_handle_event; k->read_event_data = read_event_data; k->write_event_data = NULL; } diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 0c36842729..36e5f50360 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -7,8 +7,11 @@ * This code is licensed under the LGPL. */ -/* ??? Need to check if the {read,write}[wl] routines work properly on - big-endian targets. */ +/* Note: + * LSI53C810 emulation is incorrect, in the sense that it supports + * features added in later evolutions. This should not be a problem, + * as well-behaved operating systems will not try to use them. + */ #include <assert.h> @@ -278,6 +281,7 @@ typedef struct { uint32_t script_ram[2048]; } LSIState; +#define TYPE_LSI53C810 "lsi53c810" #define TYPE_LSI53C895A "lsi53c895a" #define LSI53C895A(obj) \ @@ -998,12 +1002,6 @@ bad: s->msg_action = 0; } -/* Sign extend a 24-bit value. */ -static inline int32_t sxt24(int32_t n) -{ - return (n << 8) >> 8; -} - #define LSI_BUF_SIZE 4096 static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) { @@ -1083,7 +1081,7 @@ again: /* Table indirect addressing. */ /* 32-bit Table indirect */ - offset = sxt24(addr); + offset = sextract32(addr, 0, 24); pci_dma_read(pci_dev, s->dsa + offset, buf, 8); /* byte count is stored in bits 0:23 only */ s->dbc = cpu_to_le32(buf[0]) & 0xffffff; @@ -1183,13 +1181,13 @@ again: uint32_t id; if (insn & (1 << 25)) { - id = read_dword(s, s->dsa + sxt24(insn)); + id = read_dword(s, s->dsa + sextract32(insn, 0, 24)); } else { id = insn; } id = (id >> 16) & 0xf; if (insn & (1 << 26)) { - addr = s->dsp + sxt24(addr); + addr = s->dsp + sextract32(addr, 0, 24); } s->dnad = addr; switch (opcode) { @@ -1385,7 +1383,7 @@ again: if (cond == jmp) { if (insn & (1 << 23)) { /* Relative address. */ - addr = s->dsp + sxt24(addr); + addr = s->dsp + sextract32(addr, 0, 24); } switch ((insn >> 27) & 7) { case 0: /* Jump */ @@ -1438,7 +1436,7 @@ again: int i; if (insn & (1 << 28)) { - addr = s->dsa + sxt24(addr); + addr = s->dsa + sextract32(addr, 0, 24); } n = (insn & 7); reg = (insn >> 16) & 0xff; @@ -1521,7 +1519,7 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) used for diagnostics, so should be ok. */ return 0; case 0xc: /* DSTAT */ - tmp = s->dstat | 0x80; + tmp = s->dstat | LSI_DSTAT_DFE; if ((s->istat0 & LSI_ISTAT0_INTF) == 0) s->dstat = 0; lsi_update_irq(s); @@ -1705,8 +1703,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) s->sxfer = val; break; case 0x06: /* SDID */ - if ((val & 0xf) != (s->ssid & 0xf)) + if ((s->ssid & 0x80) && (val & 0xf) != (s->ssid & 0xf)) { BADF("Destination ID does not match SSID\n"); + } s->sdid = val & 0xf; break; case 0x07: /* GPREG0 */ @@ -1748,6 +1747,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) case 0x17: /* MBOX1 */ s->mbox1 = val; break; + case 0x18: /* CTEST0 */ + /* nothing to do */ + break; case 0x1a: /* CTEST2 */ s->ctest2 = val & LSI_CTEST2_PCICIE; break; @@ -1876,8 +1878,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) int shift; n = (offset - 0x58) >> 2; shift = (offset & 3) * 8; - s->scratch[n] &= ~(0xff << shift); - s->scratch[n] |= (val & 0xff) << shift; + s->scratch[n] = deposit32(s->scratch[n], shift, 8, val); } else { BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val); } @@ -2113,7 +2114,7 @@ static int lsi_scsi_init(PCIDevice *dev) "lsi-io", 256); pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io); - pci_register_bar(dev, 1, 0, &s->mmio_io); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio_io); pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io); QTAILQ_INIT(&s->queue); @@ -2151,9 +2152,23 @@ static const TypeInfo lsi_info = { .class_init = lsi_class_init, }; +static void lsi53c810_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->device_id = PCI_DEVICE_ID_LSI_53C810; +} + +static TypeInfo lsi53c810_info = { + .name = TYPE_LSI53C810, + .parent = TYPE_LSI53C895A, + .class_init = lsi53c810_class_init, +}; + static void lsi53c895a_register_types(void) { type_register_static(&lsi_info); + type_register_static(&lsi53c810_info); } type_init(lsi53c895a_register_types) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 5cd6137046..4d36841d40 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -224,7 +224,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, if (object_property_find(OBJECT(dev), "removable", NULL)) { qdev_prop_set_bit(dev, "removable", removable); } - if (serial) { + if (serial && object_property_find(OBJECT(dev), "serial", NULL)) { qdev_prop_set_string(dev, "serial", serial); } if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) { diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index b2fcd4b3e3..2a26042701 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -117,6 +117,20 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s) return NULL; } +static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag) +{ + vscsi_req *req; + int i; + + for (i = 0; i < VSCSI_REQ_LIMIT; i++) { + req = &s->reqs[i]; + if (req->iu.srp.cmd.tag == srp_tag) { + return req; + } + } + return NULL; +} + static void vscsi_put_req(vscsi_req *req) { if (req->sreq != NULL) { @@ -755,40 +769,91 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) { union viosrp_iu *iu = &req->iu; - int fn; + vscsi_req *tmpreq; + int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE; + SCSIDevice *d; + uint64_t tag = iu->srp.rsp.tag; + uint8_t sol_not = iu->srp.cmd.sol_not; fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n", iu->srp.tsk_mgmt.tsk_mgmt_func); - switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { -#if 0 /* We really don't deal with these for now */ - case SRP_TSK_ABORT_TASK: - fn = ABORT_TASK; - break; - case SRP_TSK_ABORT_TASK_SET: - fn = ABORT_TASK_SET; - break; - case SRP_TSK_CLEAR_TASK_SET: - fn = CLEAR_TASK_SET; - break; - case SRP_TSK_LUN_RESET: - fn = LOGICAL_UNIT_RESET; - break; - case SRP_TSK_CLEAR_ACA: - fn = CLEAR_ACA; - break; -#endif - default: - fn = 0; + d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun); + if (!d) { + resp = SRP_TSK_MGMT_FIELDS_INVALID; + } else { + switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { + case SRP_TSK_ABORT_TASK: + if (d->lun != lun) { + resp = SRP_TSK_MGMT_FIELDS_INVALID; + break; + } + + tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag); + if (tmpreq && tmpreq->sreq) { + assert(tmpreq->sreq->hba_private); + scsi_req_cancel(tmpreq->sreq); + } + break; + + case SRP_TSK_LUN_RESET: + if (d->lun != lun) { + resp = SRP_TSK_MGMT_FIELDS_INVALID; + break; + } + + qdev_reset_all(&d->qdev); + break; + + case SRP_TSK_ABORT_TASK_SET: + case SRP_TSK_CLEAR_TASK_SET: + if (d->lun != lun) { + resp = SRP_TSK_MGMT_FIELDS_INVALID; + break; + } + + for (i = 0; i < VSCSI_REQ_LIMIT; i++) { + tmpreq = &s->reqs[i]; + if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) { + continue; + } + if (!tmpreq->active || !tmpreq->sreq) { + continue; + } + assert(tmpreq->sreq->hba_private); + scsi_req_cancel(tmpreq->sreq); + } + break; + + case SRP_TSK_CLEAR_ACA: + resp = SRP_TSK_MGMT_NOT_SUPPORTED; + break; + + default: + resp = SRP_TSK_MGMT_FIELDS_INVALID; + break; + } } - if (fn) { - /* XXX Send/Handle target task management */ - ; + + /* Compose the response here as */ + memset(iu, 0, sizeof(struct srp_rsp) + 4); + iu->srp.rsp.opcode = SRP_RSP; + iu->srp.rsp.req_lim_delta = cpu_to_be32(1); + iu->srp.rsp.tag = tag; + iu->srp.rsp.flags |= SRP_RSP_FLAG_RSPVALID; + iu->srp.rsp.resp_data_len = cpu_to_be32(4); + if (resp) { + iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2; } else { - vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1; } - return !fn; + + iu->srp.rsp.status = GOOD; + iu->srp.rsp.data[3] = resp; + + vscsi_send_iu(s, req, sizeof(iu->srp.rsp) + 4, VIOSRP_SRP_FORMAT); + + return 1; } static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req) @@ -858,29 +923,97 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT); } +static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) +{ + struct viosrp_capabilities *vcap; + struct capabilities cap = { }; + uint16_t len, req_len; + uint64_t buffer; + int rc; + + vcap = &req->iu.mad.capabilities; + req_len = len = be16_to_cpu(vcap->common.length); + buffer = be64_to_cpu(vcap->buffer); + if (len > sizeof(cap)) { + fprintf(stderr, "vscsi_send_capabilities: capabilities size mismatch !\n"); + + /* + * Just read and populate the structure that is known. + * Zero rest of the structure. + */ + len = sizeof(cap); + } + rc = spapr_vio_dma_read(&s->vdev, buffer, &cap, len); + if (rc) { + fprintf(stderr, "vscsi_send_capabilities: DMA read failure !\n"); + } + + /* + * Current implementation does not suppport any migration or + * reservation capabilities. Construct the response telling the + * guest not to use them. + */ + cap.flags = 0; + cap.migration.ecl = 0; + cap.reserve.type = 0; + cap.migration.common.server_support = 0; + cap.reserve.common.server_support = 0; + + rc = spapr_vio_dma_write(&s->vdev, buffer, &cap, len); + if (rc) { + fprintf(stderr, "vscsi_send_capabilities: DMA write failure !\n"); + } + if (req_len > len) { + /* + * Being paranoid and lets not worry about the error code + * here. Actual write of the cap is done above. + */ + spapr_vio_dma_set(&s->vdev, (buffer + len), 0, (req_len - len)); + } + vcap->common.status = rc ? cpu_to_be32(1) : 0; + return vscsi_send_iu(s, req, sizeof(*vcap), VIOSRP_MAD_FORMAT); +} + static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req) { union mad_iu *mad = &req->iu.mad; + bool request_handled = false; + uint64_t retlen = 0; switch (be32_to_cpu(mad->empty_iu.common.type)) { case VIOSRP_EMPTY_IU_TYPE: fprintf(stderr, "Unsupported EMPTY MAD IU\n"); + retlen = sizeof(mad->empty_iu); break; case VIOSRP_ERROR_LOG_TYPE: fprintf(stderr, "Unsupported ERROR LOG MAD IU\n"); - mad->error_log.common.status = cpu_to_be16(1); - vscsi_send_iu(s, req, sizeof(mad->error_log), VIOSRP_MAD_FORMAT); + retlen = sizeof(mad->error_log); break; case VIOSRP_ADAPTER_INFO_TYPE: vscsi_send_adapter_info(s, req); + request_handled = true; break; case VIOSRP_HOST_CONFIG_TYPE: - mad->host_config.common.status = cpu_to_be16(1); - vscsi_send_iu(s, req, sizeof(mad->host_config), VIOSRP_MAD_FORMAT); + retlen = sizeof(mad->host_config); + break; + case VIOSRP_CAPABILITIES_TYPE: + vscsi_send_capabilities(s, req); + request_handled = true; break; default: fprintf(stderr, "VSCSI: Unknown MAD type %02x\n", be32_to_cpu(mad->empty_iu.common.type)); + /* + * PAPR+ says that "The length field is set to the length + * of the data structure(s) used in the command". + * As we did not recognize the request type, put zero there. + */ + retlen = 0; + } + + if (!request_handled) { + mad->empty_iu.common.status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED); + vscsi_send_iu(s, req, retlen, VIOSRP_MAD_FORMAT); } return 1; diff --git a/hw/scsi/srp.h b/hw/scsi/srp.h index 5e0cad5c19..d27f31d2d5 100644 --- a/hw/scsi/srp.h +++ b/hw/scsi/srp.h @@ -90,6 +90,13 @@ enum { SRP_REV16A_IB_IO_CLASS = 0x0100 }; +enum { + SRP_TSK_MGMT_COMPLETE = 0x00, + SRP_TSK_MGMT_FIELDS_INVALID = 0x02, + SRP_TSK_MGMT_NOT_SUPPORTED = 0x04, + SRP_TSK_MGMT_FAILED = 0x05 +}; + struct srp_direct_buf { uint64_t va; uint32_t key; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 3bd690d128..26d95a14ec 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -693,6 +693,7 @@ static const TypeInfo virtio_scsi_common_info = { .name = TYPE_VIRTIO_SCSI_COMMON, .parent = TYPE_VIRTIO_DEVICE, .instance_size = sizeof(VirtIOSCSICommon), + .abstract = true, .class_init = virtio_scsi_common_class_init, }; diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c index 13f6602ad2..ad77705f8c 100644 --- a/hw/usb/combined-packet.c +++ b/hw/usb/combined-packet.c @@ -39,6 +39,7 @@ static void usb_combined_packet_remove(USBCombinedPacket *combined, p->combined = NULL; QTAILQ_REMOVE(&combined->packets, p, combined_entry); if (QTAILQ_EMPTY(&combined->packets)) { + qemu_iovec_destroy(&combined->iov); g_free(combined); } } diff --git a/hw/usb/core.c b/hw/usb/core.c index 31960c28a8..cf59a1abcf 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -622,6 +622,7 @@ void usb_ep_reset(USBDevice *dev) dev->ep_ctl.nr = 0; dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; dev->ep_ctl.ifnum = 0; + dev->ep_ctl.max_packet_size = 64; dev->ep_ctl.dev = dev; dev->ep_ctl.pipeline = false; for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { @@ -633,6 +634,8 @@ void usb_ep_reset(USBDevice *dev) dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID; dev->ep_in[ep].ifnum = USB_INTERFACE_INVALID; dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID; + dev->ep_in[ep].max_packet_size = 0; + dev->ep_out[ep].max_packet_size = 0; dev->ep_in[ep].dev = dev; dev->ep_out[ep].dev = dev; dev->ep_in[ep].pipeline = false; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index f02231dc87..469c24d768 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -509,6 +509,8 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); +static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, + unsigned int slotid, unsigned int epid); static const char *TRBType_names[] = { [TRB_RESERVED] = "TRB_RESERVED", @@ -1138,7 +1140,7 @@ static void xhci_reset_streams(XHCIEPContext *epctx) static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) { assert(epctx->pstreams == NULL); - epctx->nr_pstreams = 2 << epctx->max_pstreams; + epctx->nr_pstreams = 2 << (epctx->max_pstreams + 1); epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base); } @@ -1245,6 +1247,9 @@ static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci, epctx->epid = epid; for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { + epctx->transfers[i].xhci = xhci; + epctx->transfers[i].slotid = slotid; + epctx->transfers[i].epid = epid; usb_packet_init(&epctx->transfers[i].packet); } epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx); @@ -1358,13 +1363,12 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_xfer; for (i = 0; i < TD_QUEUE; i++) { - if (epctx->transfers[xferi].packet.ep) { - ep = epctx->transfers[xferi].packet.ep; - } killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]); epctx->transfers[xferi].packet.ep = NULL; xferi = (xferi + 1) % TD_QUEUE; } + + ep = xhci_epid_to_usbep(xhci, slotid, epid); if (ep) { usb_device_ep_stopped(ep->dev, ep); } @@ -1376,6 +1380,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, { XHCISlot *slot; XHCIEPContext *epctx; + int i; trace_usb_xhci_ep_disable(slotid, epid); assert(slotid >= 1 && slotid <= xhci->numslots); @@ -1396,6 +1401,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, xhci_free_streams(epctx); } + for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { + usb_packet_cleanup(&epctx->transfers[i].packet); + } + xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); timer_free(epctx->kick_timer); @@ -1696,7 +1705,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, static int xhci_setup_packet(XHCITransfer *xfer) { XHCIState *xhci = xfer->xhci; - USBDevice *dev; USBEndpoint *ep; int dir; @@ -1704,15 +1712,13 @@ static int xhci_setup_packet(XHCITransfer *xfer) if (xfer->packet.ep) { ep = xfer->packet.ep; - dev = ep->dev; } else { - if (!xhci->slots[xfer->slotid-1].uport) { + ep = xhci_epid_to_usbep(xhci, xfer->slotid, xfer->epid); + if (!ep) { fprintf(stderr, "xhci: slot %d has no device\n", xfer->slotid); return -1; } - dev = xhci->slots[xfer->slotid-1].uport->dev; - ep = usb_ep_get(dev, dir, xfer->epid >> 1); } xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ @@ -1720,7 +1726,7 @@ static int xhci_setup_packet(XHCITransfer *xfer) xfer->trbs[0].addr, false, xfer->int_req); usb_packet_map(&xfer->packet, &xfer->sgl); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", - xfer->packet.pid, dev->addr, ep->nr); + xfer->packet.pid, ep->dev->addr, ep->nr); return 0; } @@ -2060,9 +2066,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, for (i = 0; i < length; i++) { assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL)); } - xfer->xhci = xhci; - xfer->epid = epid; - xfer->slotid = slotid; xfer->streamid = streamid; if (epid == 1) { @@ -2075,7 +2078,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, } else { if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) { epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; - ep = xfer->packet.ep; } else { if (!xfer->timed_xfer) { fprintf(stderr, "xhci: error firing data transfer\n"); @@ -2092,6 +2094,8 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, break; } } + + ep = xhci_epid_to_usbep(xhci, slotid, epid); if (ep) { usb_device_flush_ep_queue(ep->dev, ep); } @@ -3321,6 +3325,19 @@ static int xhci_find_epid(USBEndpoint *ep) } } +static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, + unsigned int slotid, unsigned int epid) +{ + assert(slotid >= 1 && slotid <= xhci->numslots); + + if (!xhci->slots[slotid - 1].uport) { + return NULL; + } + + return usb_ep_get(xhci->slots[slotid - 1].uport->dev, + (epid & 1) ? USB_TOKEN_IN : USB_TOKEN_OUT, epid >> 1); +} + static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, unsigned int stream) { diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c deleted file mode 100644 index 39f22810b3..0000000000 --- a/hw/usb/host-bsd.c +++ /dev/null @@ -1,639 +0,0 @@ -/* - * BSD host USB redirector - * - * Copyright (c) 2006 Lonnie Mendez - * Portions of code and concepts borrowed from - * usb-linux.c and libusb's bsd.c and are copyright their respective owners. - * - * 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 "monitor/monitor.h" -#include "hw/usb.h" - -/* usb.h declares these */ -#undef USB_SPEED_HIGH -#undef USB_SPEED_FULL -#undef USB_SPEED_LOW - -#include <sys/ioctl.h> -#ifndef __DragonFly__ -#include <dev/usb/usb.h> -#else -#include <bus/usb/usb.h> -#endif - -/* This value has maximum potential at 16. - * You should also set hw.usb.debug to gain - * more detailed view. - */ -//#define DEBUG -#define UGEN_DEBUG_LEVEL 0 - - -typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id, - int vendor_id, int product_id, - const char *product_name, int speed); -static int usb_host_find_device(int *pbus_num, int *paddr, - const char *devname); - -typedef struct USBHostDevice { - USBDevice dev; - int ep_fd[USB_MAX_ENDPOINTS]; - int devfd; - char devpath[32]; -} USBHostDevice; - - -static int ensure_ep_open(USBHostDevice *dev, int ep, int mode) -{ - char buf[32]; - int fd; - - /* Get the address for this endpoint */ - ep = UE_GET_ADDR(ep); - - if (dev->ep_fd[ep] < 0) { -#if defined(__FreeBSD__) || defined(__DragonFly__) - snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep); -#else - snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep); -#endif - /* Try to open it O_RDWR first for those devices which have in and out - * endpoints with the same address (eg 0x02 and 0x82) - */ - fd = open(buf, O_RDWR); - if (fd < 0 && errno == ENXIO) - fd = open(buf, mode); - if (fd < 0) { -#ifdef DEBUG - printf("ensure_ep_open: failed to open device endpoint %s: %s\n", - buf, strerror(errno)); -#endif - } - dev->ep_fd[ep] = fd; - } - - return dev->ep_fd[ep]; -} - -static void ensure_eps_closed(USBHostDevice *dev) -{ - int epnum = 1; - - if (!dev) - return; - - while (epnum < USB_MAX_ENDPOINTS) { - if (dev->ep_fd[epnum] >= 0) { - close(dev->ep_fd[epnum]); - dev->ep_fd[epnum] = -1; - } - epnum++; - } -} - -static void usb_host_handle_reset(USBDevice *dev) -{ -#if 0 - USBHostDevice *s = (USBHostDevice *)dev; -#endif -} - -/* XXX: - * -check device states against transfer requests - * and return appropriate response - */ -static void usb_host_handle_control(USBDevice *dev, - USBPacket *p, - int request, - int value, - int index, - int length, - uint8_t *data) -{ - USBHostDevice *s = (USBHostDevice *)dev; - struct usb_ctl_request req; - struct usb_alt_interface aiface; - int ret, timeout = 50; - - if ((request >> 8) == UT_WRITE_DEVICE && - (request & 0xff) == UR_SET_ADDRESS) { - - /* specific SET_ADDRESS support */ - dev->addr = value; - } else if ((request >> 8) == UT_WRITE_DEVICE && - (request & 0xff) == UR_SET_CONFIG) { - - ensure_eps_closed(s); /* can't do this without all eps closed */ - - ret = ioctl(s->devfd, USB_SET_CONFIG, &value); - if (ret < 0) { -#ifdef DEBUG - printf("handle_control: failed to set configuration - %s\n", - strerror(errno)); -#endif - p->status = USB_RET_STALL; - } - } else if ((request >> 8) == UT_WRITE_INTERFACE && - (request & 0xff) == UR_SET_INTERFACE) { - - aiface.uai_interface_index = index; - aiface.uai_alt_no = value; - - ensure_eps_closed(s); /* can't do this without all eps closed */ - ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface); - if (ret < 0) { -#ifdef DEBUG - printf("handle_control: failed to set alternate interface - %s\n", - strerror(errno)); -#endif - p->status = USB_RET_STALL; - } - } else { - req.ucr_request.bmRequestType = request >> 8; - req.ucr_request.bRequest = request & 0xff; - USETW(req.ucr_request.wValue, value); - USETW(req.ucr_request.wIndex, index); - USETW(req.ucr_request.wLength, length); - req.ucr_data = data; - req.ucr_flags = USBD_SHORT_XFER_OK; - - ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout); -#if defined(__NetBSD__) || defined(__OpenBSD__) - if (ret < 0 && errno != EINVAL) { -#else - if (ret < 0) { -#endif -#ifdef DEBUG - printf("handle_control: setting timeout failed - %s\n", - strerror(errno)); -#endif - } - - ret = ioctl(s->devfd, USB_DO_REQUEST, &req); - /* ugen returns EIO for usbd_do_request_ no matter what - * happens with the transfer */ - if (ret < 0) { -#ifdef DEBUG - printf("handle_control: error after request - %s\n", - strerror(errno)); -#endif - p->status = USB_RET_NAK; /* STALL */ - } else { - p->actual_length = req.ucr_actlen; - } - } -} - -static void usb_host_handle_data(USBDevice *dev, USBPacket *p) -{ - USBHostDevice *s = (USBHostDevice *)dev; - int ret, fd, mode; - int one = 1, shortpacket = 0, timeout = 50; - sigset_t new_mask, old_mask; - uint8_t devep = p->ep->nr; - - /* protect data transfers from SIGALRM signal */ - sigemptyset(&new_mask); - sigaddset(&new_mask, SIGALRM); - sigprocmask(SIG_BLOCK, &new_mask, &old_mask); - - if (p->pid == USB_TOKEN_IN) { - devep |= 0x80; - mode = O_RDONLY; - shortpacket = 1; - } else { - mode = O_WRONLY; - } - - fd = ensure_ep_open(s, devep, mode); - if (fd < 0) { - sigprocmask(SIG_SETMASK, &old_mask, NULL); - p->status = USB_RET_NODEV; - return; - } - - if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) { -#ifdef DEBUG - printf("handle_data: failed to set timeout - %s\n", - strerror(errno)); -#endif - } - - if (shortpacket) { - if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) { -#ifdef DEBUG - printf("handle_data: failed to set short xfer mode - %s\n", - strerror(errno)); -#endif - sigprocmask(SIG_SETMASK, &old_mask, NULL); - } - } - - if (p->pid == USB_TOKEN_IN) - ret = readv(fd, p->iov.iov, p->iov.niov); - else - ret = writev(fd, p->iov.iov, p->iov.niov); - - sigprocmask(SIG_SETMASK, &old_mask, NULL); - - if (ret < 0) { -#ifdef DEBUG - printf("handle_data: error after %s data - %s\n", - pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno)); -#endif - switch(errno) { - case ETIMEDOUT: - case EINTR: - p->status = USB_RET_NAK; - break; - default: - p->status = USB_RET_STALL; - } - } else { - p->actual_length = ret; - } -} - -static void usb_host_handle_destroy(USBDevice *opaque) -{ - USBHostDevice *s = (USBHostDevice *)opaque; - int i; - - for (i = 0; i < USB_MAX_ENDPOINTS; i++) - if (s->ep_fd[i] >= 0) - close(s->ep_fd[i]); - - if (s->devfd < 0) - return; - - close(s->devfd); - - g_free(s); -} - -static int usb_host_initfn(USBDevice *dev) -{ - dev->flags |= (1 << USB_DEV_FLAG_IS_HOST); - return 0; -} - -USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname) -{ - struct usb_device_info bus_info, dev_info; - USBDevice *d = NULL, *ret = NULL; - USBHostDevice *dev; - char ctlpath[PATH_MAX + 1]; - char buspath[PATH_MAX + 1]; - int bfd, dfd, bus, address, i; - int ugendebug = UGEN_DEBUG_LEVEL; - - if (usb_host_find_device(&bus, &address, devname) < 0) { - goto fail; - } - - snprintf(buspath, PATH_MAX, "/dev/usb%d", bus); - - bfd = open(buspath, O_RDWR); - if (bfd < 0) { -#ifdef DEBUG - printf("usb_host_device_open: failed to open usb bus - %s\n", - strerror(errno)); -#endif - goto fail; - } - - bus_info.udi_addr = address; - if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) { -#ifdef DEBUG - printf("usb_host_device_open: failed to grab bus information - %s\n", - strerror(errno)); -#endif - goto fail_bfd; - } - -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) - snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]); -#else - snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]); -#endif - - dfd = open(ctlpath, O_RDWR); - if (dfd < 0) { - dfd = open(ctlpath, O_RDONLY); - if (dfd < 0) { -#ifdef DEBUG - printf("usb_host_device_open: failed to open usb device %s - %s\n", - ctlpath, strerror(errno)); -#endif - } - goto fail_dfd; - } - - if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) { -#ifdef DEBUG - printf("usb_host_device_open: failed to grab device info - %s\n", - strerror(errno)); -#endif - goto fail_dfd; - } - - d = usb_create(guest_bus, "usb-host"); - dev = DO_UPCAST(USBHostDevice, dev, d); - - if (dev_info.udi_speed == 1) { - dev->dev.speed = USB_SPEED_LOW - 1; - dev->dev.speedmask = USB_SPEED_MASK_LOW; - } else { - dev->dev.speed = USB_SPEED_FULL - 1; - dev->dev.speedmask = USB_SPEED_MASK_FULL; - } - - if (strncmp(dev_info.udi_product, "product", 7) != 0) { - pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc), - dev_info.udi_product); - } else { - snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), - "host:%s", devname); - } - - pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/"); - pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]); - - /* Mark the endpoints as not yet open */ - for (i = 0; i < USB_MAX_ENDPOINTS; i++) { - dev->ep_fd[i] = -1; - } - - ioctl(dfd, USB_SETDEBUG, &ugendebug); - - ret = (USBDevice *)dev; - -fail_dfd: - close(dfd); -fail_bfd: - close(bfd); -fail: - return ret; -} - -static void usb_host_class_initfn(ObjectClass *klass, void *data) -{ - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "USB Host Device"; - uc->init = usb_host_initfn; - uc->handle_reset = usb_host_handle_reset; - uc->handle_control = usb_host_handle_control; - uc->handle_data = usb_host_handle_data; - uc->handle_destroy = usb_host_handle_destroy; -} - -static const TypeInfo usb_host_dev_info = { - .name = "usb-host", - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBHostDevice), - .class_init = usb_host_class_initfn, -}; - -static void usb_host_register_types(void) -{ - type_register_static(&usb_host_dev_info); -} - -type_init(usb_host_register_types) - -static int usb_host_scan(void *opaque, USBScanFunc *func) -{ - struct usb_device_info bus_info; - struct usb_device_info dev_info; - uint16_t vendor_id, product_id, class_id, speed; - int bfd, dfd, bus, address; - char busbuf[20], devbuf[20], product_name[256]; - int ret = 0; - - for (bus = 0; bus < 10; bus++) { - - snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus); - bfd = open(busbuf, O_RDWR); - if (bfd < 0) - continue; - - for (address = 1; address < 127; address++) { - - bus_info.udi_addr = address; - if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) - continue; - - /* only list devices that can be used by generic layer */ - if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0) - continue; - -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) - snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]); -#else - snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]); -#endif - - dfd = open(devbuf, O_RDONLY); - if (dfd < 0) { -#ifdef DEBUG - printf("usb_host_scan: couldn't open device %s - %s\n", devbuf, - strerror(errno)); -#endif - continue; - } - - if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) - printf("usb_host_scan: couldn't get device information for %s - %s\n", - devbuf, strerror(errno)); - - /* XXX: might need to fixup endianness of word values before copying over */ - - vendor_id = dev_info.udi_vendorNo; - product_id = dev_info.udi_productNo; - class_id = dev_info.udi_class; - speed = dev_info.udi_speed; - - if (strncmp(dev_info.udi_product, "product", 7) != 0) - pstrcpy(product_name, sizeof(product_name), - dev_info.udi_product); - else - product_name[0] = '\0'; - - ret = func(opaque, bus, address, class_id, vendor_id, - product_id, product_name, speed); - - close(dfd); - - if (ret) - goto the_end; - } - - close(bfd); - } - -the_end: - return ret; -} - -typedef struct FindDeviceState { - int vendor_id; - int product_id; - int bus_num; - int addr; -} FindDeviceState; - -static int usb_host_find_device_scan(void *opaque, int bus_num, int addr, - int class_id, - int vendor_id, int product_id, - const char *product_name, int speed) -{ - FindDeviceState *s = opaque; - if (vendor_id == s->vendor_id && - product_id == s->product_id) { - s->bus_num = bus_num; - s->addr = addr; - return 1; - } else { - return 0; - } -} - - -/* the syntax is : - 'bus.addr' (decimal numbers) or - 'vendor_id:product_id' (hexa numbers) */ -static int usb_host_find_device(int *pbus_num, int *paddr, - const char *devname) -{ - const char *p; - int ret; - FindDeviceState fs; - - p = strchr(devname, '.'); - if (p) { - *pbus_num = strtoul(devname, NULL, 0); - *paddr = strtoul(p + 1, NULL, 0); - return 0; - } - p = strchr(devname, ':'); - if (p) { - fs.vendor_id = strtoul(devname, NULL, 16); - fs.product_id = strtoul(p + 1, NULL, 16); - ret = usb_host_scan(&fs, usb_host_find_device_scan); - if (ret) { - *pbus_num = fs.bus_num; - *paddr = fs.addr; - return 0; - } - } - return -1; -} - -/**********************/ -/* USB host device info */ - -struct usb_class_info { - int class; - const char *class_name; -}; - -static const struct usb_class_info usb_class_info[] = { - { USB_CLASS_AUDIO, "Audio"}, - { USB_CLASS_COMM, "Communication"}, - { USB_CLASS_HID, "HID"}, - { USB_CLASS_HUB, "Hub" }, - { USB_CLASS_PHYSICAL, "Physical" }, - { USB_CLASS_PRINTER, "Printer" }, - { USB_CLASS_MASS_STORAGE, "Storage" }, - { USB_CLASS_CDC_DATA, "Data" }, - { USB_CLASS_APP_SPEC, "Application Specific" }, - { USB_CLASS_VENDOR_SPEC, "Vendor Specific" }, - { USB_CLASS_STILL_IMAGE, "Still Image" }, - { USB_CLASS_CSCID, "Smart Card" }, - { USB_CLASS_CONTENT_SEC, "Content Security" }, - { -1, NULL } -}; - -static const char *usb_class_str(uint8_t class) -{ - const struct usb_class_info *p; - for (p = usb_class_info; p->class != -1; p++) { - if (p->class == class) - break; - } - return p->class_name; -} - -static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id, - int vendor_id, int product_id, - const char *product_name, - int speed) -{ - const char *class_str, *speed_str; - - switch(speed) { - case USB_SPEED_LOW: - speed_str = "1.5"; - break; - case USB_SPEED_FULL: - speed_str = "12"; - break; - case USB_SPEED_HIGH: - speed_str = "480"; - break; - default: - speed_str = "?"; - break; - } - - monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n", - bus_num, addr, speed_str); - class_str = usb_class_str(class_id); - if (class_str) - monitor_printf(mon, " %s:", class_str); - else - monitor_printf(mon, " Class %02x:", class_id); - monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id); - if (product_name[0] != '\0') - monitor_printf(mon, ", %s", product_name); - monitor_printf(mon, "\n"); -} - -static int usb_host_info_device(void *opaque, - int bus_num, int addr, - int class_id, - int vendor_id, int product_id, - const char *product_name, - int speed) -{ - Monitor *mon = opaque; - - usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id, - product_name, speed); - return 0; -} - -void usb_host_info(Monitor *mon, const QDict *qdict) -{ - usb_host_scan(mon, usb_host_info_device); -} diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c deleted file mode 100644 index 65cd3b444c..0000000000 --- a/hw/usb/host-linux.c +++ /dev/null @@ -1,1911 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ <linux@tjworld.net> - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * 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/monitor.h" -#include "sysemu/sysemu.h" -#include "trace.h" - -#include <dirent.h> -#include <sys/ioctl.h> - -#include <linux/usbdevice_fs.h> -#include <linux/version.h> -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "hw/usb/host.h" - -#ifdef CONFIG_USB_LIBUSB -# define DEVNAME "usb-host-linux" -#else -# define DEVNAME "usb-host" -#endif - -/* We redefine it to avoid version problems */ -struct usb_ctrltransfer { - uint8_t bRequestType; - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; - uint32_t timeout; - void *data; -}; - -typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port, - int class_id, int vendor_id, int product_id, - const char *product_name, int speed); - -//#define DEBUG - -#ifdef DEBUG -#define DPRINTF printf -#else -#define DPRINTF(...) -#endif - -#define PRODUCT_NAME_SZ 32 -#define MAX_PORTLEN 16 - -/* endpoint association data */ -#define ISO_FRAME_DESC_PER_URB 32 - -/* devio.c limits single requests to 16k */ -#define MAX_USBFS_BUFFER_SIZE 16384 - -typedef struct AsyncURB AsyncURB; - -struct endp_data { - uint8_t halted; - uint8_t iso_started; - AsyncURB *iso_urb; - int iso_urb_idx; - int iso_buffer_used; - int inflight; -}; - -enum USBHostDeviceOptions { - USB_HOST_OPT_PIPELINE, -}; - -typedef struct USBHostDevice { - USBDevice dev; - int fd; - int hub_fd; - int hub_port; - - uint8_t descr[8192]; - int descr_len; - int closing; - uint32_t iso_urb_count; - uint32_t options; - Notifier exit; - QEMUBH *bh; - - struct endp_data ep_in[USB_MAX_ENDPOINTS]; - struct endp_data ep_out[USB_MAX_ENDPOINTS]; - QLIST_HEAD(, AsyncURB) aurbs; - - /* Host side address */ - int bus_num; - int addr; - char port[MAX_PORTLEN]; - struct USBAutoFilter match; - int32_t bootindex; - int seen, errcount; - - QTAILQ_ENTRY(USBHostDevice) next; -} USBHostDevice; - -static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs); - -static int usb_host_close(USBHostDevice *dev); -static void usb_host_auto_check(void *unused); -static int usb_host_read_file(char *line, size_t line_size, - const char *device_file, const char *device_name); -static void usb_linux_update_endp_table(USBHostDevice *s); - -static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p) -{ - static const int usbfs[] = { - [USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL, - [USB_ENDPOINT_XFER_ISOC] = USBDEVFS_URB_TYPE_ISO, - [USB_ENDPOINT_XFER_BULK] = USBDEVFS_URB_TYPE_BULK, - [USB_ENDPOINT_XFER_INT] = USBDEVFS_URB_TYPE_INTERRUPT, - }; - uint8_t type = p->ep->type; - assert(type < ARRAY_SIZE(usbfs)); - return usbfs[type]; -} - -static int usb_host_do_reset(USBHostDevice *dev) -{ - struct timeval s, e; - uint32_t usecs; - int ret; - - gettimeofday(&s, NULL); - ret = ioctl(dev->fd, USBDEVFS_RESET); - gettimeofday(&e, NULL); - usecs = (e.tv_sec - s.tv_sec) * 1000000; - usecs += e.tv_usec - s.tv_usec; - if (usecs > 1000000) { - /* more than a second, something is fishy, broken usb device? */ - fprintf(stderr, "husb: device %d:%d reset took %d.%06d seconds\n", - dev->bus_num, dev->addr, usecs / 1000000, usecs % 1000000); - } - return ret; -} - -static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep) -{ - struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out; - assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); - assert(ep > 0 && ep <= USB_MAX_ENDPOINTS); - return eps + ep - 1; -} - -static int is_isoc(USBHostDevice *s, int pid, int ep) -{ - return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC; -} - -static int is_valid(USBHostDevice *s, int pid, int ep) -{ - return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID; -} - -static int is_halted(USBHostDevice *s, int pid, int ep) -{ - return get_endp(s, pid, ep)->halted; -} - -static void clear_halt(USBHostDevice *s, int pid, int ep) -{ - trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep); - get_endp(s, pid, ep)->halted = 0; -} - -static void set_halt(USBHostDevice *s, int pid, int ep) -{ - if (ep != 0) { - trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep); - get_endp(s, pid, ep)->halted = 1; - } -} - -static int is_iso_started(USBHostDevice *s, int pid, int ep) -{ - return get_endp(s, pid, ep)->iso_started; -} - -static void clear_iso_started(USBHostDevice *s, int pid, int ep) -{ - trace_usb_host_iso_stop(s->bus_num, s->addr, ep); - get_endp(s, pid, ep)->iso_started = 0; -} - -static void set_iso_started(USBHostDevice *s, int pid, int ep) -{ - struct endp_data *e = get_endp(s, pid, ep); - - trace_usb_host_iso_start(s->bus_num, s->addr, ep); - if (!e->iso_started) { - e->iso_started = 1; - e->inflight = 0; - } -} - -static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value) -{ - struct endp_data *e = get_endp(s, pid, ep); - - e->inflight += value; - return e->inflight; -} - -static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb) -{ - get_endp(s, pid, ep)->iso_urb = iso_urb; -} - -static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep) -{ - return get_endp(s, pid, ep)->iso_urb; -} - -static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i) -{ - get_endp(s, pid, ep)->iso_urb_idx = i; -} - -static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep) -{ - return get_endp(s, pid, ep)->iso_urb_idx; -} - -static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i) -{ - get_endp(s, pid, ep)->iso_buffer_used = i; -} - -static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep) -{ - return get_endp(s, pid, ep)->iso_buffer_used; -} - -/* - * Async URB state. - * We always allocate iso packet descriptors even for bulk transfers - * to simplify allocation and casts. - */ -struct AsyncURB -{ - struct usbdevfs_urb urb; - struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB]; - USBHostDevice *hdev; - QLIST_ENTRY(AsyncURB) next; - - /* For regular async urbs */ - USBPacket *packet; - int more; /* large transfer, more urbs follow */ - - /* For buffered iso handling */ - int iso_frame_idx; /* -1 means in flight */ -}; - -static AsyncURB *async_alloc(USBHostDevice *s) -{ - AsyncURB *aurb = g_malloc0(sizeof(AsyncURB)); - aurb->hdev = s; - QLIST_INSERT_HEAD(&s->aurbs, aurb, next); - return aurb; -} - -static void async_free(AsyncURB *aurb) -{ - QLIST_REMOVE(aurb, next); - g_free(aurb); -} - -static void do_disconnect(USBHostDevice *s) -{ - usb_host_close(s); - usb_host_auto_check(NULL); -} - -static void async_complete(void *opaque) -{ - USBHostDevice *s = opaque; - AsyncURB *aurb; - int urbs = 0; - - while (1) { - USBPacket *p; - - int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb); - if (r < 0) { - if (errno == EAGAIN) { - if (urbs > 2) { - /* indicates possible latency issues */ - trace_usb_host_iso_many_urbs(s->bus_num, s->addr, urbs); - } - return; - } - if (errno == ENODEV) { - if (!s->closing) { - trace_usb_host_disconnect(s->bus_num, s->addr); - do_disconnect(s); - } - return; - } - - perror("USBDEVFS_REAPURBNDELAY"); - return; - } - - DPRINTF("husb: async completed. aurb %p status %d alen %d\n", - aurb, aurb->urb.status, aurb->urb.actual_length); - - /* If this is a buffered iso urb mark it as complete and don't do - anything else (it is handled further in usb_host_handle_iso_data) */ - if (aurb->iso_frame_idx == -1) { - int inflight; - int pid = (aurb->urb.endpoint & USB_DIR_IN) ? - USB_TOKEN_IN : USB_TOKEN_OUT; - int ep = aurb->urb.endpoint & 0xf; - if (aurb->urb.status == -EPIPE) { - set_halt(s, pid, ep); - } - aurb->iso_frame_idx = 0; - urbs++; - inflight = change_iso_inflight(s, pid, ep, -1); - if (inflight == 0 && is_iso_started(s, pid, ep)) { - /* can be latency issues, or simply end of stream */ - trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, ep); - } - continue; - } - - p = aurb->packet; - trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status, - aurb->urb.actual_length, aurb->more); - - if (p) { - switch (aurb->urb.status) { - case 0: - p->actual_length += aurb->urb.actual_length; - if (!aurb->more) { - /* Clear previous ASYNC status */ - p->status = USB_RET_SUCCESS; - } - break; - - case -EPIPE: - set_halt(s, p->pid, p->ep->nr); - p->status = USB_RET_STALL; - break; - - case -EOVERFLOW: - p->status = USB_RET_BABBLE; - break; - - default: - p->status = USB_RET_IOERROR; - break; - } - - if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, aurb->urb.actual_length); - usb_generic_async_ctrl_complete(&s->dev, p); - } else if (!aurb->more) { - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, aurb->urb.actual_length); - usb_packet_complete(&s->dev, p); - } - } - - async_free(aurb); - } -} - -static void usb_host_async_cancel(USBDevice *dev, USBPacket *p) -{ - USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); - AsyncURB *aurb; - - trace_usb_host_req_canceled(s->bus_num, s->addr, p); - - QLIST_FOREACH(aurb, &s->aurbs, next) { - if (p != aurb->packet) { - continue; - } - - trace_usb_host_urb_canceled(s->bus_num, s->addr, aurb); - - /* Mark it as dead (see async_complete above) */ - aurb->packet = NULL; - - int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb); - if (r < 0) { - DPRINTF("husb: async. discard urb failed errno %d\n", errno); - } - } -} - -static int usb_host_open_device(int bus, int addr) -{ - const char *usbfs = NULL; - char filename[32]; - struct stat st; - int fd, rc; - - rc = stat("/dev/bus/usb", &st); - if (rc == 0 && S_ISDIR(st.st_mode)) { - /* udev-created device nodes available */ - usbfs = "/dev/bus/usb"; - } else { - /* fallback: usbfs mounted below /proc */ - usbfs = "/proc/bus/usb"; - } - - snprintf(filename, sizeof(filename), "%s/%03d/%03d", - usbfs, bus, addr); - fd = open(filename, O_RDWR | O_NONBLOCK); - if (fd < 0) { - fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno)); - } - return fd; -} - -static int usb_host_claim_port(USBHostDevice *s) -{ -#ifdef USBDEVFS_CLAIM_PORT - char *h, hub_name[64], line[1024]; - int hub_addr, ret; - - snprintf(hub_name, sizeof(hub_name), "%d-%s", - s->match.bus_num, s->match.port); - - /* try strip off last ".$portnr" to get hub */ - h = strrchr(hub_name, '.'); - if (h != NULL) { - s->hub_port = atoi(h+1); - *h = '\0'; - } else { - /* no dot in there -> it is the root hub */ - snprintf(hub_name, sizeof(hub_name), "usb%d", - s->match.bus_num); - s->hub_port = atoi(s->match.port); - } - - if (!usb_host_read_file(line, sizeof(line), "devnum", - hub_name)) { - return -1; - } - if (sscanf(line, "%d", &hub_addr) != 1) { - return -1; - } - - s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr); - if (s->hub_fd < 0) { - return -1; - } - - ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &s->hub_port); - if (ret < 0) { - close(s->hub_fd); - s->hub_fd = -1; - return -1; - } - - trace_usb_host_claim_port(s->match.bus_num, hub_addr, s->hub_port); - return 0; -#else - return -1; -#endif -} - -static void usb_host_release_port(USBHostDevice *s) -{ - if (s->hub_fd == -1) { - return; - } -#ifdef USBDEVFS_RELEASE_PORT - ioctl(s->hub_fd, USBDEVFS_RELEASE_PORT, &s->hub_port); -#endif - close(s->hub_fd); - s->hub_fd = -1; -} - -static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces) -{ - /* earlier Linux 2.4 do not support that */ -#ifdef USBDEVFS_DISCONNECT - struct usbdevfs_ioctl ctrl; - int ret, interface; - - for (interface = 0; interface < nb_interfaces; interface++) { - ctrl.ioctl_code = USBDEVFS_DISCONNECT; - ctrl.ifno = interface; - ctrl.data = 0; - ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl); - if (ret < 0 && errno != ENODATA) { - perror("USBDEVFS_DISCONNECT"); - return -1; - } - } -#endif - return 0; -} - -static int usb_linux_get_num_interfaces(USBHostDevice *s) -{ - char device_name[64], line[1024]; - int num_interfaces = 0; - - sprintf(device_name, "%d-%s", s->bus_num, s->port); - if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces", - device_name)) { - return -1; - } - if (sscanf(line, "%d", &num_interfaces) != 1) { - return -1; - } - return num_interfaces; -} - -static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) -{ - const char *op = NULL; - int dev_descr_len, config_descr_len; - int interface, nb_interfaces; - int ret, i; - - for (i = 0; i < USB_MAX_INTERFACES; i++) { - dev->dev.altsetting[i] = 0; - } - - if (configuration == 0) { /* address state - ignore */ - dev->dev.ninterfaces = 0; - dev->dev.configuration = 0; - return 1; - } - - DPRINTF("husb: claiming interfaces. config %d\n", configuration); - - i = 0; - dev_descr_len = dev->descr[0]; - if (dev_descr_len > dev->descr_len) { - fprintf(stderr, "husb: update iface failed. descr too short\n"); - return 0; - } - - i += dev_descr_len; - while (i < dev->descr_len) { - DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n", - i, dev->descr_len, - dev->descr[i], dev->descr[i+1]); - - if (dev->descr[i+1] != USB_DT_CONFIG) { - i += dev->descr[i]; - continue; - } - config_descr_len = dev->descr[i]; - - DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration); - - if (configuration == dev->descr[i + 5]) { - configuration = dev->descr[i + 5]; - break; - } - - i += config_descr_len; - } - - if (i >= dev->descr_len) { - fprintf(stderr, - "husb: update iface failed. no matching configuration\n"); - return 0; - } - nb_interfaces = dev->descr[i + 4]; - - if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) { - goto fail; - } - - /* XXX: only grab if all interfaces are free */ - for (interface = 0; interface < nb_interfaces; interface++) { - op = "USBDEVFS_CLAIMINTERFACE"; - ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface); - if (ret < 0) { - goto fail; - } - } - - trace_usb_host_claim_interfaces(dev->bus_num, dev->addr, - nb_interfaces, configuration); - - dev->dev.ninterfaces = nb_interfaces; - dev->dev.configuration = configuration; - return 1; - -fail: - if (errno == ENODEV) { - do_disconnect(dev); - } - perror(op); - return 0; -} - -static int usb_host_release_interfaces(USBHostDevice *s) -{ - int ret, i; - - trace_usb_host_release_interfaces(s->bus_num, s->addr); - - for (i = 0; i < s->dev.ninterfaces; i++) { - ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i); - if (ret < 0) { - perror("USBDEVFS_RELEASEINTERFACE"); - return 0; - } - } - return 1; -} - -static void usb_host_handle_reset(USBDevice *dev) -{ - USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); - - trace_usb_host_reset(s->bus_num, s->addr); - - usb_host_do_reset(s); - - usb_host_claim_interfaces(s, 0); - usb_linux_update_endp_table(s); -} - -static void usb_host_handle_destroy(USBDevice *dev) -{ - USBHostDevice *s = (USBHostDevice *)dev; - - usb_host_release_port(s); - usb_host_close(s); - QTAILQ_REMOVE(&hostdevs, s, next); - qemu_remove_exit_notifier(&s->exit); -} - -/* iso data is special, we need to keep enough urbs in flight to make sure - that the controller never runs out of them, otherwise the device will - likely suffer a buffer underrun / overrun. */ -static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep) -{ - AsyncURB *aurb; - int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep); - - aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb)); - for (i = 0; i < s->iso_urb_count; i++) { - aurb[i].urb.endpoint = ep; - aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len; - aurb[i].urb.buffer = g_malloc(aurb[i].urb.buffer_length); - aurb[i].urb.type = USBDEVFS_URB_TYPE_ISO; - aurb[i].urb.flags = USBDEVFS_URB_ISO_ASAP; - aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB; - for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++) - aurb[i].urb.iso_frame_desc[j].length = len; - if (pid == USB_TOKEN_IN) { - aurb[i].urb.endpoint |= 0x80; - /* Mark as fully consumed (idle) */ - aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB; - } - } - set_iso_urb(s, pid, ep, aurb); - - return aurb; -} - -static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep) -{ - AsyncURB *aurb; - int i, ret, killed = 0, free = 1; - - aurb = get_iso_urb(s, pid, ep); - if (!aurb) { - return; - } - - for (i = 0; i < s->iso_urb_count; i++) { - /* in flight? */ - if (aurb[i].iso_frame_idx == -1) { - ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]); - if (ret < 0) { - perror("USBDEVFS_DISCARDURB"); - free = 0; - continue; - } - killed++; - } - } - - /* Make sure any urbs we've killed are reaped before we free them */ - if (killed) { - async_complete(s); - } - - for (i = 0; i < s->iso_urb_count; i++) { - g_free(aurb[i].urb.buffer); - } - - if (free) - g_free(aurb); - else - printf("husb: leaking iso urbs because of discard failure\n"); - set_iso_urb(s, pid, ep, NULL); - set_iso_urb_idx(s, pid, ep, 0); - clear_iso_started(s, pid, ep); -} - -static void urb_status_to_usb_ret(int status, USBPacket *p) -{ - switch (status) { - case -EPIPE: - p->status = USB_RET_STALL; - break; - case -EOVERFLOW: - p->status = USB_RET_BABBLE; - break; - default: - p->status = USB_RET_IOERROR; - } -} - -static void usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) -{ - AsyncURB *aurb; - int i, j, max_packet_size, offset, len; - uint8_t *buf; - - max_packet_size = p->ep->max_packet_size; - if (max_packet_size == 0) { - p->status = USB_RET_NAK; - return; - } - - aurb = get_iso_urb(s, p->pid, p->ep->nr); - if (!aurb) { - aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr); - } - - i = get_iso_urb_idx(s, p->pid, p->ep->nr); - j = aurb[i].iso_frame_idx; - if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { - if (in) { - /* Check urb status */ - if (aurb[i].urb.status) { - urb_status_to_usb_ret(aurb[i].urb.status, p); - /* Move to the next urb */ - aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1; - /* Check frame status */ - } else if (aurb[i].urb.iso_frame_desc[j].status) { - urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status, p); - /* Check the frame fits */ - } else if (aurb[i].urb.iso_frame_desc[j].actual_length - > p->iov.size) { - printf("husb: received iso data is larger then packet\n"); - p->status = USB_RET_BABBLE; - /* All good copy data over */ - } else { - len = aurb[i].urb.iso_frame_desc[j].actual_length; - buf = aurb[i].urb.buffer + - j * aurb[i].urb.iso_frame_desc[0].length; - usb_packet_copy(p, buf, len); - } - } else { - len = p->iov.size; - offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr); - - /* Check the frame fits */ - if (len > max_packet_size) { - printf("husb: send iso data is larger then max packet size\n"); - p->status = USB_RET_NAK; - return; - } - - /* All good copy data over */ - usb_packet_copy(p, aurb[i].urb.buffer + offset, len); - aurb[i].urb.iso_frame_desc[j].length = len; - offset += len; - set_iso_buffer_used(s, p->pid, p->ep->nr, offset); - - /* Start the stream once we have buffered enough data */ - if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) { - set_iso_started(s, p->pid, p->ep->nr); - } - } - aurb[i].iso_frame_idx++; - if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { - i = (i + 1) % s->iso_urb_count; - set_iso_urb_idx(s, p->pid, p->ep->nr, i); - } - } else { - if (in) { - set_iso_started(s, p->pid, p->ep->nr); - } else { - DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); - } - } - - if (is_iso_started(s, p->pid, p->ep->nr)) { - /* (Re)-submit all fully consumed / filled urbs */ - for (i = 0; i < s->iso_urb_count; i++) { - if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { - if (ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]) < 0) { - perror("USBDEVFS_SUBMITURB"); - if (!in || p->status == USB_RET_SUCCESS) { - switch(errno) { - case ETIMEDOUT: - p->status = USB_RET_NAK; - break; - case EPIPE: - default: - p->status = USB_RET_STALL; - } - } - break; - } - aurb[i].iso_frame_idx = -1; - change_iso_inflight(s, p->pid, p->ep->nr, 1); - } - } - } -} - -static void usb_host_handle_data(USBDevice *dev, USBPacket *p) -{ - USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); - struct usbdevfs_urb *urb; - AsyncURB *aurb; - int ret, rem, prem, v; - uint8_t *pbuf; - uint8_t ep; - - trace_usb_host_req_data(s->bus_num, s->addr, p, - p->pid == USB_TOKEN_IN, - p->ep->nr, p->iov.size); - - if (!is_valid(s, p->pid, p->ep->nr)) { - p->status = USB_RET_NAK; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - return; - } - - if (p->pid == USB_TOKEN_IN) { - ep = p->ep->nr | 0x80; - } else { - ep = p->ep->nr; - } - - if (is_halted(s, p->pid, p->ep->nr)) { - unsigned int arg = ep; - ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg); - if (ret < 0) { - perror("USBDEVFS_CLEAR_HALT"); - p->status = USB_RET_NAK; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - return; - } - clear_halt(s, p->pid, p->ep->nr); - } - - if (is_isoc(s, p->pid, p->ep->nr)) { - usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); - return; - } - - v = 0; - prem = 0; - pbuf = NULL; - rem = p->iov.size; - do { - if (prem == 0 && rem > 0) { - assert(v < p->iov.niov); - prem = p->iov.iov[v].iov_len; - pbuf = p->iov.iov[v].iov_base; - assert(prem <= rem); - v++; - } - aurb = async_alloc(s); - aurb->packet = p; - - urb = &aurb->urb; - urb->endpoint = ep; - urb->type = usb_host_usbfs_type(s, p); - urb->usercontext = s; - urb->buffer = pbuf; - urb->buffer_length = prem; - - if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) { - urb->buffer_length = MAX_USBFS_BUFFER_SIZE; - } - pbuf += urb->buffer_length; - prem -= urb->buffer_length; - rem -= urb->buffer_length; - if (rem) { - aurb->more = 1; - } - - trace_usb_host_urb_submit(s->bus_num, s->addr, aurb, - urb->buffer_length, aurb->more); - ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); - - DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n", - urb->endpoint, urb->buffer_length, aurb->more, p, aurb); - - if (ret < 0) { - perror("USBDEVFS_SUBMITURB"); - async_free(aurb); - - switch(errno) { - case ETIMEDOUT: - p->status = USB_RET_NAK; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - break; - case EPIPE: - default: - p->status = USB_RET_STALL; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - } - return; - } - } while (rem > 0); - - p->status = USB_RET_ASYNC; -} - -static int ctrl_error(void) -{ - if (errno == ETIMEDOUT) { - return USB_RET_NAK; - } else { - return USB_RET_STALL; - } -} - -static void usb_host_set_address(USBHostDevice *s, int addr) -{ - trace_usb_host_set_address(s->bus_num, s->addr, addr); - s->dev.addr = addr; -} - -static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p) -{ - int ret, first = 1; - - trace_usb_host_set_config(s->bus_num, s->addr, config); - - usb_host_release_interfaces(s); - -again: - ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config); - - DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno); - - if (ret < 0 && errno == EBUSY && first) { - /* happens if usb device is in use by host drivers */ - int count = usb_linux_get_num_interfaces(s); - if (count > 0) { - DPRINTF("husb: busy -> disconnecting %d interfaces\n", count); - usb_host_disconnect_ifaces(s, count); - first = 0; - goto again; - } - } - - if (ret < 0) { - p->status = ctrl_error(); - return; - } - usb_host_claim_interfaces(s, config); - usb_linux_update_endp_table(s); -} - -static void usb_host_set_interface(USBHostDevice *s, int iface, int alt, - USBPacket *p) -{ - struct usbdevfs_setinterface si; - int i, ret; - - trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt); - - for (i = 1; i <= USB_MAX_ENDPOINTS; i++) { - if (is_isoc(s, USB_TOKEN_IN, i)) { - usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i); - } - if (is_isoc(s, USB_TOKEN_OUT, i)) { - usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i); - } - } - - if (iface >= USB_MAX_INTERFACES) { - p->status = USB_RET_STALL; - return; - } - - si.interface = iface; - si.altsetting = alt; - ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si); - - DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n", - iface, alt, ret, errno); - - if (ret < 0) { - p->status = ctrl_error(); - return; - } - - s->dev.altsetting[iface] = alt; - usb_linux_update_endp_table(s); -} - -static void usb_host_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); - struct usbdevfs_urb *urb; - AsyncURB *aurb; - int ret; - - /* - * Process certain standard device requests. - * These are infrequent and are processed synchronously. - */ - - /* Note request is (bRequestType << 8) | bRequest */ - trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index); - - switch (request) { - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - usb_host_set_address(s, value); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - usb_host_set_config(s, value & 0xff, p); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - usb_host_set_interface(s, index, value, p); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == 0) { /* clear halt */ - int pid = (index & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; - ioctl(s->fd, USBDEVFS_CLEAR_HALT, &index); - clear_halt(s, pid, index & 0x0f); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, 0); - return; - } - } - - /* The rest are asynchronous */ - if (length > sizeof(dev->data_buf)) { - fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n", - length, sizeof(dev->data_buf)); - p->status = USB_RET_STALL; - return; - } - - aurb = async_alloc(s); - aurb->packet = p; - - /* - * Setup ctrl transfer. - * - * s->ctrl is laid out such that data buffer immediately follows - * 'req' struct which is exactly what usbdevfs expects. - */ - urb = &aurb->urb; - - urb->type = USBDEVFS_URB_TYPE_CONTROL; - urb->endpoint = p->ep->nr; - - urb->buffer = &dev->setup_buf; - urb->buffer_length = length + 8; - - urb->usercontext = s; - - trace_usb_host_urb_submit(s->bus_num, s->addr, aurb, - urb->buffer_length, aurb->more); - ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); - - DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb); - - if (ret < 0) { - DPRINTF("husb: submit failed. errno %d\n", errno); - async_free(aurb); - - switch(errno) { - case ETIMEDOUT: - p->status = USB_RET_NAK; - break; - case EPIPE: - default: - p->status = USB_RET_STALL; - break; - } - return; - } - - p->status = USB_RET_ASYNC; -} - -static void usb_linux_update_endp_table(USBHostDevice *s) -{ - static const char *tname[] = { - [USB_ENDPOINT_XFER_CONTROL] = "control", - [USB_ENDPOINT_XFER_ISOC] = "isoc", - [USB_ENDPOINT_XFER_BULK] = "bulk", - [USB_ENDPOINT_XFER_INT] = "int", - }; - uint8_t devep, type; - uint16_t mps, v, p; - int ep, pid; - unsigned int i, configuration = -1, interface = -1, altsetting = -1; - struct endp_data *epd; - USBDescriptor *d; - bool active = false; - - usb_ep_reset(&s->dev); - - for (i = 0;; i += d->bLength) { - if (i+2 >= s->descr_len) { - break; - } - d = (void *)(s->descr + i); - if (d->bLength < 2) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "descriptor too short"); - return; - } - if (i + d->bLength > s->descr_len) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "descriptor too long"); - return; - } - switch (d->bDescriptorType) { - case 0: - trace_usb_host_parse_error(s->bus_num, s->addr, - "invalid descriptor type"); - return; - case USB_DT_DEVICE: - if (d->bLength < 0x12) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "device descriptor too short"); - return; - } - v = (d->u.device.idVendor_hi << 8) | d->u.device.idVendor_lo; - p = (d->u.device.idProduct_hi << 8) | d->u.device.idProduct_lo; - trace_usb_host_parse_device(s->bus_num, s->addr, v, p); - break; - case USB_DT_CONFIG: - if (d->bLength < 0x09) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "config descriptor too short"); - return; - } - configuration = d->u.config.bConfigurationValue; - active = (configuration == s->dev.configuration); - trace_usb_host_parse_config(s->bus_num, s->addr, - configuration, active); - break; - case USB_DT_INTERFACE: - if (d->bLength < 0x09) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "interface descriptor too short"); - return; - } - interface = d->u.interface.bInterfaceNumber; - altsetting = d->u.interface.bAlternateSetting; - active = (configuration == s->dev.configuration) && - (altsetting == s->dev.altsetting[interface]); - trace_usb_host_parse_interface(s->bus_num, s->addr, - interface, altsetting, active); - break; - case USB_DT_ENDPOINT: - if (d->bLength < 0x07) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "endpoint descriptor too short"); - return; - } - devep = d->u.endpoint.bEndpointAddress; - pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; - ep = devep & 0xf; - if (ep == 0) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "invalid endpoint address"); - return; - } - - type = d->u.endpoint.bmAttributes & 0x3; - mps = d->u.endpoint.wMaxPacketSize_lo | - (d->u.endpoint.wMaxPacketSize_hi << 8); - trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep, - (devep & USB_DIR_IN) ? "in" : "out", - tname[type], active); - - if (active) { - usb_ep_set_max_packet_size(&s->dev, pid, ep, mps); - assert(usb_ep_get_type(&s->dev, pid, ep) == - USB_ENDPOINT_XFER_INVALID); - usb_ep_set_type(&s->dev, pid, ep, type); - usb_ep_set_ifnum(&s->dev, pid, ep, interface); - if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) && - (type == USB_ENDPOINT_XFER_BULK) && - (pid == USB_TOKEN_OUT)) { - usb_ep_set_pipeline(&s->dev, pid, ep, true); - } - - epd = get_endp(s, pid, ep); - epd->halted = 0; - } - - break; - default: - trace_usb_host_parse_unknown(s->bus_num, s->addr, - d->bLength, d->bDescriptorType); - break; - } - } -} - -/* - * Check if we can safely redirect a usb2 device to a usb1 virtual controller, - * this function assumes this is safe, if: - * 1) There are no isoc endpoints - * 2) There are no interrupt endpoints with a max_packet_size > 64 - * Note bulk endpoints with a max_packet_size > 64 in theory also are not - * usb1 compatible, but in practice this seems to work fine. - */ -static int usb_linux_full_speed_compat(USBHostDevice *dev) -{ - int i, packet_size; - - /* - * usb_linux_update_endp_table only registers info about ep in the current - * interface altsettings, so we need to parse the descriptors again. - */ - for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) { - if (dev->descr[i + 1] == USB_DT_ENDPOINT) { - switch (dev->descr[i + 3] & 0x3) { - case 0x00: /* CONTROL */ - break; - case 0x01: /* ISO */ - return 0; - case 0x02: /* BULK */ - break; - case 0x03: /* INTERRUPT */ - packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8); - if (packet_size > 64) - return 0; - break; - } - } - } - return 1; -} - -static int usb_host_open(USBHostDevice *dev, int bus_num, - int addr, const char *port, - const char *prod_name, int speed) -{ - int fd = -1, ret; - - trace_usb_host_open_started(bus_num, addr); - - if (dev->fd != -1) { - goto fail; - } - - fd = usb_host_open_device(bus_num, addr); - if (fd < 0) { - goto fail; - } - DPRINTF("husb: opened %s\n", buf); - - dev->bus_num = bus_num; - dev->addr = addr; - pstrcpy(dev->port, sizeof(dev->port), port); - dev->fd = fd; - - /* read the device description */ - dev->descr_len = read(fd, dev->descr, sizeof(dev->descr)); - if (dev->descr_len <= 0) { - perror("husb: reading device data failed"); - goto fail; - } - -#ifdef DEBUG - { - int x; - printf("=== begin dumping device descriptor data ===\n"); - for (x = 0; x < dev->descr_len; x++) { - printf("%02x ", dev->descr[x]); - } - printf("\n=== end dumping device descriptor data ===\n"); - } -#endif - - - /* start unconfigured -- we'll wait for the guest to set a configuration */ - if (!usb_host_claim_interfaces(dev, 0)) { - goto fail; - } - - usb_ep_init(&dev->dev); - usb_linux_update_endp_table(dev); - - if (speed == -1) { - struct usbdevfs_connectinfo ci; - - ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); - if (ret < 0) { - perror("usb_host_device_open: USBDEVFS_CONNECTINFO"); - goto fail; - } - - if (ci.slow) { - speed = USB_SPEED_LOW; - } else { - speed = USB_SPEED_HIGH; - } - } - dev->dev.speed = speed; - dev->dev.speedmask = (1 << speed); - if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) { - dev->dev.speedmask |= USB_SPEED_MASK_FULL; - } - - trace_usb_host_open_success(bus_num, addr); - - if (!prod_name || prod_name[0] == '\0') { - snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), - "host:%d.%d", bus_num, addr); - } else { - pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc), - prod_name); - } - - ret = usb_device_attach(&dev->dev); - if (ret) { - goto fail; - } - - /* USB devio uses 'write' flag to check for async completions */ - qemu_set_fd_handler(dev->fd, NULL, async_complete, dev); - - return 0; - -fail: - trace_usb_host_open_failure(bus_num, addr); - if (dev->fd != -1) { - close(dev->fd); - dev->fd = -1; - } - return -1; -} - -static int usb_host_close(USBHostDevice *dev) -{ - int i; - - if (dev->fd == -1) { - return -1; - } - - trace_usb_host_close(dev->bus_num, dev->addr); - - qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); - dev->closing = 1; - for (i = 1; i <= USB_MAX_ENDPOINTS; i++) { - if (is_isoc(dev, USB_TOKEN_IN, i)) { - usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i); - } - if (is_isoc(dev, USB_TOKEN_OUT, i)) { - usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i); - } - } - async_complete(dev); - dev->closing = 0; - if (dev->dev.attached) { - usb_device_detach(&dev->dev); - } - usb_host_do_reset(dev); - close(dev->fd); - dev->fd = -1; - return 0; -} - -static void usb_host_exit_notifier(struct Notifier *n, void *data) -{ - USBHostDevice *s = container_of(n, USBHostDevice, exit); - - usb_host_release_port(s); - if (s->fd != -1) { - usb_host_do_reset(s); - } -} - -/* - * This is *NOT* about restoring state. We have absolutely no idea - * what state the host device is in at the moment and whenever it is - * still present in the first place. Attemping to contine where we - * left off is impossible. - * - * What we are going to to to here is emulate a surprise removal of - * the usb device passed through, then kick host scan so the device - * will get re-attached (and re-initialized by the guest) in case it - * is still present. - * - * As the device removal will change the state of other devices (usb - * host controller, most likely interrupt controller too) we have to - * wait with it until *all* vmstate is loaded. Thus post_load just - * kicks a bottom half which then does the actual work. - */ -static void usb_host_post_load_bh(void *opaque) -{ - USBHostDevice *dev = opaque; - - if (dev->fd != -1) { - usb_host_close(dev); - } - if (dev->dev.attached) { - usb_device_detach(&dev->dev); - } - usb_host_auto_check(NULL); -} - -static int usb_host_post_load(void *opaque, int version_id) -{ - USBHostDevice *dev = opaque; - - qemu_bh_schedule(dev->bh); - return 0; -} - -static int usb_host_initfn(USBDevice *dev) -{ - USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); - - dev->flags |= (1 << USB_DEV_FLAG_IS_HOST); - dev->auto_attach = 0; - s->fd = -1; - s->hub_fd = -1; - - QTAILQ_INSERT_TAIL(&hostdevs, s, next); - s->exit.notify = usb_host_exit_notifier; - qemu_add_exit_notifier(&s->exit); - s->bh = qemu_bh_new(usb_host_post_load_bh, s); - usb_host_auto_check(NULL); - - if (s->match.bus_num != 0 && s->match.port != NULL) { - usb_host_claim_port(s); - } - add_boot_device_path(s->bootindex, &dev->qdev, NULL); - return 0; -} - -static const VMStateDescription vmstate_usb_host = { - .name = DEVNAME, - .version_id = 1, - .minimum_version_id = 1, - .post_load = usb_host_post_load, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBHostDevice), - VMSTATE_END_OF_LIST() - } -}; - -static Property usb_host_dev_properties[] = { - DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0), - DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0), - DEFINE_PROP_STRING("hostport", USBHostDevice, match.port), - DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0), - DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0), - DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), - DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex, -1), - DEFINE_PROP_BIT("pipeline", USBHostDevice, options, - USB_HOST_OPT_PIPELINE, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_host_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->init = usb_host_initfn; - uc->product_desc = "USB Host Device"; - uc->cancel_packet = usb_host_async_cancel; - uc->handle_data = usb_host_handle_data; - uc->handle_control = usb_host_handle_control; - uc->handle_reset = usb_host_handle_reset; - uc->handle_destroy = usb_host_handle_destroy; - dc->vmsd = &vmstate_usb_host; - dc->props = usb_host_dev_properties; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo usb_host_dev_info = { - .name = DEVNAME, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBHostDevice), - .class_init = usb_host_class_initfn, -}; - -static void usb_host_register_types(void) -{ - type_register_static(&usb_host_dev_info); -} - -type_init(usb_host_register_types) - -/* - * Read sys file-system device file - * - * @line address of buffer to put file contents in - * @line_size size of line - * @device_file path to device file (printf format string) - * @device_name device being opened (inserted into device_file) - * - * @return 0 failed, 1 succeeded ('line' contains data) - */ -static int usb_host_read_file(char *line, size_t line_size, - const char *device_file, const char *device_name) -{ - FILE *f; - int ret = 0; - char filename[PATH_MAX]; - - snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name, - device_file); - f = fopen(filename, "r"); - if (f) { - ret = fgets(line, line_size, f) != NULL; - fclose(f); - } - - return ret; -} - -/* - * Use /sys/bus/usb/devices/ directory to determine host's USB - * devices. - * - * This code is based on Robert Schiele's original patches posted to - * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950 - */ -static int usb_host_scan(void *opaque, USBScanFunc *func) -{ - DIR *dir = NULL; - char line[1024]; - int bus_num, addr, speed, class_id, product_id, vendor_id; - int ret = 0; - char port[MAX_PORTLEN]; - char product_name[512]; - struct dirent *de; - - dir = opendir("/sys/bus/usb/devices"); - if (!dir) { - perror("husb: opendir /sys/bus/usb/devices"); - fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n"); - goto the_end; - } - - while ((de = readdir(dir))) { - if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) { - if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) { - continue; - } - - if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) { - goto the_end; - } - if (sscanf(line, "%d", &addr) != 1) { - goto the_end; - } - if (!usb_host_read_file(line, sizeof(line), "bDeviceClass", - de->d_name)) { - goto the_end; - } - if (sscanf(line, "%x", &class_id) != 1) { - goto the_end; - } - - if (!usb_host_read_file(line, sizeof(line), "idVendor", - de->d_name)) { - goto the_end; - } - if (sscanf(line, "%x", &vendor_id) != 1) { - goto the_end; - } - if (!usb_host_read_file(line, sizeof(line), "idProduct", - de->d_name)) { - goto the_end; - } - if (sscanf(line, "%x", &product_id) != 1) { - goto the_end; - } - if (!usb_host_read_file(line, sizeof(line), "product", - de->d_name)) { - *product_name = 0; - } else { - if (strlen(line) > 0) { - line[strlen(line) - 1] = '\0'; - } - pstrcpy(product_name, sizeof(product_name), line); - } - - if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) { - goto the_end; - } - if (!strcmp(line, "5000\n")) { - speed = USB_SPEED_SUPER; - } else if (!strcmp(line, "480\n")) { - speed = USB_SPEED_HIGH; - } else if (!strcmp(line, "1.5\n")) { - speed = USB_SPEED_LOW; - } else { - speed = USB_SPEED_FULL; - } - - ret = func(opaque, bus_num, addr, port, class_id, vendor_id, - product_id, product_name, speed); - if (ret) { - goto the_end; - } - } - } - the_end: - if (dir) { - closedir(dir); - } - return ret; -} - -static QEMUTimer *usb_auto_timer; -static VMChangeStateEntry *usb_vmstate; - -static int usb_host_auto_scan(void *opaque, int bus_num, - int addr, const char *port, - int class_id, int vendor_id, int product_id, - const char *product_name, int speed) -{ - struct USBAutoFilter *f; - struct USBHostDevice *s; - - /* Ignore hubs */ - if (class_id == 9) - return 0; - - QTAILQ_FOREACH(s, &hostdevs, next) { - f = &s->match; - - if (f->bus_num > 0 && f->bus_num != bus_num) { - continue; - } - if (f->addr > 0 && f->addr != addr) { - continue; - } - if (f->port != NULL && strcmp(f->port, port) != 0) { - continue; - } - - if (f->vendor_id > 0 && f->vendor_id != vendor_id) { - continue; - } - - if (f->product_id > 0 && f->product_id != product_id) { - continue; - } - /* We got a match */ - s->seen++; - if (s->errcount >= 3) { - return 0; - } - - /* Already attached ? */ - if (s->fd != -1) { - return 0; - } - DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); - - if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) { - s->errcount++; - } - break; - } - - return 0; -} - -static void usb_host_vm_state(void *unused, int running, RunState state) -{ - if (running) { - usb_host_auto_check(unused); - } -} - -static void usb_host_auto_check(void *unused) -{ - struct USBHostDevice *s; - int unconnected = 0; - - if (runstate_is_running()) { - usb_host_scan(NULL, usb_host_auto_scan); - - QTAILQ_FOREACH(s, &hostdevs, next) { - if (s->fd == -1) { - unconnected++; - } - if (s->seen == 0) { - s->errcount = 0; - } - s->seen = 0; - } - - if (unconnected == 0) { - /* nothing to watch */ - if (usb_auto_timer) { - timer_del(usb_auto_timer); - trace_usb_host_auto_scan_disabled(); - } - return; - } - } - - if (!usb_vmstate) { - usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL); - } - if (!usb_auto_timer) { - usb_auto_timer = timer_new_ms(QEMU_CLOCK_REALTIME, usb_host_auto_check, NULL); - if (!usb_auto_timer) { - return; - } - trace_usb_host_auto_scan_enabled(); - } - timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000); -} - -#ifndef CONFIG_USB_LIBUSB - -/**********************/ -/* USB host device info */ - -struct usb_class_info { - int class; - const char *class_name; -}; - -static const struct usb_class_info usb_class_info[] = { - { USB_CLASS_AUDIO, "Audio"}, - { USB_CLASS_COMM, "Communication"}, - { USB_CLASS_HID, "HID"}, - { USB_CLASS_HUB, "Hub" }, - { USB_CLASS_PHYSICAL, "Physical" }, - { USB_CLASS_PRINTER, "Printer" }, - { USB_CLASS_MASS_STORAGE, "Storage" }, - { USB_CLASS_CDC_DATA, "Data" }, - { USB_CLASS_APP_SPEC, "Application Specific" }, - { USB_CLASS_VENDOR_SPEC, "Vendor Specific" }, - { USB_CLASS_STILL_IMAGE, "Still Image" }, - { USB_CLASS_CSCID, "Smart Card" }, - { USB_CLASS_CONTENT_SEC, "Content Security" }, - { -1, NULL } -}; - -static const char *usb_class_str(uint8_t class) -{ - const struct usb_class_info *p; - for(p = usb_class_info; p->class != -1; p++) { - if (p->class == class) { - break; - } - } - return p->class_name; -} - -static void usb_info_device(Monitor *mon, int bus_num, - int addr, const char *port, - int class_id, int vendor_id, int product_id, - const char *product_name, - int speed) -{ - const char *class_str, *speed_str; - - switch(speed) { - case USB_SPEED_LOW: - speed_str = "1.5"; - break; - case USB_SPEED_FULL: - speed_str = "12"; - break; - case USB_SPEED_HIGH: - speed_str = "480"; - break; - case USB_SPEED_SUPER: - speed_str = "5000"; - break; - default: - speed_str = "?"; - break; - } - - monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n", - bus_num, addr, port, speed_str); - class_str = usb_class_str(class_id); - if (class_str) { - monitor_printf(mon, " %s:", class_str); - } else { - monitor_printf(mon, " Class %02x:", class_id); - } - monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id); - if (product_name[0] != '\0') { - monitor_printf(mon, ", %s", product_name); - } - monitor_printf(mon, "\n"); -} - -static int usb_host_info_device(void *opaque, int bus_num, int addr, - const char *path, int class_id, - int vendor_id, int product_id, - const char *product_name, - int speed) -{ - Monitor *mon = opaque; - - usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id, - product_name, speed); - return 0; -} - -static void dec2str(int val, char *str, size_t size) -{ - if (val == 0) { - snprintf(str, size, "*"); - } else { - snprintf(str, size, "%d", val); - } -} - -static void hex2str(int val, char *str, size_t size) -{ - if (val == 0) { - snprintf(str, size, "*"); - } else { - snprintf(str, size, "%04x", val); - } -} - -void usb_host_info(Monitor *mon, const QDict *qdict) -{ - struct USBAutoFilter *f; - struct USBHostDevice *s; - - usb_host_scan(mon, usb_host_info_device); - - if (QTAILQ_EMPTY(&hostdevs)) { - return; - } - - monitor_printf(mon, " Auto filters:\n"); - QTAILQ_FOREACH(s, &hostdevs, next) { - char bus[10], addr[10], vid[10], pid[10]; - f = &s->match; - dec2str(f->bus_num, bus, sizeof(bus)); - dec2str(f->addr, addr, sizeof(addr)); - hex2str(f->vendor_id, vid, sizeof(vid)); - hex2str(f->product_id, pid, sizeof(pid)); - monitor_printf(mon, " Bus %s, Addr %s, Port %s, ID %s:%s\n", - bus, addr, f->port ? f->port : "*", vid, pid); - } -} - -#endif diff --git a/include/block/block.h b/include/block/block.h index 728ec1aebf..f808550959 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -142,8 +142,9 @@ BlockDriver *bdrv_find_format(const char *format_name); BlockDriver *bdrv_find_whitelisted_format(const char *format_name, bool readonly); int bdrv_create(BlockDriver *drv, const char* filename, - QEMUOptionParameter *options); -int bdrv_create_file(const char* filename, QEMUOptionParameter *options); + QEMUOptionParameter *options, Error **errp); +int bdrv_create_file(const char* filename, QEMUOptionParameter *options, + Error **errp); BlockDriverState *bdrv_new(const char *device_name); void bdrv_make_anon(BlockDriverState *bs); void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old); @@ -151,10 +152,10 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); int bdrv_parse_cache_flags(const char *mode, int *flags); int bdrv_parse_discard_flags(const char *mode, int *flags); int bdrv_file_open(BlockDriverState **pbs, const char *filename, - QDict *options, int flags); -int bdrv_open_backing_file(BlockDriverState *bs, QDict *options); + QDict *options, int flags, Error **errp); +int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp); int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, - int flags, BlockDriver *drv); + int flags, BlockDriver *drv, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, int flags); int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); @@ -241,6 +242,8 @@ typedef enum { int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); +int bdrv_amend_options(BlockDriverState *bs_new, QEMUOptionParameter *options); + /* async block I/O */ typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector, int sector_num); diff --git a/include/block/block_int.h b/include/block/block_int.h index 7c35198ad7..211087aa91 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -73,6 +73,12 @@ struct BlockDriver { /* Any driver implementing this callback is expected to be able to handle * NULL file names in its .bdrv_open() implementation */ void (*bdrv_parse_filename)(const char *filename, QDict *options, Error **errp); + /* Drivers not implementing bdrv_parse_filename nor bdrv_open should have + * this field set to true, except ones that are defined only by their + * child's bs. + * An example of the last type will be the quorum block driver. + */ + bool bdrv_needs_filename; /* For handling image reopen for split or non-split files */ int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state, @@ -80,15 +86,18 @@ struct BlockDriver { void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state); void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state); - int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags); - int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags); + int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags, + Error **errp); + int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, + Error **errp); int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); void (*bdrv_close)(BlockDriverState *bs); void (*bdrv_rebind)(BlockDriverState *bs); - int (*bdrv_create)(const char *filename, QEMUOptionParameter *options); + int (*bdrv_create)(const char *filename, QEMUOptionParameter *options, + Error **errp); int (*bdrv_set_key)(BlockDriverState *bs, const char *key); int (*bdrv_make_empty)(BlockDriverState *bs); /* aio */ @@ -150,7 +159,10 @@ struct BlockDriver { QEMUSnapshotInfo *sn_info); int (*bdrv_snapshot_goto)(BlockDriverState *bs, const char *snapshot_id); - int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id); + int (*bdrv_snapshot_delete)(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp); int (*bdrv_snapshot_list)(BlockDriverState *bs, QEMUSnapshotInfo **psn_info); int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, @@ -188,6 +200,9 @@ struct BlockDriver { int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result, BdrvCheckMode fix); + int (*bdrv_amend_options)(BlockDriverState *bs, + QEMUOptionParameter *options); + void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ diff --git a/include/block/snapshot.h b/include/block/snapshot.h index eaf61f0326..012bf226d3 100644 --- a/include/block/snapshot.h +++ b/include/block/snapshot.h @@ -26,6 +26,7 @@ #define SNAPSHOT_H #include "qemu-common.h" +#include "qapi/error.h" typedef struct QEMUSnapshotInfo { char id_str[128]; /* unique snapshot id */ @@ -40,12 +41,23 @@ typedef struct QEMUSnapshotInfo { int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, const char *name); +bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, + const char *id, + const char *name, + QEMUSnapshotInfo *sn_info, + Error **errp); int bdrv_can_snapshot(BlockDriverState *bs); int bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int bdrv_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); -int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); +int bdrv_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp); +void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, + const char *id_or_name, + Error **errp); int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info); int bdrv_snapshot_load_tmp(BlockDriverState *bs, diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index a407b50f4a..b6998f055a 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -453,9 +453,7 @@ typedef struct RAMBlock { * Writes must take both locks. */ QTAILQ_ENTRY(RAMBlock) next; -#if defined(__linux__) && !defined(TARGET_S390X) int fd; -#endif } RAMBlock; typedef struct RAMList { diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index beb41491b4..dc27f33152 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -324,9 +324,7 @@ extern uintptr_t tci_tb_ptr; In some implementations, we pass the "logical" return address manually; in others, we must infer the logical return from the true return. */ #if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU) -# if defined (_ARCH_PPC) && !defined (_ARCH_PPC64) -# define GETRA_LDST(RA) (*(int32_t *)((RA) - 4)) -# elif defined(__arm__) +# if defined(__arm__) /* We define two insns between the return address and the branch back to straight-line. Find and decode that branch insn. */ # define GETRA_LDST(RA) tcg_getra_ldst(RA) @@ -368,6 +366,8 @@ static inline uintptr_t tcg_getra_ext(uintptr_t ra) #if !defined(CONFIG_USER_ONLY) +void phys_mem_set_alloc(void *(*alloc)(ram_addr_t)); + struct MemoryRegion *iotlb_to_region(hwaddr index); bool io_mem_read(struct MemoryRegion *mr, hwaddr addr, uint64_t *pvalue, unsigned size); diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index 6eb7ab676f..56de92ede2 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -85,7 +85,7 @@ typedef struct Q35PCIHost { #define MCH_HOST_BRIDGE_CONFIG_DATA 0xcfc /* D0:F0 configuration space */ -#define MCH_HOST_BRIDGE_REVISION_DEFUALT 0x0 +#define MCH_HOST_BRIDGE_REVISION_DEFAULT 0x0 #define MCH_HOST_BRIDGE_PCIEXBAR 0x60 /* 64bit register */ #define MCH_HOST_BRIDGE_PCIEXBAR_SIZE 8 /* 64bit register */ diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 3ddaf6aad5..4c0002beca 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -53,6 +53,7 @@ /* Vendors and devices. Sort key: vendor first, device next. */ #define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_DEVICE_ID_LSI_53C810 0x0001 #define PCI_DEVICE_ID_LSI_53C895A 0x0012 #define PCI_DEVICE_ID_LSI_SAS1078 0x0060 diff --git a/include/hw/s390x/ebcdic.h b/include/hw/s390x/ebcdic.h new file mode 100644 index 0000000000..1d6fde9c12 --- /dev/null +++ b/include/hw/s390x/ebcdic.h @@ -0,0 +1,104 @@ +/* + * EBCDIC/ASCII conversion Support + * + * Copyright (c) 2011 Alexander Graf + * Copyright IBM, Corp. 2013 + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#ifndef EBCDIC_H_ +#define EBCDIC_H_ + +/* EBCDIC handling */ +static const uint8_t ebcdic2ascii[] = { + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07, +}; + +static const uint8_t ascii2ebcdic[] = { + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +static inline void ebcdic_put(uint8_t *p, const char *ascii, int len) +{ + int i; + + for (i = 0; i < len; i++) { + p[i] = ascii2ebcdic[(uint8_t)ascii[i]]; + } +} + +static inline void ascii_put(uint8_t *p, const char *ebcdic, int len) +{ + int i; + + for (i = 0; i < len; i++) { + p[i] = ebcdic2ascii[(uint8_t)ebcdic[i]]; + } +} + +#endif /* EBCDIC_H_ */ diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 791ab2a6de..7ce7079f9f 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -19,12 +19,18 @@ #include "qemu/thread.h" /* SCLP event types */ +#define SCLP_EVENT_OPRTNS_COMMAND 0x01 +#define SCLP_EVENT_MESSAGE 0x02 +#define SCLP_EVENT_PMSGCMD 0x09 #define SCLP_EVENT_ASCII_CONSOLE_DATA 0x1a #define SCLP_EVENT_SIGNAL_QUIESCE 0x1d /* SCLP event masks */ #define SCLP_EVENT_MASK_SIGNAL_QUIESCE 0x00000008 #define SCLP_EVENT_MASK_MSG_ASCII 0x00000040 +#define SCLP_EVENT_MASK_OP_CMD 0x80000000 +#define SCLP_EVENT_MASK_MSG 0x40000000 +#define SCLP_EVENT_MASK_PMSGCMD 0x00800000 #define SCLP_UNCONDITIONAL_READ 0x00 #define SCLP_SELECTIVE_READ 0x01 @@ -43,8 +49,8 @@ typedef struct WriteEventMask { uint16_t mask_length; uint32_t cp_receive_mask; uint32_t cp_send_mask; - uint32_t send_mask; uint32_t receive_mask; + uint32_t send_mask; } QEMU_PACKED WriteEventMask; typedef struct EventBufferHeader { @@ -54,6 +60,80 @@ typedef struct EventBufferHeader { uint16_t _reserved; } QEMU_PACKED EventBufferHeader; +typedef struct MdbHeader { + uint16_t length; + uint16_t type; + uint32_t tag; + uint32_t revision_code; +} QEMU_PACKED MdbHeader; + +typedef struct MTO { + uint16_t line_type_flags; + uint8_t alarm_control; + uint8_t _reserved[3]; + char message[]; +} QEMU_PACKED MTO; + +typedef struct GO { + uint32_t domid; + uint8_t hhmmss_time[8]; + uint8_t th_time[3]; + uint8_t _reserved_0; + uint8_t dddyyyy_date[7]; + uint8_t _reserved_1; + uint16_t general_msg_flags; + uint8_t _reserved_2[10]; + uint8_t originating_system_name[8]; + uint8_t job_guest_name[8]; +} QEMU_PACKED GO; + +#define MESSAGE_TEXT 0x0004 + +typedef struct MDBO { + uint16_t length; + uint16_t type; + union { + GO go; + MTO mto; + }; +} QEMU_PACKED MDBO; + +typedef struct MDB { + MdbHeader header; + MDBO mdbo[0]; +} QEMU_PACKED MDB; + +typedef struct SclpMsg { + EventBufferHeader header; + MDB mdb; +} QEMU_PACKED SclpMsg; + +#define GDS_ID_MDSMU 0x1310 +#define GDS_ID_CPMSU 0x1212 +#define GDS_ID_TEXTCMD 0x1320 + +typedef struct GdsVector { + uint16_t length; + uint16_t gds_id; +} QEMU_PACKED GdsVector; + +#define GDS_KEY_SELFDEFTEXTMSG 0x31 +#define GDS_KEY_TEXTMSG 0x30 + +typedef struct GdsSubvector { + uint8_t length; + uint8_t key; +} QEMU_PACKED GdsSubvector; + +/* MDS Message Unit */ +typedef struct MDMSU { + GdsVector mdmsu; + GdsVector cpmsu; + GdsVector text_command; + GdsSubvector self_def_text_message; + GdsSubvector text_message; +} QEMU_PACKED MDMSU; + typedef struct WriteEventData { SCCBHeader h; EventBufferHeader ebh; @@ -68,7 +148,6 @@ typedef struct ReadEventData { typedef struct SCLPEvent { DeviceState qdev; bool event_pending; - uint32_t event_type; char *name; } SCLPEvent; @@ -88,9 +167,8 @@ typedef struct SCLPEventClass { int (*write_event_data)(SCLPEvent *event, EventBufferHeader *evt_buf_hdr); - /* returns the supported event type */ - int (*event_type)(void); - + /* can we handle this event type? */ + bool (*can_handle_event)(uint8_t type); } SCLPEventClass; #endif diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index b87cf490b1..41885da1a0 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -123,6 +123,7 @@ typedef struct VirtIOBlock { BlockConf *conf; VirtIOBlkConf blk; unsigned short sector_mask; + bool original_wce; VMChangeStateEntry *change; #ifdef CONFIG_VIRTIO_BLK_DATA_PLANE Notifier migration_state_notifier; diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1c31b5d6fb..9d09e60419 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -310,8 +310,18 @@ extern const VMStateInfo vmstate_info_bitmap; .offset = vmstate_offset_value(_state, _field, _type), \ } -#define VMSTATE_STRUCT_POINTER_TEST(_field, _state, _test, _vmsd, _type) { \ +#define VMSTATE_STRUCT_POINTER_V(_field, _state, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_value(_state, _field, _type), \ +} + +#define VMSTATE_STRUCT_POINTER_TEST_V(_field, _state, _test, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ .field_exists = (_test), \ .vmsd = &(_vmsd), \ .size = sizeof(_type), \ @@ -497,7 +507,10 @@ extern const VMStateInfo vmstate_info_bitmap; VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type) #define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \ - VMSTATE_STRUCT_POINTER_TEST(_field, _state, NULL, _vmsd, _type) + VMSTATE_STRUCT_POINTER_V(_field, _state, 0, _vmsd, _type) + +#define VMSTATE_STRUCT_POINTER_TEST(_field, _state, _test, _vmsd, _type) \ + VMSTATE_STRUCT_POINTER_TEST_V(_field, _state, _test, 0, _vmsd, _type) #define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) \ VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, NULL, _version, \ diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index d6855d112e..5cefd8022a 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -67,4 +67,6 @@ const char *qdict_get_try_str(const QDict *qdict, const char *key); QDict *qdict_clone_shallow(const QDict *src); void qdict_flatten(QDict *qdict); +void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); + #endif /* QDICT_H */ diff --git a/include/qemu-common.h b/include/qemu-common.h index 6948bb9177..50548361d0 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -191,6 +191,9 @@ int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix); int64_t strtosz_suffix_unit(const char *nptr, char **end, const char default_suffix, int64_t unit); +/* used to print char* safely */ +#define STR_OR_NULL(str) ((str) ? (str) : "null") + /* path.c */ void init_paths(const char *prefix); const char *path(const char *pathname); diff --git a/include/qemu/timer.h b/include/qemu/timer.h index e4934dd61b..b58903bef5 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -115,6 +115,10 @@ static inline int64_t qemu_clock_get_us(QEMUClockType type) * Determines whether a clock's default timer list * has timers attached * + * Note that this function should not be used when other threads also access + * the timer list. The return value may be outdated by the time it is acted + * upon. + * * Returns: true if the clock's default timer list * has timers attached */ @@ -271,6 +275,10 @@ void timerlist_free(QEMUTimerList *timer_list); * * Determine whether a timer list has active timers * + * Note that this function should not be used when other threads also access + * the timer list. The return value may be outdated by the time it is acted + * upon. + * * Returns: true if the timer list has timers. */ bool timerlist_has_timers(QEMUTimerList *timer_list); @@ -512,6 +520,9 @@ void timer_free(QEMUTimer *ts); * @ts: the timer * * Delete a timer from the active list. + * + * This function is thread-safe but the timer and its timer list must not be + * freed while this function is running. */ void timer_del(QEMUTimer *ts); @@ -521,6 +532,9 @@ void timer_del(QEMUTimer *ts); * @expire_time: the expiry time in nanoseconds * * Modify a timer to expire at @expire_time + * + * This function is thread-safe but the timer and its timer list must not be + * freed while this function is running. */ void timer_mod_ns(QEMUTimer *ts, int64_t expire_time); @@ -531,6 +545,9 @@ void timer_mod_ns(QEMUTimer *ts, int64_t expire_time); * * Modify a timer to expiry at @expire_time, taking into * account the scale associated with the timer. + * + * This function is thread-safe but the timer and its timer list must not be + * freed while this function is running. */ void timer_mod(QEMUTimer *ts, int64_t expire_timer); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 8e7668524b..3b25f27a7c 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -46,6 +46,7 @@ extern bool kvm_halt_in_kernel_allowed; extern bool kvm_irqfds_allowed; extern bool kvm_msi_via_irqfd_allowed; extern bool kvm_gsi_routing_allowed; +extern bool kvm_gsi_direct_mapping; extern bool kvm_readonly_mem_allowed; #if defined CONFIG_KVM || !defined NEED_CPU_H @@ -108,6 +109,13 @@ extern bool kvm_readonly_mem_allowed; #define kvm_gsi_routing_enabled() (kvm_gsi_routing_allowed) /** + * kvm_gsi_direct_mapping: + * + * Returns: true if GSI direct mapping is enabled. + */ +#define kvm_gsi_direct_mapping() (kvm_gsi_direct_mapping) + +/** * kvm_readonly_mem_enabled: * * Returns: true if KVM readonly memory is enabled (ie the kernel @@ -123,6 +131,7 @@ extern bool kvm_readonly_mem_allowed; #define kvm_irqfds_enabled() (false) #define kvm_msi_via_irqfd_enabled() (false) #define kvm_gsi_routing_allowed() (false) +#define kvm_gsi_direct_mapping() (false) #define kvm_readonly_mem_enabled() (false) #endif @@ -161,11 +170,6 @@ int kvm_cpu_exec(CPUState *cpu); #ifdef NEED_CPU_H -#if !defined(CONFIG_USER_ONLY) -void *kvm_ram_alloc(ram_addr_t size); -void *kvm_arch_ram_alloc(ram_addr_t size); -#endif - void kvm_setup_guest_memory(void *start, size_t size); void kvm_flush_coalesced_mmio_buffer(void); @@ -270,16 +274,6 @@ int kvm_check_extension(KVMState *s, unsigned int extension); uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, uint32_t index, int reg); -void kvm_cpu_synchronize_state(CPUState *cpu); - -/* generic hooks - to be moved/refactored once there are more users */ - -static inline void cpu_synchronize_state(CPUState *cpu) -{ - if (kvm_enabled()) { - kvm_cpu_synchronize_state(cpu); - } -} #if !defined(CONFIG_USER_ONLY) int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, @@ -288,9 +282,19 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, #endif /* NEED_CPU_H */ +void kvm_cpu_synchronize_state(CPUState *cpu); void kvm_cpu_synchronize_post_reset(CPUState *cpu); void kvm_cpu_synchronize_post_init(CPUState *cpu); +/* generic hooks - to be moved/refactored once there are more users */ + +static inline void cpu_synchronize_state(CPUState *cpu) +{ + if (kvm_enabled()) { + kvm_cpu_synchronize_state(cpu); + } +} + static inline void cpu_synchronize_post_reset(CPUState *cpu) { if (kvm_enabled()) { diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 42577363ca..cd5791eb74 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -18,7 +18,9 @@ extern const char *qemu_name; extern uint8_t qemu_uuid[]; extern bool qemu_uuid_set; int qemu_uuid_parse(const char *str, uint8_t *uuid); + #define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" +#define UUID_NONE "00000000-0000-0000-0000-000000000000" bool runstate_check(RunState state); void runstate_set(RunState new_state); @@ -40,9 +42,11 @@ int vm_stop(RunState state); int vm_stop_force_state(RunState state); typedef enum WakeupReason { - QEMU_WAKEUP_REASON_OTHER = 0, + /* Always keep QEMU_WAKEUP_REASON_NONE = 0 */ + QEMU_WAKEUP_REASON_NONE = 0, QEMU_WAKEUP_REASON_RTC, QEMU_WAKEUP_REASON_PMTIMER, + QEMU_WAKEUP_REASON_OTHER, } WakeupReason; void qemu_system_reset_request(void); @@ -111,6 +111,7 @@ bool kvm_halt_in_kernel_allowed; bool kvm_irqfds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; +bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; @@ -1069,6 +1070,10 @@ void kvm_irqchip_release_virq(KVMState *s, int virq) struct kvm_irq_routing_entry *e; int i; + if (kvm_gsi_direct_mapping()) { + return; + } + for (i = 0; i < s->irq_routes->nr; i++) { e = &s->irq_routes->entries[i]; if (e->gsi == virq) { @@ -1190,6 +1195,10 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) struct kvm_irq_routing_entry kroute = {}; int virq; + if (kvm_gsi_direct_mapping()) { + return msg.data & 0xffff; + } + if (!kvm_gsi_routing_enabled()) { return -ENOSYS; } @@ -1216,6 +1225,10 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) { struct kvm_irq_routing_entry kroute = {}; + if (kvm_gsi_direct_mapping()) { + return 0; + } + if (!kvm_irqchip_in_kernel()) { return -ENOSYS; } @@ -1322,24 +1335,20 @@ static int kvm_irqchip_create(KVMState *s) return 0; } -static int kvm_max_vcpus(KVMState *s) +/* Find number of supported CPUs using the recommended + * procedure from the kernel API documentation to cope with + * older kernels that may be missing capabilities. + */ +static int kvm_recommended_vcpus(KVMState *s) { - int ret; - - /* Find number of supported CPUs using the recommended - * procedure from the kernel API documentation to cope with - * older kernels that may be missing capabilities. - */ - ret = kvm_check_extension(s, KVM_CAP_MAX_VCPUS); - if (ret) { - return ret; - } - ret = kvm_check_extension(s, KVM_CAP_NR_VCPUS); - if (ret) { - return ret; - } + int ret = kvm_check_extension(s, KVM_CAP_NR_VCPUS); + return (ret) ? ret : 4; +} - return 4; +static int kvm_max_vcpus(KVMState *s) +{ + int ret = kvm_check_extension(s, KVM_CAP_MAX_VCPUS); + return (ret) ? ret : kvm_recommended_vcpus(s); } int kvm_init(void) @@ -1347,11 +1356,19 @@ int kvm_init(void) static const char upgrade_note[] = "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" "(see http://sourceforge.net/projects/kvm).\n"; + struct { + const char *name; + int num; + } num_cpus[] = { + { "SMP", smp_cpus }, + { "hotpluggable", max_cpus }, + { NULL, } + }, *nc = num_cpus; + int soft_vcpus_limit, hard_vcpus_limit; KVMState *s; const KVMCapabilityInfo *missing_cap; int ret; int i; - int max_vcpus; s = g_malloc0(sizeof(KVMState)); @@ -1392,19 +1409,26 @@ int kvm_init(void) goto err; } - max_vcpus = kvm_max_vcpus(s); - if (smp_cpus > max_vcpus) { - ret = -EINVAL; - fprintf(stderr, "Number of SMP cpus requested (%d) exceeds max cpus " - "supported by KVM (%d)\n", smp_cpus, max_vcpus); - goto err; - } + /* check the vcpu limits */ + soft_vcpus_limit = kvm_recommended_vcpus(s); + hard_vcpus_limit = kvm_max_vcpus(s); - if (max_cpus > max_vcpus) { - ret = -EINVAL; - fprintf(stderr, "Number of hotpluggable cpus requested (%d) exceeds max cpus " - "supported by KVM (%d)\n", max_cpus, max_vcpus); - goto err; + while (nc->name) { + if (nc->num > soft_vcpus_limit) { + fprintf(stderr, + "Warning: Number of %s cpus requested (%d) exceeds " + "the recommended cpus supported by KVM (%d)\n", + nc->name, nc->num, soft_vcpus_limit); + + if (nc->num > hard_vcpus_limit) { + ret = -EINVAL; + fprintf(stderr, "Number of %s cpus requested (%d) exceeds " + "the maximum cpus supported by KVM (%d)\n", + nc->name, nc->num, hard_vcpus_limit); + goto err; + } + } + nc++; } s->vmfd = kvm_ioctl(s, KVM_CREATE_VM, 0); @@ -1812,19 +1836,6 @@ int kvm_has_intx_set_mask(void) return kvm_state->intx_set_mask; } -void *kvm_ram_alloc(ram_addr_t size) -{ -#ifdef TARGET_S390X - void *mem; - - mem = kvm_arch_ram_alloc(size); - if (mem) { - return mem; - } -#endif - return qemu_anon_ram_alloc(size); -} - void kvm_setup_guest_memory(void *start, size_t size) { #ifdef CONFIG_VALGRIND_H diff --git a/kvm-stub.c b/kvm-stub.c index 548f471c17..e979f76d07 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -25,6 +25,7 @@ bool kvm_async_interrupts_allowed; bool kvm_irqfds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; +bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; diff --git a/libcacard/Makefile b/libcacard/Makefile index 47827a0eb8..4d15da49b8 100644 --- a/libcacard/Makefile +++ b/libcacard/Makefile @@ -4,7 +4,8 @@ TOOLS += vscclient$(EXESUF) # objects linked into a shared library, built with libtool with -fPIC if required libcacard-obj-y = $(stub-obj-y) $(libcacard-y) -libcacard-obj-y += util/osdep.o util/cutils.o util/qemu-timer-common.o util/error.o +libcacard-obj-y += util/osdep.o util/cutils.o util/qemu-timer-common.o +libcacard-obj-y += util/error.o util/qemu-error.o libcacard-obj-$(CONFIG_WIN32) += util/oslib-win32.o util/qemu-thread-win32.o libcacard-obj-$(CONFIG_POSIX) += util/oslib-posix.o util/qemu-thread-posix.o libcacard-obj-y += $(filter trace/%, $(util-obj-y)) diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c index 5180d2945a..a3cb7762b5 100644 --- a/libcacard/vscclient.c +++ b/libcacard/vscclient.c @@ -645,7 +645,6 @@ main( GIOChannel *channel_stdin; char *qemu_host; char *qemu_port; - VSCMsgHeader mhHeader; VCardEmulOptions *command_line_options = NULL; @@ -754,7 +753,7 @@ main( .magic = VSCARD_MAGIC, .capabilities = {0} }; - send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init)); + send_msg(VSC_Init, 0, &init, sizeof(init)); g_main_loop_run(loop); g_main_loop_unref(loop); diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h new file mode 100644 index 0000000000..5031f42639 --- /dev/null +++ b/linux-headers/asm-arm64/kvm.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier <marc.zyngier@arm.com> + * + * Derived from arch/arm/include/uapi/asm/kvm.h: + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall <c.dall@virtualopensystems.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __ARM_KVM_H__ +#define __ARM_KVM_H__ + +#define KVM_SPSR_EL1 0 +#define KVM_SPSR_SVC KVM_SPSR_EL1 +#define KVM_SPSR_ABT 1 +#define KVM_SPSR_UND 2 +#define KVM_SPSR_IRQ 3 +#define KVM_SPSR_FIQ 4 +#define KVM_NR_SPSR 5 + +#ifndef __ASSEMBLY__ +#include <asm/types.h> +#include <asm/ptrace.h> + +#define __KVM_HAVE_GUEST_DEBUG +#define __KVM_HAVE_IRQ_LINE + +#define KVM_REG_SIZE(id) \ + (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) + +struct kvm_regs { + struct user_pt_regs regs; /* sp = sp_el0 */ + + __u64 sp_el1; + __u64 elr_el1; + + __u64 spsr[KVM_NR_SPSR]; + + struct user_fpsimd_state fp_regs; +}; + +/* Supported Processor Types */ +#define KVM_ARM_TARGET_AEM_V8 0 +#define KVM_ARM_TARGET_FOUNDATION_V8 1 +#define KVM_ARM_TARGET_CORTEX_A57 2 + +#define KVM_ARM_NUM_TARGETS 3 + +/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ +#define KVM_ARM_DEVICE_TYPE_SHIFT 0 +#define KVM_ARM_DEVICE_TYPE_MASK (0xffff << KVM_ARM_DEVICE_TYPE_SHIFT) +#define KVM_ARM_DEVICE_ID_SHIFT 16 +#define KVM_ARM_DEVICE_ID_MASK (0xffff << KVM_ARM_DEVICE_ID_SHIFT) + +/* Supported device IDs */ +#define KVM_ARM_DEVICE_VGIC_V2 0 + +/* Supported VGIC address types */ +#define KVM_VGIC_V2_ADDR_TYPE_DIST 0 +#define KVM_VGIC_V2_ADDR_TYPE_CPU 1 + +#define KVM_VGIC_V2_DIST_SIZE 0x1000 +#define KVM_VGIC_V2_CPU_SIZE 0x2000 + +#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ +#define KVM_ARM_VCPU_EL1_32BIT 1 /* CPU running a 32bit VM */ + +struct kvm_vcpu_init { + __u32 target; + __u32 features[7]; +}; + +struct kvm_sregs { +}; + +struct kvm_fpu { +}; + +struct kvm_guest_debug_arch { +}; + +struct kvm_debug_exit_arch { +}; + +struct kvm_sync_regs { +}; + +struct kvm_arch_memory_slot { +}; + +/* If you need to interpret the index values, here is the key: */ +#define KVM_REG_ARM_COPROC_MASK 0x000000000FFF0000 +#define KVM_REG_ARM_COPROC_SHIFT 16 + +/* Normal registers are mapped as coprocessor 16. */ +#define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / sizeof(__u32)) + +/* Some registers need more space to represent values. */ +#define KVM_REG_ARM_DEMUX (0x0011 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_DEMUX_ID_MASK 0x000000000000FF00 +#define KVM_REG_ARM_DEMUX_ID_SHIFT 8 +#define KVM_REG_ARM_DEMUX_ID_CCSIDR (0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT) +#define KVM_REG_ARM_DEMUX_VAL_MASK 0x00000000000000FF +#define KVM_REG_ARM_DEMUX_VAL_SHIFT 0 + +/* AArch64 system registers */ +#define KVM_REG_ARM64_SYSREG (0x0013 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM64_SYSREG_OP0_MASK 0x000000000000c000 +#define KVM_REG_ARM64_SYSREG_OP0_SHIFT 14 +#define KVM_REG_ARM64_SYSREG_OP1_MASK 0x0000000000003800 +#define KVM_REG_ARM64_SYSREG_OP1_SHIFT 11 +#define KVM_REG_ARM64_SYSREG_CRN_MASK 0x0000000000000780 +#define KVM_REG_ARM64_SYSREG_CRN_SHIFT 7 +#define KVM_REG_ARM64_SYSREG_CRM_MASK 0x0000000000000078 +#define KVM_REG_ARM64_SYSREG_CRM_SHIFT 3 +#define KVM_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007 +#define KVM_REG_ARM64_SYSREG_OP2_SHIFT 0 + +/* KVM_IRQ_LINE irq field index values */ +#define KVM_ARM_IRQ_TYPE_SHIFT 24 +#define KVM_ARM_IRQ_TYPE_MASK 0xff +#define KVM_ARM_IRQ_VCPU_SHIFT 16 +#define KVM_ARM_IRQ_VCPU_MASK 0xff +#define KVM_ARM_IRQ_NUM_SHIFT 0 +#define KVM_ARM_IRQ_NUM_MASK 0xffff + +/* irq_type field */ +#define KVM_ARM_IRQ_TYPE_CPU 0 +#define KVM_ARM_IRQ_TYPE_SPI 1 +#define KVM_ARM_IRQ_TYPE_PPI 2 + +/* out-of-kernel GIC cpu interrupt injection irq_number field */ +#define KVM_ARM_IRQ_CPU_IRQ 0 +#define KVM_ARM_IRQ_CPU_FIQ 1 + +/* Highest supported SPI, from VGIC_NR_IRQS */ +#define KVM_ARM_IRQ_GIC_MAX 127 + +/* PSCI interface */ +#define KVM_PSCI_FN_BASE 0x95c1ba5e +#define KVM_PSCI_FN(n) (KVM_PSCI_FN_BASE + (n)) + +#define KVM_PSCI_FN_CPU_SUSPEND KVM_PSCI_FN(0) +#define KVM_PSCI_FN_CPU_OFF KVM_PSCI_FN(1) +#define KVM_PSCI_FN_CPU_ON KVM_PSCI_FN(2) +#define KVM_PSCI_FN_MIGRATE KVM_PSCI_FN(3) + +#define KVM_PSCI_RET_SUCCESS 0 +#define KVM_PSCI_RET_NI ((unsigned long)-1) +#define KVM_PSCI_RET_INVAL ((unsigned long)-2) +#define KVM_PSCI_RET_DENIED ((unsigned long)-3) + +#endif + +#endif /* __ARM_KVM_H__ */ diff --git a/linux-headers/asm-arm64/kvm_para.h b/linux-headers/asm-arm64/kvm_para.h new file mode 100644 index 0000000000..14fab8f0b9 --- /dev/null +++ b/linux-headers/asm-arm64/kvm_para.h @@ -0,0 +1 @@ +#include <asm-generic/kvm_para.h> diff --git a/linux-headers/asm-mips/kvm.h b/linux-headers/asm-mips/kvm.h index 3f424f5217..f09ff5ae20 100644 --- a/linux-headers/asm-mips/kvm.h +++ b/linux-headers/asm-mips/kvm.h @@ -58,56 +58,53 @@ struct kvm_fpu { * bits[2..0] - Register 'sel' index. * bits[7..3] - Register 'rd' index. * bits[15..8] - Must be zero. - * bits[63..16] - 1 -> CP0 registers. + * bits[31..16] - 1 -> CP0 registers. + * bits[51..32] - Must be zero. + * bits[63..52] - As per linux/kvm.h * * Other sets registers may be added in the future. Each set would - * have its own identifier in bits[63..16]. - * - * The addr field of struct kvm_one_reg must point to an aligned - * 64-bit wide location. For registers that are narrower than - * 64-bits, the value is stored in the low order bits of the location, - * and sign extended to 64-bits. + * have its own identifier in bits[31..16]. * * The registers defined in struct kvm_regs are also accessible, the * id values for these are below. */ -#define KVM_REG_MIPS_R0 0 -#define KVM_REG_MIPS_R1 1 -#define KVM_REG_MIPS_R2 2 -#define KVM_REG_MIPS_R3 3 -#define KVM_REG_MIPS_R4 4 -#define KVM_REG_MIPS_R5 5 -#define KVM_REG_MIPS_R6 6 -#define KVM_REG_MIPS_R7 7 -#define KVM_REG_MIPS_R8 8 -#define KVM_REG_MIPS_R9 9 -#define KVM_REG_MIPS_R10 10 -#define KVM_REG_MIPS_R11 11 -#define KVM_REG_MIPS_R12 12 -#define KVM_REG_MIPS_R13 13 -#define KVM_REG_MIPS_R14 14 -#define KVM_REG_MIPS_R15 15 -#define KVM_REG_MIPS_R16 16 -#define KVM_REG_MIPS_R17 17 -#define KVM_REG_MIPS_R18 18 -#define KVM_REG_MIPS_R19 19 -#define KVM_REG_MIPS_R20 20 -#define KVM_REG_MIPS_R21 21 -#define KVM_REG_MIPS_R22 22 -#define KVM_REG_MIPS_R23 23 -#define KVM_REG_MIPS_R24 24 -#define KVM_REG_MIPS_R25 25 -#define KVM_REG_MIPS_R26 26 -#define KVM_REG_MIPS_R27 27 -#define KVM_REG_MIPS_R28 28 -#define KVM_REG_MIPS_R29 29 -#define KVM_REG_MIPS_R30 30 -#define KVM_REG_MIPS_R31 31 +#define KVM_REG_MIPS_R0 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 0) +#define KVM_REG_MIPS_R1 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 1) +#define KVM_REG_MIPS_R2 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 2) +#define KVM_REG_MIPS_R3 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 3) +#define KVM_REG_MIPS_R4 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 4) +#define KVM_REG_MIPS_R5 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 5) +#define KVM_REG_MIPS_R6 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 6) +#define KVM_REG_MIPS_R7 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 7) +#define KVM_REG_MIPS_R8 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 8) +#define KVM_REG_MIPS_R9 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 9) +#define KVM_REG_MIPS_R10 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 10) +#define KVM_REG_MIPS_R11 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 11) +#define KVM_REG_MIPS_R12 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 12) +#define KVM_REG_MIPS_R13 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 13) +#define KVM_REG_MIPS_R14 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 14) +#define KVM_REG_MIPS_R15 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 15) +#define KVM_REG_MIPS_R16 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 16) +#define KVM_REG_MIPS_R17 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 17) +#define KVM_REG_MIPS_R18 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 18) +#define KVM_REG_MIPS_R19 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 19) +#define KVM_REG_MIPS_R20 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 20) +#define KVM_REG_MIPS_R21 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 21) +#define KVM_REG_MIPS_R22 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 22) +#define KVM_REG_MIPS_R23 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 23) +#define KVM_REG_MIPS_R24 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 24) +#define KVM_REG_MIPS_R25 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 25) +#define KVM_REG_MIPS_R26 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 26) +#define KVM_REG_MIPS_R27 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 27) +#define KVM_REG_MIPS_R28 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 28) +#define KVM_REG_MIPS_R29 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 29) +#define KVM_REG_MIPS_R30 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 30) +#define KVM_REG_MIPS_R31 (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 31) -#define KVM_REG_MIPS_HI 32 -#define KVM_REG_MIPS_LO 33 -#define KVM_REG_MIPS_PC 34 +#define KVM_REG_MIPS_HI (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 32) +#define KVM_REG_MIPS_LO (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 33) +#define KVM_REG_MIPS_PC (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 34) /* * KVM MIPS specific structures and definitions diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h index a1c3d72acd..e41c5c1a28 100644 --- a/linux-headers/asm-x86/kvm_para.h +++ b/linux-headers/asm-x86/kvm_para.h @@ -23,6 +23,7 @@ #define KVM_FEATURE_ASYNC_PF 4 #define KVM_FEATURE_STEAL_TIME 5 #define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_PV_UNHALT 7 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index c614070662..13e890c53b 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -666,6 +666,8 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_IRQ_MPIC 90 #define KVM_CAP_PPC_RTAS 91 #define KVM_CAP_IRQ_XICS 92 +#define KVM_CAP_ARM_EL1_32BIT 93 +#define KVM_CAP_SPAPR_MULTITCE 94 #ifdef KVM_CAP_IRQ_ROUTING @@ -783,6 +785,8 @@ struct kvm_dirty_tlb { #define KVM_REG_IA64 0x3000000000000000ULL #define KVM_REG_ARM 0x4000000000000000ULL #define KVM_REG_S390 0x5000000000000000ULL +#define KVM_REG_ARM64 0x6000000000000000ULL +#define KVM_REG_MIPS 0x7000000000000000ULL #define KVM_REG_SIZE_SHIFT 52 #define KVM_REG_SIZE_MASK 0x00f0000000000000ULL diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h index 7bdcf93c1d..2dff7838b4 100644 --- a/linux-headers/linux/kvm_para.h +++ b/linux-headers/linux/kvm_para.h @@ -19,6 +19,7 @@ #define KVM_HC_MMU_OP 2 #define KVM_HC_FEATURES 3 #define KVM_HC_PPC_MAP_MAGIC_PAGE 4 +#define KVM_HC_KICK_CPU 5 /* * hypercalls use architecture specific diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 7ec1864765..17c58e0ede 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -22,6 +22,7 @@ /* Extensions */ #define VFIO_TYPE1_IOMMU 1 +#define VFIO_SPAPR_TCE_IOMMU 2 /* * The IOCTL interface is designed for extensibility by embedding the @@ -323,6 +324,44 @@ enum { VFIO_PCI_NUM_IRQS }; +/** + * VFIO_DEVICE_GET_PCI_HOT_RESET_INFO - _IORW(VFIO_TYPE, VFIO_BASE + 12, + * struct vfio_pci_hot_reset_info) + * + * Return: 0 on success, -errno on failure: + * -enospc = insufficient buffer, -enodev = unsupported for device. + */ +struct vfio_pci_dependent_device { + __u32 group_id; + __u16 segment; + __u8 bus; + __u8 devfn; /* Use PCI_SLOT/PCI_FUNC */ +}; + +struct vfio_pci_hot_reset_info { + __u32 argsz; + __u32 flags; + __u32 count; + struct vfio_pci_dependent_device devices[]; +}; + +#define VFIO_DEVICE_GET_PCI_HOT_RESET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12) + +/** + * VFIO_DEVICE_PCI_HOT_RESET - _IOW(VFIO_TYPE, VFIO_BASE + 13, + * struct vfio_pci_hot_reset) + * + * Return: 0 on success, -errno on failure. + */ +struct vfio_pci_hot_reset { + __u32 argsz; + __u32 flags; + __u32 count; + __s32 group_fds[]; +}; + +#define VFIO_DEVICE_PCI_HOT_RESET _IO(VFIO_TYPE, VFIO_BASE + 13) + /* -------- API for Type1 VFIO IOMMU -------- */ /** @@ -361,10 +400,14 @@ struct vfio_iommu_type1_dma_map { #define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13) /** - * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap) + * VFIO_IOMMU_UNMAP_DMA - _IOWR(VFIO_TYPE, VFIO_BASE + 14, + * struct vfio_dma_unmap) * * Unmap IO virtual addresses using the provided struct vfio_dma_unmap. - * Caller sets argsz. + * Caller sets argsz. The actual unmapped size is returned in the size + * field. No guarantee is made to the user that arbitrary unmaps of iova + * or size different from those used in the original mapping call will + * succeed. */ struct vfio_iommu_type1_dma_unmap { __u32 argsz; @@ -375,4 +418,37 @@ struct vfio_iommu_type1_dma_unmap { #define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14) +/* + * IOCTLs to enable/disable IOMMU container usage. + * No parameters are supported. + */ +#define VFIO_IOMMU_ENABLE _IO(VFIO_TYPE, VFIO_BASE + 15) +#define VFIO_IOMMU_DISABLE _IO(VFIO_TYPE, VFIO_BASE + 16) + +/* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */ + +/* + * The SPAPR TCE info struct provides the information about the PCI bus + * address ranges available for DMA, these values are programmed into + * the hardware so the guest has to know that information. + * + * The DMA 32 bit window start is an absolute PCI bus address. + * The IOVA address passed via map/unmap ioctls are absolute PCI bus + * addresses too so the window works as a filter rather than an offset + * for IOVA addresses. + * + * A flag will need to be added if other page sizes are supported, + * so as defined here, it is always 4k. + */ +struct vfio_iommu_spapr_tce_info { + __u32 argsz; + __u32 flags; /* reserved for future use */ + __u32 dma32_window_start; /* 32 bit window start (bytes) */ + __u32 dma32_window_size; /* 32 bit window size (bytes) */ +}; + +#define VFIO_IOMMU_SPAPR_TCE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12) + +/* ***************************************************************** */ + #endif /* VFIO_H */ diff --git a/linux-headers/linux/virtio_config.h b/linux-headers/linux/virtio_config.h index 4f51d8f3af..75dc20ba6f 100644 --- a/linux-headers/linux/virtio_config.h +++ b/linux-headers/linux/virtio_config.h @@ -51,4 +51,7 @@ * suppressed them? */ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 +/* Can the device handle any descriptor layout? */ +#define VIRTIO_F_ANY_LAYOUT 27 + #endif /* _LINUX_VIRTIO_CONFIG_H */ diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h index ac2b6e2c65..d52d76e08e 100644 --- a/linux-user/alpha/syscall_nr.h +++ b/linux-user/alpha/syscall_nr.h @@ -20,7 +20,7 @@ #define TARGET_NR_lseek 19 #define TARGET_NR_getxpid 20 #define TARGET_NR_osf_mount 21 -#define TARGET_NR_umount 22 +#define TARGET_NR_umount2 22 #define TARGET_NR_setuid 23 #define TARGET_NR_getxuid 24 #define TARGET_NR_exec_with_loader 25 /* not implemented */ @@ -255,7 +255,7 @@ #define TARGET_NR_sysinfo 318 #define TARGET_NR__sysctl 319 /* 320 was sys_idle. */ -#define TARGET_NR_oldumount 321 +#define TARGET_NR_umount 321 #define TARGET_NR_swapon 322 #define TARGET_NR_times 323 #define TARGET_NR_personality 324 diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 439c2a9e32..7381012d3d 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -20,6 +20,7 @@ IOCTL(TIOCSCTTY, 0, TYPE_INT) IOCTL(TIOCGPGRP, IOC_R, MK_PTR(TYPE_INT)) IOCTL(TIOCSPGRP, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(TIOCGSID, IOC_W, MK_PTR(TYPE_INT)) IOCTL(TIOCOUTQ, IOC_R, MK_PTR(TYPE_INT)) IOCTL(TIOCSTI, IOC_W, MK_PTR(TYPE_INT)) IOCTL(TIOCMGET, IOC_R, MK_PTR(TYPE_INT)) diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 5cd6d91554..a1fe5ed9ae 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -131,7 +131,7 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, return sp; } -int loader_exec(const char * filename, char ** argv, char ** envp, +int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct target_pt_regs * regs, struct image_info *infop, struct linux_binprm *bprm) { @@ -140,11 +140,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp, bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); memset(bprm->page, 0, sizeof(bprm->page)); - retval = open(filename, O_RDONLY); - if (retval < 0) { - return -errno; - } - bprm->fd = retval; + bprm->fd = fdexec; bprm->filename = (char *)filename; bprm->argc = count(argv); bprm->argv = argv; diff --git a/linux-user/main.c b/linux-user/main.c index 01e3cd4cc1..1561950bf5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1861,7 +1861,7 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_lseek , 3) MIPS_SYS(sys_getpid , 0) /* 4020 */ MIPS_SYS(sys_mount , 5) - MIPS_SYS(sys_oldumount , 1) + MIPS_SYS(sys_umount , 1) MIPS_SYS(sys_setuid , 1) MIPS_SYS(sys_getuid , 0) MIPS_SYS(sys_stime , 1) /* 4025 */ @@ -1891,7 +1891,7 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_geteuid , 0) MIPS_SYS(sys_getegid , 0) /* 4050 */ MIPS_SYS(sys_acct , 0) - MIPS_SYS(sys_umount , 2) + MIPS_SYS(sys_umount2 , 2) MIPS_SYS(sys_ni_syscall , 0) MIPS_SYS(sys_ioctl , 3) MIPS_SYS(sys_fcntl , 3) /* 4055 */ @@ -2400,12 +2400,31 @@ done_syscall: if (env->hflags & MIPS_HFLAG_M16) { if (env->insn_flags & ASE_MICROMIPS) { /* microMIPS mode */ - abi_ulong instr[2]; - - ret = get_user_u16(instr[0], env->active_tc.PC) || - get_user_u16(instr[1], env->active_tc.PC + 2); + ret = get_user_u16(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } - trap_instr = (instr[0] << 16) | instr[1]; + if ((trap_instr >> 10) == 0x11) { + /* 16-bit instruction */ + code = trap_instr & 0xf; + } else { + /* 32-bit instruction */ + abi_ulong instr_lo; + + ret = get_user_u16(instr_lo, + env->active_tc.PC + 2); + if (ret != 0) { + goto error; + } + trap_instr = (trap_instr << 16) | instr_lo; + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + /* Unfortunately, microMIPS also suffers from + the old assembler bug... */ + if (code >= (1 << 10)) { + code >>= 10; + } + } } else { /* MIPS16e mode */ ret = get_user_u16(trap_instr, env->active_tc.PC); @@ -2413,26 +2432,21 @@ done_syscall: goto error; } code = (trap_instr >> 6) & 0x3f; - if (do_break(env, &info, code) != 0) { - goto error; - } - break; } } else { ret = get_user_ual(trap_instr, env->active_tc.PC); - } - - if (ret != 0) { - goto error; - } + if (ret != 0) { + goto error; + } - /* As described in the original Linux kernel code, the - * below checks on 'code' are to work around an old - * assembly bug. - */ - code = ((trap_instr >> 6) & ((1 << 20) - 1)); - if (code >= (1 << 10)) { - code >>= 10; + /* As described in the original Linux kernel code, the + * below checks on 'code' are to work around an old + * assembly bug. + */ + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + if (code >= (1 << 10)) { + code >>= 10; + } } if (do_break(env, &info, code) != 0) { @@ -3618,6 +3632,26 @@ static int parse_args(int argc, char **argv) return optind; } +static int get_execfd(char **envp) +{ + typedef struct { + long a_type; + long a_val; + } auxv_t; + auxv_t *auxv; + + while (*envp++ != NULL) { + ; + } + + for (auxv = (auxv_t *)envp; auxv->a_type != AT_NULL; auxv++) { + if (auxv->a_type == AT_EXECFD) { + return auxv->a_val; + } + } + return -1; +} + int main(int argc, char **argv, char **envp) { struct target_pt_regs regs1, *regs = ®s1; @@ -3632,6 +3666,7 @@ int main(int argc, char **argv, char **envp) int target_argc; int i; int ret; + int execfd; module_call_init(MODULE_INIT_QOM); @@ -3809,7 +3844,16 @@ int main(int argc, char **argv, char **envp) env->opaque = ts; task_settid(ts); - ret = loader_exec(filename, target_argv, target_environ, regs, + execfd = get_execfd(envp); + if (execfd < 0) { + execfd = open(filename, O_RDONLY); + } + if (execfd < 0) { + printf("Error while loading %s: %s\n", filename, strerror(-execfd)); + _exit(1); + } + + ret = loader_exec(execfd, filename, target_argv, target_environ, regs, info, &bprm); if (ret != 0) { printf("Error while loading %s: %s\n", filename, strerror(-ret)); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 6ffe5a2dec..da64e877c7 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -174,7 +174,7 @@ struct linux_binprm { void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); -int loader_exec(const char * filename, char ** argv, char ** envp, +int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct target_pt_regs * regs, struct image_info *infop, struct linux_binprm *); @@ -380,9 +380,9 @@ abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len); abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len); /* Functions for accessing guest memory. The tget and tput functions - read/write single values, byteswapping as necessary. The lock_user + read/write single values, byteswapping as necessary. The lock_user function gets a pointer to a contiguous area of guest memory, but does not perform - and byteswapping. lock_user may return either a pointer to the guest + any byteswapping. lock_user may return either a pointer to the guest memory, or a temporary buffer. */ /* Lock an area of guest memory into the host. If copy is true then the @@ -438,7 +438,7 @@ static inline void *lock_user_string(abi_ulong guest_addr) return lock_user(VERIFY_READ, guest_addr, (long)(len + 1), 1); } -/* Helper macros for locking/ulocking a target struct. */ +/* Helper macros for locking/unlocking a target struct. */ #define lock_user_struct(type, host_ptr, guest_addr, copy) \ (host_ptr = lock_user(type, guest_addr, sizeof(*host_ptr), copy)) #define unlock_user_struct(host_ptr, guest_addr, copy) \ diff --git a/linux-user/strace.list b/linux-user/strace.list index 08f115d843..cf5841af60 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -612,9 +612,6 @@ #ifdef TARGET_NR_oldstat { TARGET_NR_oldstat, "oldstat" , NULL, NULL, NULL }, #endif -#ifdef TARGET_NR_oldumount -{ TARGET_NR_oldumount, "oldumount" , NULL, NULL, NULL }, -#endif #ifdef TARGET_NR_olduname { TARGET_NR_olduname, "olduname" , NULL, NULL, NULL }, #endif @@ -1524,3 +1521,9 @@ #ifdef TARGET_NR_pipe2 { TARGET_NR_pipe2, "pipe2", NULL, NULL, NULL }, #endif +#ifdef TARGET_NR_atomic_cmpxchg_32 +{ TARGET_NR_atomic_cmpxchg_32, "atomic_cmpxchg_32", NULL, NULL, NULL }, +#endif +#ifdef TARGET_NR_atomic_barrier +{ TARGET_NR_atomic_barrier, "atomic_barrier", NULL, NULL, NULL }, +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c62d8754f0..4a14a43037 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -106,6 +106,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include <linux/dm-ioctl.h> #include <linux/reboot.h> #include <linux/route.h> +#include <linux/filter.h> #include "linux_loop.h" #include "cpu-uname.h" @@ -1149,11 +1150,15 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, break; } - cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level); + if (tswap32(target_cmsg->cmsg_level) == TARGET_SOL_SOCKET) { + cmsg->cmsg_level = SOL_SOCKET; + } else { + cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level); + } cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type); cmsg->cmsg_len = CMSG_LEN(len); - if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); memcpy(data, target_data, len); } else { @@ -1204,11 +1209,15 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, break; } - target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level); + if (cmsg->cmsg_level == SOL_SOCKET) { + target_cmsg->cmsg_level = tswap32(TARGET_SOL_SOCKET); + } else { + target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level); + } target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type); target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(len)); - if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) && + if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_RIGHTS)) { int *fd = (int *)data; int *target_fd = (int *)target_data; @@ -1216,7 +1225,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, for (i = 0; i < numfds; i++) target_fd[i] = tswap32(fd[i]); - } else if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) && + } else if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP) && (len == sizeof(struct timeval))) { /* copy struct timeval to target */ @@ -1314,6 +1323,26 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, goto unimplemented; } break; + case SOL_IPV6: + switch (optname) { + case IPV6_MTU_DISCOVER: + case IPV6_MTU: + case IPV6_V6ONLY: + case IPV6_RECVPKTINFO: + val = 0; + if (optlen < sizeof(uint32_t)) { + return -TARGET_EINVAL; + } + if (get_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + ret = get_errno(setsockopt(sockfd, level, optname, + &val, sizeof(val))); + break; + default: + goto unimplemented; + } + break; case SOL_RAW: switch (optname) { case ICMP_FILTER: @@ -1357,6 +1386,49 @@ set_timeout: case TARGET_SO_SNDTIMEO: optname = SO_SNDTIMEO; goto set_timeout; + case TARGET_SO_ATTACH_FILTER: + { + struct target_sock_fprog *tfprog; + struct target_sock_filter *tfilter; + struct sock_fprog fprog; + struct sock_filter *filter; + int i; + + if (optlen != sizeof(*tfprog)) { + return -TARGET_EINVAL; + } + if (!lock_user_struct(VERIFY_READ, tfprog, optval_addr, 0)) { + return -TARGET_EFAULT; + } + if (!lock_user_struct(VERIFY_READ, tfilter, + tswapal(tfprog->filter), 0)) { + unlock_user_struct(tfprog, optval_addr, 1); + return -TARGET_EFAULT; + } + + fprog.len = tswap16(tfprog->len); + filter = malloc(fprog.len * sizeof(*filter)); + if (filter == NULL) { + unlock_user_struct(tfilter, tfprog->filter, 1); + unlock_user_struct(tfprog, optval_addr, 1); + return -TARGET_ENOMEM; + } + for (i = 0; i < fprog.len; i++) { + filter[i].code = tswap16(tfilter[i].code); + filter[i].jt = tfilter[i].jt; + filter[i].jf = tfilter[i].jf; + filter[i].k = tswap32(tfilter[i].k); + } + fprog.filter = filter; + + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, + SO_ATTACH_FILTER, &fprog, sizeof(fprog))); + free(filter); + + unlock_user_struct(tfilter, tfprog->filter, 1); + unlock_user_struct(tfprog, optval_addr, 1); + return ret; + } /* Options with 'int' argument. */ case TARGET_SO_DEBUG: optname = SO_DEBUG; @@ -1701,7 +1773,7 @@ static void unlock_iovec(struct iovec *vec, abi_ulong target_addr, free(vec); } -static inline void target_to_host_sock_type(int *type) +static inline int target_to_host_sock_type(int *type) { int host_type = 0; int target_type = *type; @@ -1718,22 +1790,56 @@ static inline void target_to_host_sock_type(int *type) break; } if (target_type & TARGET_SOCK_CLOEXEC) { +#if defined(SOCK_CLOEXEC) host_type |= SOCK_CLOEXEC; +#else + return -TARGET_EINVAL; +#endif } if (target_type & TARGET_SOCK_NONBLOCK) { +#if defined(SOCK_NONBLOCK) host_type |= SOCK_NONBLOCK; +#elif !defined(O_NONBLOCK) + return -TARGET_EINVAL; +#endif } *type = host_type; + return 0; +} + +/* Try to emulate socket type flags after socket creation. */ +static int sock_flags_fixup(int fd, int target_type) +{ +#if !defined(SOCK_NONBLOCK) && defined(O_NONBLOCK) + if (target_type & TARGET_SOCK_NONBLOCK) { + int flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) == -1) { + close(fd); + return -TARGET_EINVAL; + } + } +#endif + return fd; } /* do_socket() Must return target values and target errnos. */ static abi_long do_socket(int domain, int type, int protocol) { - target_to_host_sock_type(&type); + int target_type = type; + int ret; + + ret = target_to_host_sock_type(&type); + if (ret) { + return ret; + } if (domain == PF_NETLINK) return -EAFNOSUPPORT; /* do not NETLINK socket connections possible */ - return get_errno(socket(domain, type, protocol)); + ret = get_errno(socket(domain, type, protocol)); + if (ret >= 0) { + ret = sock_flags_fixup(ret, target_type); + } + return ret; } /* do_bind() Must return target values and target errnos. */ @@ -5071,22 +5177,70 @@ static int is_proc_myself(const char *filename, const char *entry) return 0; } +#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) +static int is_proc(const char *filename, const char *entry) +{ + return strcmp(filename, entry) == 0; +} + +static int open_net_route(void *cpu_env, int fd) +{ + FILE *fp; + char *line = NULL; + size_t len = 0; + ssize_t read; + + fp = fopen("/proc/net/route", "r"); + if (fp == NULL) { + return -EACCES; + } + + /* read header */ + + read = getline(&line, &len, fp); + dprintf(fd, "%s", line); + + /* read routes */ + + while ((read = getline(&line, &len, fp)) != -1) { + char iface[16]; + uint32_t dest, gw, mask; + unsigned int flags, refcnt, use, metric, mtu, window, irtt; + sscanf(line, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n", + iface, &dest, &gw, &flags, &refcnt, &use, &metric, + &mask, &mtu, &window, &irtt); + dprintf(fd, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n", + iface, tswap32(dest), tswap32(gw), flags, refcnt, use, + metric, tswap32(mask), mtu, window, irtt); + } + + free(line); + fclose(fp); + + return 0; +} +#endif + static int do_open(void *cpu_env, const char *pathname, int flags, mode_t mode) { struct fake_open { const char *filename; int (*fill)(void *cpu_env, int fd); + int (*cmp)(const char *s1, const char *s2); }; const struct fake_open *fake_open; static const struct fake_open fakes[] = { - { "maps", open_self_maps }, - { "stat", open_self_stat }, - { "auxv", open_self_auxv }, - { NULL, NULL } + { "maps", open_self_maps, is_proc_myself }, + { "stat", open_self_stat, is_proc_myself }, + { "auxv", open_self_auxv, is_proc_myself }, +#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) + { "/proc/net/route", open_net_route, is_proc }, +#endif + { NULL, NULL, NULL } }; for (fake_open = fakes; fake_open->filename; fake_open++) { - if (is_proc_myself(pathname, fake_open->filename)) { + if (fake_open->cmp(pathname, fake_open->filename)) { break; } } @@ -5697,7 +5851,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; -#ifdef TARGET_NR_umount2 /* not on alpha */ +#ifdef TARGET_NR_umount2 case TARGET_NR_umount2: if (!(p = lock_user_string(arg1))) goto efault; @@ -9013,6 +9167,34 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } #endif +#ifdef TARGET_NR_atomic_cmpxchg_32 + case TARGET_NR_atomic_cmpxchg_32: + { + /* should use start_exclusive from main.c */ + abi_ulong mem_value; + if (get_user_u32(mem_value, arg6)) { + target_siginfo_t info; + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = arg6; + queue_signal((CPUArchState *)cpu_env, info.si_signo, &info); + ret = 0xdeadbeef; + + } + if (mem_value == arg2) + put_user_u32(arg1, arg6); + ret = mem_value; + break; + } +#endif +#ifdef TARGET_NR_atomic_barrier + case TARGET_NR_atomic_barrier: + { + /* Like the kernel implementation and the qemu arm barrier, no-op this? */ + break; + } +#endif default: unimplemented: gemu_log("qemu: Unsupported syscall: %d\n", num); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 2ebe3560d7..5f53a28d1b 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -119,6 +119,18 @@ struct target_sockaddr { uint8_t sa_data[14]; }; +struct target_sock_filter { + abi_ushort code; + uint8_t jt; + uint8_t jf; + abi_uint k; +}; + +struct target_sock_fprog { + abi_ushort len; + abi_ulong filter; +}; + struct target_in_addr { uint32_t s_addr; /* big endian */ }; diff --git a/main-loop.c b/main-loop.c index 1c38ea2b93..c3c9c28fba 100644 --- a/main-loop.c +++ b/main-loop.c @@ -466,8 +466,7 @@ int main_loop_wait(int nonblocking) g_array_set_size(gpollfds, 0); /* reset for new iteration */ /* XXX: separate device handlers from system ones */ #ifdef CONFIG_SLIRP - slirp_update_timeout(&timeout); - slirp_pollfds_fill(gpollfds); + slirp_pollfds_fill(gpollfds, &timeout); #endif qemu_iohandler_fill(gpollfds); diff --git a/migration-rdma.c b/migration-rdma.c index 05a155b93d..f94f3b4e3a 100644 --- a/migration-rdma.c +++ b/migration-rdma.c @@ -356,6 +356,7 @@ typedef struct RDMAContext { */ struct rdma_cm_id *cm_id; /* connection manager ID */ struct rdma_cm_id *listen_id; + bool connected; struct ibv_context *verbs; struct rdma_event_channel *channel; @@ -510,19 +511,21 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, int *resp_idx, int (*callback)(RDMAContext *rdma)); -static inline uint64_t ram_chunk_index(uint8_t *start, uint8_t *host) +static inline uint64_t ram_chunk_index(const uint8_t *start, + const uint8_t *host) { return ((uintptr_t) host - (uintptr_t) start) >> RDMA_REG_CHUNK_SHIFT; } -static inline uint8_t *ram_chunk_start(RDMALocalBlock *rdma_ram_block, +static inline uint8_t *ram_chunk_start(const RDMALocalBlock *rdma_ram_block, uint64_t i) { return (uint8_t *) (((uintptr_t) rdma_ram_block->local_host_addr) + (i << RDMA_REG_CHUNK_SHIFT)); } -static inline uint8_t *ram_chunk_end(RDMALocalBlock *rdma_ram_block, uint64_t i) +static inline uint8_t *ram_chunk_end(const RDMALocalBlock *rdma_ram_block, + uint64_t i) { uint8_t *result = ram_chunk_start(rdma_ram_block, i) + (1UL << RDMA_REG_CHUNK_SHIFT); @@ -2194,7 +2197,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) struct rdma_cm_event *cm_event; int ret, idx; - if (rdma->cm_id) { + if (rdma->cm_id && rdma->connected) { if (rdma->error_state) { RDMAControlHeader head = { .len = 0, .type = RDMA_CONTROL_ERROR, @@ -2213,7 +2216,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) } } DDPRINTF("Disconnected.\n"); - rdma->cm_id = NULL; + rdma->connected = false; } g_free(rdma->block); @@ -2235,7 +2238,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) } if (rdma->qp) { - ibv_destroy_qp(rdma->qp); + rdma_destroy_qp(rdma->cm_id); rdma->qp = NULL; } if (rdma->cq) { @@ -2372,6 +2375,7 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp) rdma->cm_id = NULL; goto err_rdma_source_connect; } + rdma->connected = true; memcpy(&cap, cm_event->param.conn.private_data, sizeof(cap)); network_to_caps(&cap); @@ -2906,6 +2910,7 @@ static int qemu_rdma_accept(RDMAContext *rdma) } rdma_ack_cm_event(cm_event); + rdma->connected = true; ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); if (ret) { diff --git a/migration.c b/migration.c index 200d404547..b4f8462ae4 100644 --- a/migration.c +++ b/migration.c @@ -567,7 +567,8 @@ static void *migration_thread(void *opaque) if (!qemu_file_rate_limit(s->file)) { DPRINTF("iterate\n"); pending_size = qemu_savevm_state_pending(s->file, max_size); - DPRINTF("pending size %lu max %lu\n", pending_size, max_size); + DPRINTF("pending size %" PRIu64 " max %" PRIu64 "\n", + pending_size, max_size); if (pending_size && pending_size >= max_size) { qemu_savevm_state_iterate(s->file); } else { diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex 05fc7c2fae..6727f0ca39 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 49f2d291fc..4d6e48fcbe 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -123,6 +123,7 @@ static void vring_init(struct vring *vr, unsigned int num, void *p, /* We're running with interrupts off anyways, so don't bother */ vr->used->flags = VRING_USED_F_NO_NOTIFY; vr->used->idx = 0; + vr->used_idx = 0; debug_print_addr("init vr", vr); } @@ -150,8 +151,6 @@ static void vring_send_buf(struct vring *vr, void *p, int len, int flags) if (!(flags & VRING_DESC_F_NEXT)) { vr->avail->idx++; } - - vr->used->idx = vr->next_idx; } static u64 get_clock(void) @@ -180,7 +179,8 @@ static int vring_wait_reply(struct vring *vr, int timeout) struct subchannel_id schid = vr->schid; int r = 0; - while (vr->used->idx == vr->next_idx) { + /* Wait until the used index has moved. */ + while (vr->used->idx == vr->used_idx) { vring_notify(schid); if (timeout && (get_second() >= target_second)) { r = 1; @@ -189,6 +189,7 @@ static int vring_wait_reply(struct vring *vr, int timeout) yield(); } + vr->used_idx = vr->used->idx; vr->next_idx = 0; vr->desc[0].len = 0; vr->desc[0].flags = 0; diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 86fdd579b4..772a63f152 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -115,6 +115,7 @@ struct vring_used { struct vring { unsigned int num; int next_idx; + int used_idx; struct vring_desc *desc; struct vring_avail *avail; struct vring_used *used; diff --git a/qapi-schema.json b/qapi-schema.json index 2b2c8bce07..145eca8855 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1686,6 +1686,22 @@ '*mode': 'NewImageMode' } } ## +# @BlockdevSnapshotInternal +# +# @device: the name of the device to generate the snapshot from +# +# @name: the name of the internal snapshot to be created +# +# Notes: In transaction, if @name is empty, or any snapshot matching @name +# exists, the operation will fail. Only some image formats support it, +# for example, qcow2, rbd, and sheepdog. +# +# Since: 1.7 +## +{ 'type': 'BlockdevSnapshotInternal', + 'data': { 'device': 'str', 'name': 'str' } } + +## # @DriveBackup # # @device: the name of the device which should be copied. @@ -1747,7 +1763,8 @@ 'data': { 'blockdev-snapshot-sync': 'BlockdevSnapshot', 'drive-backup': 'DriveBackup', - 'abort': 'Abort' + 'abort': 'Abort', + 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal' } } ## @@ -1788,6 +1805,53 @@ 'data': 'BlockdevSnapshot' } ## +# @blockdev-snapshot-internal-sync +# +# Synchronously take an internal snapshot of a block device, when the format +# of the image used supports it. +# +# For the arguments, see the documentation of BlockdevSnapshotInternal. +# +# Returns: nothing on success +# If @device is not a valid block device, DeviceNotFound +# If any snapshot matching @name exists, or @name is empty, +# GenericError +# If the format of the image used does not support it, +# BlockFormatFeatureNotSupported +# +# Since 1.7 +## +{ 'command': 'blockdev-snapshot-internal-sync', + 'data': 'BlockdevSnapshotInternal' } + +## +# @blockdev-snapshot-delete-internal-sync +# +# Synchronously delete an internal snapshot of a block device, when the format +# of the image used support it. The snapshot is identified by name or id or +# both. One of the name or id is required. Return SnapshotInfo for the +# successfully deleted snapshot. +# +# @device: the name of the device to delete the snapshot from +# +# @id: optional the snapshot's ID to be deleted +# +# @name: optional the snapshot's name to be deleted +# +# Returns: SnapshotInfo on success +# If @device is not a valid block device, DeviceNotFound +# If snapshot not found, GenericError +# If the format of the image used does not support it, +# BlockFormatFeatureNotSupported +# If @id and @name are both not specified, GenericError +# +# Since 1.7 +## +{ 'command': 'blockdev-snapshot-delete-internal-sync', + 'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, + 'returns': 'SnapshotInfo' } + +## # @human-monitor-command: # # Execute a command on the human monitor and return the output. diff --git a/qemu-char.c b/qemu-char.c index 62594965bd..f7f5464b67 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1026,15 +1026,11 @@ static gboolean pty_chr_timer(gpointer opaque) struct CharDriverState *chr = opaque; PtyCharDriver *s = chr->opaque; - if (s->connected) { - goto out; - } - - /* Next poll ... */ - pty_chr_update_read_handler(chr); - -out: s->timer_tag = 0; + if (!s->connected) { + /* Next poll ... */ + pty_chr_update_read_handler(chr); + } return FALSE; } diff --git a/qemu-coroutine.c b/qemu-coroutine.c index 423430d3a0..470852100a 100644 --- a/qemu-coroutine.c +++ b/qemu-coroutine.c @@ -30,15 +30,17 @@ static unsigned int pool_size; Coroutine *qemu_coroutine_create(CoroutineEntry *entry) { - Coroutine *co; - - qemu_mutex_lock(&pool_lock); - co = QSLIST_FIRST(&pool); - if (co) { - QSLIST_REMOVE_HEAD(&pool, pool_next); - pool_size--; + Coroutine *co = NULL; + + if (CONFIG_COROUTINE_POOL) { + qemu_mutex_lock(&pool_lock); + co = QSLIST_FIRST(&pool); + if (co) { + QSLIST_REMOVE_HEAD(&pool, pool_next); + pool_size--; + } + qemu_mutex_unlock(&pool_lock); } - qemu_mutex_unlock(&pool_lock); if (!co) { co = qemu_coroutine_new(); @@ -51,15 +53,17 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry) static void coroutine_delete(Coroutine *co) { - qemu_mutex_lock(&pool_lock); - if (pool_size < POOL_MAX_SIZE) { - QSLIST_INSERT_HEAD(&pool, co, pool_next); - co->caller = NULL; - pool_size++; + if (CONFIG_COROUTINE_POOL) { + qemu_mutex_lock(&pool_lock); + if (pool_size < POOL_MAX_SIZE) { + QSLIST_INSERT_HEAD(&pool, co, pool_next); + co->caller = NULL; + pool_size++; + qemu_mutex_unlock(&pool_lock); + return; + } qemu_mutex_unlock(&pool_lock); - return; } - qemu_mutex_unlock(&pool_lock); qemu_coroutine_delete(co); } diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 0c36e5968f..da1d965f3e 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -67,5 +67,11 @@ DEF("resize", img_resize, "resize [-q] filename [+ | -]size") STEXI @item resize [-q] @var{filename} [+ | -]@var{size} +ETEXI + +DEF("amend", img_amend, + "amend [-q] [-f fmt] -o options filename") +STEXI +@item amend [-q] [-f @var{fmt}] -o @var{options} @var{filename} @end table ETEXI diff --git a/qemu-img.c b/qemu-img.c index 3e5e388d1c..926f0a0feb 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -266,6 +266,7 @@ static BlockDriverState *bdrv_new_open(const char *filename, BlockDriverState *bs; BlockDriver *drv; char password[256]; + Error *local_err = NULL; int ret; bs = bdrv_new("image"); @@ -280,9 +281,11 @@ static BlockDriverState *bdrv_new_open(const char *filename, drv = NULL; } - ret = bdrv_open(bs, filename, NULL, flags, drv); + ret = bdrv_open(bs, filename, NULL, flags, drv, &local_err); if (ret < 0) { - error_report("Could not open '%s': %s", filename, strerror(-ret)); + error_report("Could not open '%s': %s", filename, + error_get_pretty(local_err)); + error_free(local_err); goto fail; } @@ -409,7 +412,7 @@ static int img_create(int argc, char **argv) bdrv_img_create(filename, fmt, base_filename, base_fmt, options, img_size, BDRV_O_FLAGS, &local_err, quiet); if (error_is_set(&local_err)) { - error_report("%s", error_get_pretty(local_err)); + error_report("%s: %s", filename, error_get_pretty(local_err)); error_free(local_err); return 1; } @@ -1136,6 +1139,7 @@ static int img_convert(int argc, char **argv) float local_progress = 0; int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */ bool quiet = false; + Error *local_err = NULL; fmt = NULL; out_fmt = "raw"; @@ -1338,18 +1342,11 @@ static int img_convert(int argc, char **argv) if (!skip_create) { /* Create the new image */ - ret = bdrv_create(drv, out_filename, param); + ret = bdrv_create(drv, out_filename, param, &local_err); if (ret < 0) { - if (ret == -ENOTSUP) { - error_report("Formatting not supported for file format '%s'", - out_fmt); - } else if (ret == -EFBIG) { - error_report("The image size is too large for file format '%s'", - out_fmt); - } else { - error_report("%s: error while converting %s: %s", - out_filename, out_fmt, strerror(-ret)); - } + error_report("%s: error while converting %s: %s", + out_filename, out_fmt, error_get_pretty(local_err)); + error_free(local_err); goto out; } } @@ -1842,7 +1839,7 @@ static void dump_map_entry(OutputFormat output_format, MapEntry *e, (e->flags & BDRV_BLOCK_ZERO) ? "true" : "false", (e->flags & BDRV_BLOCK_DATA) ? "true" : "false"); if (e->flags & BDRV_BLOCK_OFFSET_VALID) { - printf(", 'offset': %"PRId64"", e->offset); + printf(", \"offset\": %"PRId64"", e->offset); } putchar('}'); @@ -2006,6 +2003,7 @@ static int img_snapshot(int argc, char **argv) int action = 0; qemu_timeval tv; bool quiet = false; + Error *err = NULL; bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; /* Parse commandline parameters */ @@ -2098,10 +2096,12 @@ static int img_snapshot(int argc, char **argv) break; case SNAPSHOT_DELETE: - ret = bdrv_snapshot_delete(bs, snapshot_name); - if (ret) { - error_report("Could not delete snapshot '%s': %d (%s)", - snapshot_name, ret, strerror(-ret)); + bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err); + if (error_is_set(&err)) { + error_report("Could not delete snapshot '%s': (%s)", + snapshot_name, error_get_pretty(err)); + error_free(err); + ret = 1; } break; } @@ -2124,6 +2124,7 @@ static int img_rebase(int argc, char **argv) int unsafe = 0; int progress = 0; bool quiet = false; + Error *local_err = NULL; /* Parse commandline parameters */ fmt = NULL; @@ -2227,18 +2228,21 @@ static int img_rebase(int argc, char **argv) bs_old_backing = bdrv_new("old_backing"); bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); ret = bdrv_open(bs_old_backing, backing_name, NULL, BDRV_O_FLAGS, - old_backing_drv); + old_backing_drv, &local_err); if (ret) { - error_report("Could not open old backing file '%s'", backing_name); + error_report("Could not open old backing file '%s': %s", + backing_name, error_get_pretty(local_err)); + error_free(local_err); goto out; } if (out_baseimg[0]) { bs_new_backing = bdrv_new("new_backing"); ret = bdrv_open(bs_new_backing, out_baseimg, NULL, BDRV_O_FLAGS, - new_backing_drv); + new_backing_drv, &local_err); if (ret) { - error_report("Could not open new backing file '%s'", - out_baseimg); + error_report("Could not open new backing file '%s': %s", + out_baseimg, error_get_pretty(local_err)); + error_free(local_err); goto out; } } @@ -2525,6 +2529,90 @@ out: return 0; } +static int img_amend(int argc, char **argv) +{ + int c, ret = 0; + char *options = NULL; + QEMUOptionParameter *create_options = NULL, *options_param = NULL; + const char *fmt = NULL, *filename; + bool quiet = false; + BlockDriverState *bs = NULL; + + for (;;) { + c = getopt(argc, argv, "hqf:o:"); + if (c == -1) { + break; + } + + switch (c) { + case 'h': + case '?': + help(); + break; + case 'o': + options = optarg; + break; + case 'f': + fmt = optarg; + break; + case 'q': + quiet = true; + break; + } + } + + if (optind != argc - 1) { + help(); + } + + if (!options) { + help(); + } + + filename = argv[argc - 1]; + + bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet); + if (!bs) { + error_report("Could not open image '%s'", filename); + ret = -1; + goto out; + } + + fmt = bs->drv->format_name; + + if (is_help_option(options)) { + ret = print_block_option_help(filename, fmt); + goto out; + } + + create_options = append_option_parameters(create_options, + bs->drv->create_options); + options_param = parse_option_parameters(options, create_options, + options_param); + if (options_param == NULL) { + error_report("Invalid options for file format '%s'", fmt); + ret = -1; + goto out; + } + + ret = bdrv_amend_options(bs, options_param); + if (ret < 0) { + error_report("Error while amending options: %s", strerror(-ret)); + goto out; + } + +out: + if (bs) { + bdrv_unref(bs); + } + free_option_parameters(create_options); + free_option_parameters(options_param); + if (ret) { + return 1; + } + return 0; +} + static const img_cmd_t img_cmds[] = { #define DEF(option, callback, arg_string) \ { option, callback }, diff --git a/qemu-img.texi b/qemu-img.texi index 43ee4eb5c4..768054e900 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -350,6 +350,11 @@ sizes accordingly. Failure to do so will result in data loss! After using this command to grow a disk image, you must use file system and partitioning tools inside the VM to actually begin using the new space on the device. + +@item amend [-f @var{fmt}] -o @var{options} @var{filename} + +Amends the image format specific @var{options} for the image file +@var{filename}. Not all file formats support this operation. @end table @c man end @@ -46,21 +46,27 @@ static const cmdinfo_t close_cmd = { static int openfile(char *name, int flags, int growable) { + Error *local_err = NULL; + if (qemuio_bs) { fprintf(stderr, "file open already, try 'help close'\n"); return 1; } if (growable) { - if (bdrv_file_open(&qemuio_bs, name, NULL, flags)) { - fprintf(stderr, "%s: can't open device %s\n", progname, name); + if (bdrv_file_open(&qemuio_bs, name, NULL, flags, &local_err)) { + fprintf(stderr, "%s: can't open device %s: %s\n", progname, name, + error_get_pretty(local_err)); + error_free(local_err); return 1; } } else { qemuio_bs = bdrv_new("hda"); - if (bdrv_open(qemuio_bs, name, NULL, flags, NULL) < 0) { - fprintf(stderr, "%s: can't open device %s\n", progname, name); + if (bdrv_open(qemuio_bs, name, NULL, flags, NULL, &local_err) < 0) { + fprintf(stderr, "%s: can't open device %s: %s\n", progname, name, + error_get_pretty(local_err)); + error_free(local_err); bdrv_unref(qemuio_bs); qemuio_bs = NULL; return 1; diff --git a/qemu-nbd.c b/qemu-nbd.c index f044546c28..c26c98ef1d 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -355,6 +355,7 @@ int main(int argc, char **argv) #endif pthread_t client_thread; const char *fmt = NULL; + Error *local_err = NULL; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -573,10 +574,11 @@ int main(int argc, char **argv) bs = bdrv_new("hda"); srcpath = argv[optind]; - ret = bdrv_open(bs, srcpath, NULL, flags, drv); + ret = bdrv_open(bs, srcpath, NULL, flags, drv, &local_err); if (ret < 0) { errno = -ret; - err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]); + err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind], + error_get_pretty(local_err)); } fd_size = bdrv_getlength(bs); diff --git a/qemu-timer.c b/qemu-timer.c index 95ff47fef3..6b62e88669 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -66,6 +66,7 @@ QEMUClock qemu_clocks[QEMU_CLOCK_MAX]; struct QEMUTimerList { QEMUClock *clock; + QemuMutex active_timers_lock; QEMUTimer *active_timers; QLIST_ENTRY(QEMUTimerList) list; QEMUTimerListNotifyCB *notify_cb; @@ -101,6 +102,7 @@ QEMUTimerList *timerlist_new(QEMUClockType type, timer_list->clock = clock; timer_list->notify_cb = cb; timer_list->notify_opaque = opaque; + qemu_mutex_init(&timer_list->active_timers_lock); QLIST_INSERT_HEAD(&clock->timerlists, timer_list, list); return timer_list; } @@ -111,6 +113,7 @@ void timerlist_free(QEMUTimerList *timer_list) if (timer_list->clock) { QLIST_REMOVE(timer_list, list); } + qemu_mutex_destroy(&timer_list->active_timers_lock); g_free(timer_list); } @@ -163,9 +166,17 @@ bool qemu_clock_has_timers(QEMUClockType type) bool timerlist_expired(QEMUTimerList *timer_list) { - return (timer_list->active_timers && - timer_list->active_timers->expire_time < - qemu_clock_get_ns(timer_list->clock->type)); + int64_t expire_time; + + qemu_mutex_lock(&timer_list->active_timers_lock); + if (!timer_list->active_timers) { + qemu_mutex_unlock(&timer_list->active_timers_lock); + return false; + } + expire_time = timer_list->active_timers->expire_time; + qemu_mutex_unlock(&timer_list->active_timers_lock); + + return expire_time < qemu_clock_get_ns(timer_list->clock->type); } bool qemu_clock_expired(QEMUClockType type) @@ -182,13 +193,25 @@ bool qemu_clock_expired(QEMUClockType type) int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) { int64_t delta; + int64_t expire_time; - if (!timer_list->clock->enabled || !timer_list->active_timers) { + if (!timer_list->clock->enabled) { return -1; } - delta = timer_list->active_timers->expire_time - - qemu_clock_get_ns(timer_list->clock->type); + /* The active timers list may be modified before the caller uses our return + * value but ->notify_cb() is called when the deadline changes. Therefore + * the caller should notice the change and there is no race condition. + */ + qemu_mutex_lock(&timer_list->active_timers_lock); + if (!timer_list->active_timers) { + qemu_mutex_unlock(&timer_list->active_timers_lock); + return -1; + } + expire_time = timer_list->active_timers->expire_time; + qemu_mutex_unlock(&timer_list->active_timers_lock); + + delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); if (delta <= 0) { return 0; @@ -289,6 +312,7 @@ void timer_init(QEMUTimer *ts, ts->cb = cb; ts->opaque = opaque; ts->scale = scale; + ts->expire_time = -1; } void timer_free(QEMUTimer *ts) @@ -296,14 +320,12 @@ void timer_free(QEMUTimer *ts) g_free(ts); } -/* stop a timer, but do not dealloc it */ -void timer_del(QEMUTimer *ts) +static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts) { QEMUTimer **pt, *t; - /* NOTE: this code must be signal safe because - timer_expired() can be called from a signal. */ - pt = &ts->timer_list->active_timers; + ts->expire_time = -1; + pt = &timer_list->active_timers; for(;;) { t = *pt; if (!t) @@ -316,18 +338,28 @@ void timer_del(QEMUTimer *ts) } } +/* stop a timer, but do not dealloc it */ +void timer_del(QEMUTimer *ts) +{ + QEMUTimerList *timer_list = ts->timer_list; + + qemu_mutex_lock(&timer_list->active_timers_lock); + timer_del_locked(timer_list, ts); + qemu_mutex_unlock(&timer_list->active_timers_lock); +} + /* modify the current timer so that it will be fired when current_time >= expire_time. The corresponding callback will be called. */ void timer_mod_ns(QEMUTimer *ts, int64_t expire_time) { + QEMUTimerList *timer_list = ts->timer_list; QEMUTimer **pt, *t; - timer_del(ts); + qemu_mutex_lock(&timer_list->active_timers_lock); + timer_del_locked(timer_list, ts); /* add the timer in the sorted list */ - /* NOTE: this code must be signal safe because - timer_expired() can be called from a signal. */ - pt = &ts->timer_list->active_timers; + pt = &timer_list->active_timers; for(;;) { t = *pt; if (!timer_expired_ns(t, expire_time)) { @@ -335,15 +367,16 @@ void timer_mod_ns(QEMUTimer *ts, int64_t expire_time) } pt = &t->next; } - ts->expire_time = expire_time; + ts->expire_time = MAX(expire_time, 0); ts->next = *pt; *pt = ts; + qemu_mutex_unlock(&timer_list->active_timers_lock); /* Rearm if necessary */ - if (pt == &ts->timer_list->active_timers) { + if (pt == &timer_list->active_timers) { /* Interrupt execution to force deadline recalculation. */ - qemu_clock_warp(ts->timer_list->clock->type); - timerlist_notify(ts->timer_list); + qemu_clock_warp(timer_list->clock->type); + timerlist_notify(timer_list); } } @@ -354,13 +387,7 @@ void timer_mod(QEMUTimer *ts, int64_t expire_time) bool timer_pending(QEMUTimer *ts) { - QEMUTimer *t; - for (t = ts->timer_list->active_timers; t != NULL; t = t->next) { - if (t == ts) { - return true; - } - } - return false; + return ts->expire_time >= 0; } bool timer_expired(QEMUTimer *timer_head, int64_t current_time) @@ -373,23 +400,32 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) QEMUTimer *ts; int64_t current_time; bool progress = false; - + QEMUTimerCB *cb; + void *opaque; + if (!timer_list->clock->enabled) { return progress; } current_time = qemu_clock_get_ns(timer_list->clock->type); for(;;) { + qemu_mutex_lock(&timer_list->active_timers_lock); ts = timer_list->active_timers; if (!timer_expired_ns(ts, current_time)) { + qemu_mutex_unlock(&timer_list->active_timers_lock); break; } + /* remove timer from the list before calling the callback */ timer_list->active_timers = ts->next; ts->next = NULL; + ts->expire_time = -1; + cb = ts->cb; + opaque = ts->opaque; + qemu_mutex_unlock(&timer_list->active_timers_lock); /* run the callback (the timer list can be modified) */ - ts->cb(ts->opaque); + cb(opaque); progress = true; } return progress; diff --git a/qmp-commands.hx b/qmp-commands.hx index 008cad95a2..b17c46e0b1 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1001,14 +1001,15 @@ SQMP transaction ----------- -Atomically operate on one or more block devices. The only supported -operation for now is snapshotting. If there is any failure performing -any of the operations, all snapshots for the group are abandoned, and -the original disks pre-snapshot attempt are used. +Atomically operate on one or more block devices. The only supported operations +for now are drive-backup, internal and external snapshotting. A list of +dictionaries is accepted, that contains the actions to be performed. +If there is any failure performing any of the operations, all operations +for the group are abandoned. -A list of dictionaries is accepted, that contains the actions to be performed. -For snapshots this is the device, the file to use for the new snapshot, -and the format. The default format, if not specified, is qcow2. +For external snapshots, the dictionary contains the device, the file to use for +the new snapshot, and the format. The default format, if not specified, is +qcow2. Each new snapshot defaults to being created by QEMU (wiping any contents if the file already exists), but it is also possible to reuse @@ -1017,6 +1018,17 @@ the new image file has the same contents as the current one; QEMU cannot perform any meaningful check. Typically this is achieved by using the current image file as the backing file for the new image. +On failure, the original disks pre-snapshot attempt will be used. + +For internal snapshots, the dictionary contains the device and the snapshot's +name. If an internal snapshot matching name already exists, the request will +be rejected. Only some image formats support it, for example, qcow2, rbd, +and sheepdog. + +On failure, qemu will try delete the newly created internal snapshot in the +transaction. When an I/O error occurs during deletion, the user needs to fix +it later with qemu-img or other command. + Arguments: actions array: @@ -1029,6 +1041,9 @@ actions array: - "format": format of new image (json-string, optional) - "mode": whether and how QEMU should create the snapshot file (NewImageMode, optional, default "absolute-paths") + When "type" is "blockdev-snapshot-internal-sync": + - "device": device name to snapshot (json-string) + - "name": name of the new snapshot (json-string) Example: @@ -1040,7 +1055,10 @@ Example: { 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1", "snapshot-file": "/some/place/my-image2", "mode": "existing", - "format": "qcow2" } } ] } } + "format": "qcow2" } }, + { 'type': 'blockdev-snapshot-internal-sync', 'data' : { + "device": "ide-hd2", + "name": "snapshot0" } } ] } } <- { "return": {} } EQMP @@ -1080,6 +1098,76 @@ Example: EQMP { + .name = "blockdev-snapshot-internal-sync", + .args_type = "device:B,name:s", + .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_internal_sync, + }, + +SQMP +blockdev-snapshot-internal-sync +------------------------------- + +Synchronously take an internal snapshot of a block device when the format of +image used supports it. If the name is an empty string, or a snapshot with +name already exists, the operation will fail. + +Arguments: + +- "device": device name to snapshot (json-string) +- "name": name of the new snapshot (json-string) + +Example: + +-> { "execute": "blockdev-snapshot-internal-sync", + "arguments": { "device": "ide-hd0", + "name": "snapshot0" } + } +<- { "return": {} } + +EQMP + + { + .name = "blockdev-snapshot-delete-internal-sync", + .args_type = "device:B,id:s?,name:s?", + .mhandler.cmd_new = + qmp_marshal_input_blockdev_snapshot_delete_internal_sync, + }, + +SQMP +blockdev-snapshot-delete-internal-sync +-------------------------------------- + +Synchronously delete an internal snapshot of a block device when the format of +image used supports it. The snapshot is identified by name or id or both. One +of name or id is required. If the snapshot is not found, the operation will +fail. + +Arguments: + +- "device": device name (json-string) +- "id": ID of the snapshot (json-string, optional) +- "name": name of the snapshot (json-string, optional) + +Example: + +-> { "execute": "blockdev-snapshot-delete-internal-sync", + "arguments": { "device": "ide-hd0", + "name": "snapshot0" } + } +<- { "return": { + "id": "1", + "name": "snapshot0", + "vm-state-size": 0, + "date-sec": 1000012, + "date-nsec": 10, + "vm-clock-sec": 100, + "vm-clock-nsec": 20 + } + } + +EQMP + + { .name = "drive-mirror", .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," "on-source-error:s?,on-target-error:s?," diff --git a/qobject/qdict.c b/qobject/qdict.c index 472f106e27..0f3e0a6c81 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -527,3 +527,24 @@ void qdict_flatten(QDict *qdict) { qdict_do_flatten(qdict, qdict, NULL); } + +/* extract all the src QDict entries starting by start into dst */ +void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) + +{ + const QDictEntry *entry, *next; + const char *p; + + *dst = qdict_new(); + entry = qdict_first(src); + + while (entry != NULL) { + next = qdict_next(src, entry); + if (strstart(entry->key, start, &p)) { + qobject_incref(entry->value); + qdict_put_obj(*dst, p, entry->value); + qdict_del(src, entry->key); + } + entry = next; + } +} @@ -162,6 +162,7 @@ void cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, CPUClass *cc = CPU_GET_CLASS(cpu); if (cc->dump_state) { + cpu_synchronize_state(cpu); cc->dump_state(cpu, f, cpu_fprintf, flags); } } @@ -566,6 +566,13 @@ QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) return f; } +/* + * Get last error for stream f + * + * Return negative error value if there has been an error on previous + * operations, return 0 if no error happened. + * + */ int qemu_file_get_error(QEMUFile *f) { return f->last_error; @@ -642,7 +649,7 @@ void ram_control_after_iterate(QEMUFile *f, uint64_t flags) void ram_control_load_hook(QEMUFile *f, uint64_t flags) { - int ret = 0; + int ret = -EINVAL; if (f->ops->hook_ram_load) { ret = f->ops->hook_ram_load(f, f->opaque, flags); @@ -2325,18 +2332,21 @@ static int del_existing_snapshots(Monitor *mon, const char *name) { BlockDriverState *bs; QEMUSnapshotInfo sn1, *snapshot = &sn1; - int ret; + Error *err = NULL; bs = NULL; while ((bs = bdrv_next(bs))) { if (bdrv_can_snapshot(bs) && bdrv_snapshot_find(bs, snapshot, name) >= 0) { - ret = bdrv_snapshot_delete(bs, name); - if (ret < 0) { + bdrv_snapshot_delete_by_id_or_name(bs, name, &err); + if (error_is_set(&err)) { monitor_printf(mon, - "Error while deleting snapshot on '%s'\n", - bdrv_get_device_name(bs)); + "Error while deleting snapshot on device '%s':" + " %s\n", + bdrv_get_device_name(bs), + error_get_pretty(err)); + error_free(err); return -1; } } @@ -2550,7 +2560,7 @@ int load_vmstate(const char *name) void do_delvm(Monitor *mon, const QDict *qdict) { BlockDriverState *bs, *bs1; - int ret; + Error *err = NULL; const char *name = qdict_get_str(qdict, "name"); bs = find_vmstate_bs(); @@ -2562,15 +2572,14 @@ void do_delvm(Monitor *mon, const QDict *qdict) bs1 = NULL; while ((bs1 = bdrv_next(bs1))) { if (bdrv_can_snapshot(bs1)) { - ret = bdrv_snapshot_delete(bs1, name); - if (ret < 0) { - if (ret == -ENOTSUP) - monitor_printf(mon, - "Snapshots not supported on device '%s'\n", - bdrv_get_device_name(bs1)); - else - monitor_printf(mon, "Error %d while deleting snapshot on " - "'%s'\n", ret, bdrv_get_device_name(bs1)); + bdrv_snapshot_delete_by_id_or_name(bs, name, &err); + if (error_is_set(&err)) { + monitor_printf(mon, + "Error while deleting snapshot on device '%s':" + " %s\n", + bdrv_get_device_name(bs), + error_get_pretty(err)); + error_free(err); } } } diff --git a/QMP/qemu-ga-client b/scripts/qmp/qemu-ga-client index b5f7e7c5ff..b5f7e7c5ff 100755 --- a/QMP/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client diff --git a/QMP/qmp b/scripts/qmp/qmp index 1db3c7ffeb..1db3c7ffeb 100755 --- a/QMP/qmp +++ b/scripts/qmp/qmp diff --git a/QMP/qmp-shell b/scripts/qmp/qmp-shell index 73cb3b6cef..d6b420f18a 100755 --- a/QMP/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -91,7 +91,7 @@ class QMPShell(qmp.QEMUMonitorProtocol): """ Build a QMP input object from a user provided command-line in the following format: - + < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] """ cmdargs = cmdline.split() diff --git a/QMP/qmp.py b/scripts/qmp/qmp.py index c551df1ed7..5c9717594f 100644 --- a/QMP/qmp.py +++ b/scripts/qmp/qmp.py @@ -1,5 +1,5 @@ # QEMU Monitor Protocol Python class -# +# # Copyright (C) 2009, 2010 Red Hat Inc. # # Authors: @@ -188,3 +188,9 @@ class QEMUMonitorProtocol: def settimeout(self, timeout): self.__sock.settimeout(timeout) + + def get_sock_fd(self): + return self.__sock.fileno() + + def is_scm_available(self): + return self.__sock.family == socket.AF_UNIX diff --git a/QMP/qom-fuse b/scripts/qmp/qom-fuse index 5c6754aa63..5c6754aa63 100755 --- a/QMP/qom-fuse +++ b/scripts/qmp/qom-fuse diff --git a/QMP/qom-get b/scripts/qmp/qom-get index 0172c69441..0172c69441 100755 --- a/QMP/qom-get +++ b/scripts/qmp/qom-get diff --git a/QMP/qom-list b/scripts/qmp/qom-list index 1e7cc6cb2d..1e7cc6cb2d 100755 --- a/QMP/qom-list +++ b/scripts/qmp/qom-list diff --git a/QMP/qom-set b/scripts/qmp/qom-set index 54ecfecc53..54ecfecc53 100755 --- a/QMP/qom-set +++ b/scripts/qmp/qom-set diff --git a/slirp/libslirp.h b/slirp/libslirp.h index ceabff81b2..5bdcbd50f7 100644 --- a/slirp/libslirp.h +++ b/slirp/libslirp.h @@ -16,8 +16,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, void *opaque); void slirp_cleanup(Slirp *slirp); -void slirp_update_timeout(uint32_t *timeout); -void slirp_pollfds_fill(GArray *pollfds); +void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout); void slirp_pollfds_poll(GArray *pollfds, int select_error); diff --git a/slirp/slirp.c b/slirp/slirp.c index 5c3dabba93..bad8dad02e 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -40,8 +40,6 @@ static const uint8_t special_ethaddr[ETH_ALEN] = { static const uint8_t zero_ethaddr[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; u_int curtime; -static u_int time_fasttimo, last_slowtimo; -static int do_slowtimo; static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances = QTAILQ_HEAD_INITIALIZER(slirp_instances); @@ -49,6 +47,11 @@ static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances = static struct in_addr dns_addr; static u_int dns_addr_time; +#define TIMEOUT_FAST 2 /* milliseconds */ +#define TIMEOUT_SLOW 499 /* milliseconds */ +/* for the aging of certain requests like DNS */ +#define TIMEOUT_DEFAULT 1000 /* milliseconds */ + #ifdef _WIN32 int get_dns_addr(struct in_addr *pdns_addr) @@ -59,7 +62,7 @@ int get_dns_addr(struct in_addr *pdns_addr) IP_ADDR_STRING *pIPAddr; struct in_addr tmp_addr; - if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < 1000) { + if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) { *pdns_addr = dns_addr; return 0; } @@ -115,7 +118,7 @@ int get_dns_addr(struct in_addr *pdns_addr) if (dns_addr.s_addr != 0) { struct stat old_stat; - if ((curtime - dns_addr_time) < 1000) { + if ((curtime - dns_addr_time) < TIMEOUT_DEFAULT) { *pdns_addr = dns_addr; return 0; } @@ -259,14 +262,33 @@ void slirp_cleanup(Slirp *slirp) #define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) #define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) -void slirp_update_timeout(uint32_t *timeout) +static void slirp_update_timeout(uint32_t *timeout) { - if (!QTAILQ_EMPTY(&slirp_instances)) { - *timeout = MIN(1000, *timeout); + Slirp *slirp; + uint32_t t; + + if (*timeout <= TIMEOUT_FAST) { + return; + } + + t = MIN(1000, *timeout); + + /* If we have tcp timeout with slirp, then we will fill @timeout with + * more precise value. + */ + QTAILQ_FOREACH(slirp, &slirp_instances, entry) { + if (slirp->time_fasttimo) { + *timeout = TIMEOUT_FAST; + return; + } + if (slirp->do_slowtimo) { + t = MIN(TIMEOUT_SLOW, t); + } } + *timeout = t; } -void slirp_pollfds_fill(GArray *pollfds) +void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout) { Slirp *slirp; struct socket *so, *so_next; @@ -278,14 +300,13 @@ void slirp_pollfds_fill(GArray *pollfds) /* * First, TCP sockets */ - do_slowtimo = 0; QTAILQ_FOREACH(slirp, &slirp_instances, entry) { /* * *_slowtimo needs calling if there are IP fragments * in the fragment queue, or there are TCP connections active */ - do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) || + slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) || (&slirp->ipq.ip_link != slirp->ipq.ip_link.next)); for (so = slirp->tcb.so_next; so != &slirp->tcb; @@ -299,8 +320,9 @@ void slirp_pollfds_fill(GArray *pollfds) /* * See if we need a tcp_fasttimo */ - if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) { - time_fasttimo = curtime; /* Flag when we want a fasttimo */ + if (slirp->time_fasttimo == 0 && + so->so_tcpcb->t_flags & TF_DELACK) { + slirp->time_fasttimo = curtime; /* Flag when want a fasttimo */ } /* @@ -381,7 +403,7 @@ void slirp_pollfds_fill(GArray *pollfds) udp_detach(so); continue; } else { - do_slowtimo = 1; /* Let socket expire */ + slirp->do_slowtimo = true; /* Let socket expire */ } } @@ -422,7 +444,7 @@ void slirp_pollfds_fill(GArray *pollfds) icmp_detach(so); continue; } else { - do_slowtimo = 1; /* Let socket expire */ + slirp->do_slowtimo = true; /* Let socket expire */ } } @@ -436,6 +458,7 @@ void slirp_pollfds_fill(GArray *pollfds) } } } + slirp_update_timeout(timeout); } void slirp_pollfds_poll(GArray *pollfds, int select_error) @@ -454,14 +477,16 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error) /* * See if anything has timed out */ - if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) { + if (slirp->time_fasttimo && + ((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) { tcp_fasttimo(slirp); - time_fasttimo = 0; + slirp->time_fasttimo = 0; } - if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) { + if (slirp->do_slowtimo && + ((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) { ip_slowtimo(slirp); tcp_slowtimo(slirp); - last_slowtimo = curtime; + slirp->last_slowtimo = curtime; } /* diff --git a/slirp/slirp.h b/slirp/slirp.h index fe0e65d0ee..e4a1bd4abb 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -203,6 +203,9 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr, struct Slirp { QTAILQ_ENTRY(Slirp) entry; + u_int time_fasttimo; + u_int last_slowtimo; + bool do_slowtimo; /* virtual network configuration */ struct in_addr vnetwork_addr; diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index f306cbada3..df92fe5b15 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -22,6 +22,7 @@ stub-obj-y += reset.o stub-obj-y += set-fd-handler.o stub-obj-y += slirp.o stub-obj-y += sysbus.o +stub-obj-y += uuid.o stub-obj-y += vm-stop.o stub-obj-y += vmstate.o stub-obj-$(CONFIG_WIN32) += fd-register.o diff --git a/stubs/slirp.c b/stubs/slirp.c index f1fc833f7a..bd0ac7f27d 100644 --- a/stubs/slirp.c +++ b/stubs/slirp.c @@ -1,11 +1,7 @@ #include "qemu-common.h" #include "slirp/slirp.h" -void slirp_update_timeout(uint32_t *timeout) -{ -} - -void slirp_pollfds_fill(GArray *pollfds) +void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout) { } diff --git a/stubs/uuid.c b/stubs/uuid.c new file mode 100644 index 0000000000..ffc0ed40ae --- /dev/null +++ b/stubs/uuid.c @@ -0,0 +1,12 @@ +#include "qemu-common.h" +#include "sysemu/sysemu.h" +#include "qmp-commands.h" + +UuidInfo *qmp_query_uuid(Error **errp) +{ + UuidInfo *info = g_malloc0(sizeof(*info)); + + info->UUID = g_strdup(UUID_NONE); + return info; +} + diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index c4447c2b6e..f4fab155bd 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -70,6 +70,9 @@ typedef struct X86CPU { bool hyperv_relaxed_timing; int hyperv_spinlock_attempts; + /* if true the CPUID code directly forward host cache leaves to the guest */ + bool cache_info_passthrough; + /* Features that were filtered out because of missing host capabilities */ uint32_t filtered_features[FEATURE_WORDS]; diff --git a/target-i386/cpu.c b/target-i386/cpu.c index c36345e426..b6828022bc 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -235,7 +235,7 @@ static const char *ext4_feature_name[] = { static const char *kvm_feature_name[] = { "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", - "kvm_asyncpf", "kvm_steal_time", "kvm_pv_eoi", NULL, + "kvm_asyncpf", "kvm_steal_time", "kvm_pv_eoi", "kvm_pv_unhalt", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -486,6 +486,7 @@ typedef struct x86_def_t { int stepping; FeatureWordArray features; char model_id[48]; + bool cache_info_passthrough; } x86_def_t; #define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE) @@ -1139,6 +1140,7 @@ static void kvm_cpu_fill_host(x86_def_t *x86_cpu_def) assert(kvm_enabled()); x86_cpu_def->name = "host"; + x86_cpu_def->cache_info_passthrough = true; host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx); @@ -1888,6 +1890,7 @@ static void cpu_x86_register(X86CPU *cpu, const char *name, Error **errp) env->features[FEAT_C000_0001_EDX] = def->features[FEAT_C000_0001_EDX]; env->features[FEAT_7_0_EBX] = def->features[FEAT_7_0_EBX]; env->cpuid_xlevel2 = def->xlevel2; + cpu->cache_info_passthrough = def->cache_info_passthrough; object_property_set_str(OBJECT(cpu), def->model_id, "model-id", errp); } @@ -2062,6 +2065,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 2: /* cache info: needed for Pentium Pro compatibility */ + if (cpu->cache_info_passthrough) { + host_cpuid(index, 0, eax, ebx, ecx, edx); + break; + } *eax = 1; /* Number of CPUID[EAX=2] calls required */ *ebx = 0; *ecx = 0; @@ -2071,6 +2078,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 4: /* cache info: needed for Core compatibility */ + if (cpu->cache_info_passthrough) { + host_cpuid(index, count, eax, ebx, ecx, edx); + break; + } if (cs->nr_cores > 1) { *eax = (cs->nr_cores - 1) << 26; } else { @@ -2228,6 +2239,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x80000005: /* cache info (L1 cache) */ + if (cpu->cache_info_passthrough) { + host_cpuid(index, 0, eax, ebx, ecx, edx); + break; + } *eax = (L1_DTLB_2M_ASSOC << 24) | (L1_DTLB_2M_ENTRIES << 16) | \ (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ @@ -2239,6 +2254,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x80000006: /* cache info (L2 cache) */ + if (cpu->cache_info_passthrough) { + host_cpuid(index, 0, eax, ebx, ecx, edx); + break; + } *eax = (AMD_ENC_ASSOC(L2_DTLB_2M_ASSOC) << 28) | \ (L2_DTLB_2M_ENTRIES << 16) | \ (AMD_ENC_ASSOC(L2_ITLB_2M_ASSOC) << 12) | \ diff --git a/target-i386/helper.c b/target-i386/helper.c index 7c58e274d9..7c196ffc42 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -147,7 +147,9 @@ cpu_x86_dump_seg_cache(CPUX86State *env, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, " [%c%c", (sc->flags & DESC_C_MASK) ? 'C' : '-', (sc->flags & DESC_R_MASK) ? 'R' : '-'); } else { - cpu_fprintf(f, (sc->flags & DESC_B_MASK) ? "DS " : "DS16"); + cpu_fprintf(f, + (sc->flags & DESC_B_MASK || env->hflags & HF_LMA_MASK) + ? "DS " : "DS16"); cpu_fprintf(f, " [%c%c", (sc->flags & DESC_E_MASK) ? 'E' : '-', (sc->flags & DESC_W_MASK) ? 'W' : '-'); } @@ -188,8 +190,6 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, char cc_op_name[32]; static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" }; - cpu_synchronize_state(cs); - eflags = cpu_compute_eflags(env); #ifdef TARGET_X86_64 if (env->hflags & HF_CS64_MASK) { @@ -894,7 +894,10 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) uint32_t page_offset; int page_size; - if (env->cr[4] & CR4_PAE_MASK) { + if (!(env->cr[0] & CR0_PG_MASK)) { + pte = addr & env->a20_mask; + page_size = 4096; + } else if (env->cr[4] & CR4_PAE_MASK) { target_ulong pdpe_addr; uint64_t pde, pdpe; @@ -952,26 +955,21 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } else { uint32_t pde; - if (!(env->cr[0] & CR0_PG_MASK)) { - pte = addr; - page_size = 4096; + /* page directory entry */ + pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask; + pde = ldl_phys(pde_addr); + if (!(pde & PG_PRESENT_MASK)) + return -1; + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + pte = pde & ~0x003ff000; /* align to 4MB */ + page_size = 4096 * 1024; } else { /* page directory entry */ - pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask; - pde = ldl_phys(pde_addr); - if (!(pde & PG_PRESENT_MASK)) + pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask; + pte = ldl_phys(pte_addr); + if (!(pte & PG_PRESENT_MASK)) return -1; - if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { - pte = pde & ~0x003ff000; /* align to 4MB */ - page_size = 4096 * 1024; - } else { - /* page directory entry */ - pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask; - pte = ldl_phys(pte_addr); - if (!(pte & PG_PRESENT_MASK)) - return -1; - page_size = 4096; - } + page_size = 4096; } pte = pte & env->a20_mask; } diff --git a/target-i386/machine.c b/target-i386/machine.c index dc81cde535..e568da2ba4 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -330,9 +330,9 @@ static bool pv_eoi_msr_needed(void *opaque) static bool steal_time_msr_needed(void *opaque) { - CPUX86State *cpu = opaque; + X86CPU *cpu = opaque; - return cpu->steal_time_msr != 0; + return cpu->env.steal_time_msr != 0; } static const VMStateDescription vmstate_steal_time_msr = { @@ -341,7 +341,7 @@ static const VMStateDescription vmstate_steal_time_msr = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_UINT64(steal_time_msr, CPUX86State), + VMSTATE_UINT64(env.steal_time_msr, X86CPU), VMSTATE_END_OF_LIST() } }; diff --git a/target-i386/translate.c b/target-i386/translate.c index 6d879003b3..be74ebc278 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -6434,12 +6434,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x1d: /* fucomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } gen_update_cc_op(s); gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); gen_helper_fucomi_ST0_FT0(cpu_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0x1e: /* fcomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } gen_update_cc_op(s); gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); gen_helper_fcomi_ST0_FT0(cpu_env); @@ -6495,6 +6501,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x3d: /* fucomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } gen_update_cc_op(s); gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); gen_helper_fucomi_ST0_FT0(cpu_env); @@ -6502,6 +6511,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, set_cc_op(s, CC_OP_EFLAGS); break; case 0x3e: /* fcomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } gen_update_cc_op(s); gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); gen_helper_fcomi_ST0_FT0(cpu_env); @@ -6518,6 +6530,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, (JCC_BE << 1), (JCC_P << 1), }; + + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); l1 = gen_new_label(); gen_jcc1_noeob(s, op1, l1); @@ -6889,6 +6905,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_ldst_modrm(env, s, modrm, OT_BYTE, OR_TMP0, 1); break; case 0x140 ... 0x14f: /* cmov Gv, Ev */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } ot = dflag + OT_WORD; modrm = cpu_ldub_code(env, s->pc++); reg = ((modrm >> 3) & 7) | rex_r; diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 2da7bc740f..9c59f69ee1 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -9536,8 +9536,6 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env = &cpu->env; int i; - cpu_synchronize_state(cs); - cpu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " TARGET_FMT_lx " XER " TARGET_FMT_lx "\n", env->nip, env->lr, env->ctr, cpu_read_xer(env)); diff --git a/target-s390x/arch_dump.c b/target-s390x/arch_dump.c index 9d36116242..5cbb53ca2e 100644 --- a/target-s390x/arch_dump.c +++ b/target-s390x/arch_dump.c @@ -151,6 +151,7 @@ static int s390x_write_all_elf64_notes(const char *note_name, int ret = -1; for (nf = note_func; nf->note_contents_func; nf++) { + memset(¬e, 0, sizeof(note)); note.hdr.n_namesz = cpu_to_be32(sizeof(note.name)); note.hdr.n_descsz = cpu_to_be32(nf->contents_size); strncpy(note.name, note_name, sizeof(note.name)); diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 8be5648806..a2c077bdcd 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -148,6 +148,7 @@ typedef struct CPUS390XState { } CPUS390XState; #include "cpu-qom.h" +#include <sysemu/kvm.h> /* distinguish between 24 bit and 31 bit addressing */ #define HIGH_ORDER_BIT 0x80000000 @@ -692,6 +693,14 @@ static inline const char *cc_name(int cc_op) return cc_names[cc_op]; } +static inline void setcc(S390CPU *cpu, uint64_t cc) +{ + CPUS390XState *env = &cpu->env; + + env->psw.mask &= ~(3ull << 44); + env->psw.mask |= (cc & 3) << 44; +} + typedef struct LowCore { /* prefix area: defined by architecture */ @@ -1058,8 +1067,6 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen); void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, uintptr_t retaddr); -#include <sysemu/kvm.h> - #ifdef CONFIG_KVM void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, uint16_t subchannel_nr, uint32_t io_int_parm, diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c index 85fd285736..8d6363df4e 100644 --- a/target-s390x/ioinst.c +++ b/target-s390x/ioinst.c @@ -36,7 +36,7 @@ int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, return 0; } -int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1) { int cssid, ssid, schid, m; SubchDev *sch; @@ -44,8 +44,8 @@ int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) int cc; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); + return; } trace_ioinst_sch_id("xsch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -66,11 +66,10 @@ int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) cc = 1; break; } - - return cc; + setcc(cpu, cc); } -int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1) { int cssid, ssid, schid, m; SubchDev *sch; @@ -78,8 +77,8 @@ int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) int cc; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); + return; } trace_ioinst_sch_id("csch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -91,10 +90,10 @@ int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) } else { cc = 0; } - return cc; + setcc(cpu, cc); } -int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1) { int cssid, ssid, schid, m; SubchDev *sch; @@ -102,8 +101,8 @@ int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) int cc; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); + return; } trace_ioinst_sch_id("hsch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -124,8 +123,7 @@ int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) cc = 1; break; } - - return cc; + setcc(cpu, cc); } static int ioinst_schib_valid(SCHIB *schib) @@ -141,7 +139,7 @@ static int ioinst_schib_valid(SCHIB *schib) return 1; } -int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { int cssid, ssid, schid, m; SubchDev *sch; @@ -150,22 +148,21 @@ int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) int ret = -ENODEV; int cc; hwaddr len = sizeof(*schib); + CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); if (addr & 3) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } schib = s390_cpu_physical_memory_map(env, addr, &len, 0); if (!schib || len != sizeof(*schib)) { program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; goto out; } if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || !ioinst_schib_valid(schib)) { program_interrupt(env, PGM_OPERAND, 2); - cc = -EIO; goto out; } trace_ioinst_sch_id("msch", cssid, ssid, schid); @@ -187,9 +184,10 @@ int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) cc = 1; break; } + setcc(cpu, cc); + out: s390_cpu_physical_memory_unmap(env, schib, len, 0); - return cc; } static void copy_orb_from_guest(ORB *dest, const ORB *src) @@ -213,7 +211,7 @@ static int ioinst_orb_valid(ORB *orb) return 1; } -int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { int cssid, ssid, schid, m; SubchDev *sch; @@ -222,23 +220,22 @@ int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) int ret = -ENODEV; int cc; hwaddr len = sizeof(*orig_orb); + CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); if (addr & 3) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0); if (!orig_orb || len != sizeof(*orig_orb)) { program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; goto out; } copy_orb_from_guest(&orb, orig_orb); if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || !ioinst_orb_valid(&orb)) { program_interrupt(env, PGM_OPERAND, 2); - cc = -EIO; goto out; } trace_ioinst_sch_id("ssch", cssid, ssid, schid); @@ -260,38 +257,39 @@ int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) cc = 1; break; } + setcc(cpu, cc); out: s390_cpu_physical_memory_unmap(env, orig_orb, len, 0); - return cc; } -int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb) +void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb) { CRW *crw; uint64_t addr; int cc; hwaddr len = sizeof(*crw); + CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); if (addr & 3) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } crw = s390_cpu_physical_memory_map(env, addr, &len, 1); if (!crw || len != sizeof(*crw)) { program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; goto out; } cc = css_do_stcrw(crw); /* 0 - crw stored, 1 - zeroes stored */ + setcc(cpu, cc); + out: s390_cpu_physical_memory_unmap(env, crw, len, 1); - return cc; } -int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { int cssid, ssid, schid, m; SubchDev *sch; @@ -299,22 +297,21 @@ int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) int cc; SCHIB *schib; hwaddr len = sizeof(*schib); + CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); if (addr & 3) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } schib = s390_cpu_physical_memory_map(env, addr, &len, 1); if (!schib || len != sizeof(*schib)) { program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; goto out; } if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { program_interrupt(env, PGM_OPERAND, 2); - cc = -EIO; goto out; } trace_ioinst_sch_id("stsch", cssid, ssid, schid); @@ -336,9 +333,10 @@ int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) cc = 0; } } + setcc(cpu, cc); + out: s390_cpu_physical_memory_unmap(env, schib, len, 1); - return cc; } int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) @@ -575,7 +573,7 @@ static void ioinst_handle_chsc_unimplemented(ChscResp *res) res->param = 0; } -int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) +void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb) { ChscReq *req; ChscResp *res; @@ -584,7 +582,7 @@ int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) uint16_t len; uint16_t command; hwaddr map_size = TARGET_PAGE_SIZE; - int ret = 0; + CPUS390XState *env = &cpu->env; trace_ioinst("chsc"); reg = (ipb >> 20) & 0x00f; @@ -592,19 +590,17 @@ int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) /* Page boundary? */ if (addr & 0xfff) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } req = s390_cpu_physical_memory_map(env, addr, &map_size, 1); if (!req || map_size != TARGET_PAGE_SIZE) { program_interrupt(env, PGM_ADDRESSING, 2); - ret = -EIO; goto out; } len = be16_to_cpu(req->len); /* Length field valid? */ if ((len < 16) || (len > 4088) || (len & 7)) { program_interrupt(env, PGM_OPERAND, 2); - ret = -EIO; goto out; } memset((char *)req + len, 0, TARGET_PAGE_SIZE - len); @@ -628,7 +624,6 @@ int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) out: s390_cpu_physical_memory_unmap(env, req, map_size, 1); - return ret; } int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb) @@ -666,18 +661,19 @@ out: #define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1) #define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001) -int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, - uint32_t ipb) +void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, + uint32_t ipb) { uint8_t mbk; int update; int dct; + CPUS390XState *env = &cpu->env; trace_ioinst("schm"); if (SCHM_REG1_RES(reg1)) { program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + return; } mbk = SCHM_REG1_MBK(reg1); @@ -686,15 +682,13 @@ int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, if (update && (reg2 & 0x000000000000001f)) { program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + return; } css_do_schm(mbk, update, dct, update ? reg2 : 0); - - return 0; } -int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1) { int cssid, ssid, schid, m; SubchDev *sch; @@ -702,8 +696,8 @@ int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) int cc; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); + return; } trace_ioinst_sch_id("rsch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -724,24 +718,23 @@ int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) cc = 1; break; } - - return cc; - + setcc(cpu, cc); } #define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00) #define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16) #define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff) -int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1) { int cc; uint8_t cssid; uint8_t chpid; int ret; + CPUS390XState *env = &cpu->env; if (RCHP_REG1_RES(reg1)) { program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + return; } cssid = RCHP_REG1_CSSID(reg1); @@ -764,19 +757,16 @@ int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1) default: /* Invalid channel subsystem. */ program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + return; } - - return cc; + setcc(cpu, cc); } #define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000) -int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1) { /* We do not provide address limit checking, so let's suppress it. */ if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); } - return 0; } diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h index 7bed2910dc..613da49b3b 100644 --- a/target-s390x/ioinst.h +++ b/target-s390x/ioinst.h @@ -214,20 +214,20 @@ typedef struct IOIntCode { int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, int *schid); -int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); -int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); -int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb); -int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); +void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); +void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb); +void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); -int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb); +void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb); int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb); -int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, - uint32_t ipb); -int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1); +void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, + uint32_t ipb); +void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1); #endif diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 185c8f5a45..a444f6999b 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -93,9 +93,15 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { static int cap_sync_regs; +static void *legacy_s390_alloc(ram_addr_t size); + int kvm_arch_init(KVMState *s) { cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS); + if (!kvm_check_extension(s, KVM_CAP_S390_GMAP) + || !kvm_check_extension(s, KVM_CAP_S390_COW)) { + phys_mem_set_alloc(legacy_s390_alloc); + } return 0; } @@ -326,22 +332,7 @@ static void *legacy_s390_alloc(ram_addr_t size) mem = mmap((void *) 0x800000000ULL, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - if (mem == MAP_FAILED) { - fprintf(stderr, "Allocating RAM failed\n"); - abort(); - } - return mem; -} - -void *kvm_arch_ram_alloc(ram_addr_t size) -{ - /* Can we use the standard allocation ? */ - if (kvm_check_extension(kvm_state, KVM_CAP_S390_GMAP) && - kvm_check_extension(kvm_state, KVM_CAP_S390_COW)) { - return NULL; - } else { - return legacy_s390_alloc(size); - } + return mem == MAP_FAILED ? NULL : mem; } int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) @@ -427,18 +418,6 @@ static void enter_pgmcheck(S390CPU *cpu, uint16_t code) kvm_s390_interrupt(cpu, KVM_S390_PROGRAM_INT, code); } -static inline void setcc(S390CPU *cpu, uint64_t cc) -{ - CPUS390XState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - cs->kvm_run->psw_mask &= ~(3ull << 44); - cs->kvm_run->psw_mask |= (cc & 3) << 44; - - env->psw.mask &= ~(3ul << 44); - env->psw.mask |= (cc & 3) << 44; -} - static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, uint16_t ipbh0) { @@ -448,6 +427,10 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, int r = 0; cpu_synchronize_state(CPU(cpu)); + if (env->psw.mask & PSW_MASK_PSTATE) { + enter_pgmcheck(cpu, PGM_PRIVILEGED); + return 0; + } sccb = env->regs[ipbh0 & 0xf]; code = env->regs[(ipbh0 & 0xf0) >> 4]; @@ -463,8 +446,6 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run, uint8_t ipa0, uint8_t ipa1, uint8_t ipb) { - int r = 0; - int no_cc = 0; CPUS390XState *env = &cpu->env; CPUState *cs = CPU(cpu); @@ -478,69 +459,61 @@ static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run, switch (ipa1) { case PRIV_XSCH: - r = ioinst_handle_xsch(env, env->regs[1]); + ioinst_handle_xsch(cpu, env->regs[1]); break; case PRIV_CSCH: - r = ioinst_handle_csch(env, env->regs[1]); + ioinst_handle_csch(cpu, env->regs[1]); break; case PRIV_HSCH: - r = ioinst_handle_hsch(env, env->regs[1]); + ioinst_handle_hsch(cpu, env->regs[1]); break; case PRIV_MSCH: - r = ioinst_handle_msch(env, env->regs[1], run->s390_sieic.ipb); + ioinst_handle_msch(cpu, env->regs[1], run->s390_sieic.ipb); break; case PRIV_SSCH: - r = ioinst_handle_ssch(env, env->regs[1], run->s390_sieic.ipb); + ioinst_handle_ssch(cpu, env->regs[1], run->s390_sieic.ipb); break; case PRIV_STCRW: - r = ioinst_handle_stcrw(env, run->s390_sieic.ipb); + ioinst_handle_stcrw(cpu, run->s390_sieic.ipb); break; case PRIV_STSCH: - r = ioinst_handle_stsch(env, env->regs[1], run->s390_sieic.ipb); + ioinst_handle_stsch(cpu, env->regs[1], run->s390_sieic.ipb); break; case PRIV_TSCH: /* We should only get tsch via KVM_EXIT_S390_TSCH. */ fprintf(stderr, "Spurious tsch intercept\n"); break; case PRIV_CHSC: - r = ioinst_handle_chsc(env, run->s390_sieic.ipb); + ioinst_handle_chsc(cpu, run->s390_sieic.ipb); break; case PRIV_TPI: /* This should have been handled by kvm already. */ fprintf(stderr, "Spurious tpi intercept\n"); break; case PRIV_SCHM: - no_cc = 1; - r = ioinst_handle_schm(env, env->regs[1], env->regs[2], - run->s390_sieic.ipb); + ioinst_handle_schm(cpu, env->regs[1], env->regs[2], + run->s390_sieic.ipb); break; case PRIV_RSCH: - r = ioinst_handle_rsch(env, env->regs[1]); + ioinst_handle_rsch(cpu, env->regs[1]); break; case PRIV_RCHP: - r = ioinst_handle_rchp(env, env->regs[1]); + ioinst_handle_rchp(cpu, env->regs[1]); break; case PRIV_STCPS: /* We do not provide this instruction, it is suppressed. */ - no_cc = 1; - r = 0; break; case PRIV_SAL: - no_cc = 1; - r = ioinst_handle_sal(env, env->regs[1]); + ioinst_handle_sal(cpu, env->regs[1]); break; case PRIV_SIGA: /* Not provided, set CC = 3 for subchannel not operational */ - r = 3; + setcc(cpu, 3); break; default: return -1; } - if (r >= 0 && !no_cc) { - setcc(cpu, r); - } - return 0; } diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index 1690907169..10d04252d5 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -33,6 +33,7 @@ #include "exec/softmmu_exec.h" #include "sysemu/cpus.h" #include "sysemu/sysemu.h" +#include "hw/s390x/ebcdic.h" #endif /* #define DEBUG_HELPER */ @@ -72,86 +73,6 @@ void HELPER(exception)(CPUS390XState *env, uint32_t excp) #ifndef CONFIG_USER_ONLY -/* EBCDIC handling */ -static const uint8_t ebcdic2ascii[] = { - 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, - 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, - 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, - 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, - 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, - 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, - 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, - 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, - 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, - 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, - 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, - 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, - 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, - 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, - 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, - 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, - 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, - 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, - 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, - 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, - 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, - 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07, -}; - -static const uint8_t ascii2ebcdic[] = { - 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, - 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, - 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, - 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, - 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, - 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, - 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, - 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, - 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, - 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF -}; - -static inline void ebcdic_put(uint8_t *p, const char *ascii, int len) -{ - int i; - - for (i = 0; i < len; i++) { - p[i] = ascii2ebcdic[(uint8_t)ascii[i]]; - } -} - void program_interrupt(CPUS390XState *env, uint32_t code, int ilen) { qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", @@ -192,6 +113,29 @@ static void cpu_reset_all(void) } } +static void cpu_full_reset_all(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + cpu_reset(cpu); + } +} + +static int modified_clear_reset(S390CPU *cpu) +{ + S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); + + pause_all_vcpus(); + cpu_synchronize_all_states(); + cpu_full_reset_all(); + io_subsystem_reset(); + scc->load_normal(CPU(cpu)); + cpu_synchronize_all_post_reset(); + resume_all_vcpus(); + return 0; +} + static int load_normal_reset(S390CPU *cpu) { S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); @@ -225,6 +169,9 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) } switch (subcode) { + case 0: + modified_clear_reset(s390_env_get_cpu(env)); + break; case 1: load_normal_reset(s390_env_get_cpu(env)); break; diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c index 25955563b8..97e33edcfd 100644 --- a/tcg/ppc/tcg-target.c +++ b/tcg/ppc/tcg-target.c @@ -450,7 +450,9 @@ static const uint32_t tcg_to_bc[] = { static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - tcg_out32 (s, OR | SAB (arg, ret, arg)); + if (ret != arg) { + tcg_out32(s, OR | SAB(arg, ret, arg)); + } } static void tcg_out_movi(TCGContext *s, TCGType type, @@ -490,7 +492,8 @@ static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target) } } -static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg) +static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg, + int lk) { #ifdef _CALL_AIX int reg; @@ -504,14 +507,14 @@ static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg) tcg_out32 (s, LWZ | RT (0) | RA (reg)); tcg_out32 (s, MTSPR | RA (0) | CTR); tcg_out32 (s, LWZ | RT (2) | RA (reg) | 4); - tcg_out32 (s, BCCTR | BO_ALWAYS | LK); + tcg_out32 (s, BCCTR | BO_ALWAYS | lk); #else if (const_arg) { - tcg_out_b (s, LK, arg); + tcg_out_b (s, lk, arg); } else { tcg_out32 (s, MTSPR | RS (arg) | LR); - tcg_out32 (s, BCLR | BO_ALWAYS | LK); + tcg_out32 (s, BCLR | BO_ALWAYS | lk); } #endif } @@ -549,118 +552,128 @@ static void add_qemu_ldst_label (TCGContext *s, label->label_ptr[0] = label_ptr; } -/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, - int mmu_idx) */ +/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, + * int mmu_idx, uintptr_t ra) + */ static const void * const qemu_ld_helpers[4] = { - helper_ldb_mmu, - helper_ldw_mmu, - helper_ldl_mmu, - helper_ldq_mmu, + helper_ret_ldub_mmu, + helper_ret_lduw_mmu, + helper_ret_ldul_mmu, + helper_ret_ldq_mmu, }; -/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, - uintxx_t val, int mmu_idx) */ +/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, + * uintxx_t val, int mmu_idx, uintptr_t ra) + */ static const void * const qemu_st_helpers[4] = { - helper_stb_mmu, - helper_stw_mmu, - helper_stl_mmu, - helper_stq_mmu, + helper_ret_stb_mmu, + helper_ret_stw_mmu, + helper_ret_stl_mmu, + helper_ret_stq_mmu, }; static void *ld_trampolines[4]; static void *st_trampolines[4]; -static void tcg_out_tlb_check (TCGContext *s, int r0, int r1, int r2, - int addr_reg, int addr_reg2, int s_bits, - int offset1, int offset2, uint8_t **label_ptr) +/* Perform the TLB load and compare. Branches to the slow path, placing the + address of the branch in *LABEL_PTR. Loads the addend of the TLB into R0. + Clobbers R1 and R2. */ + +static void tcg_out_tlb_check(TCGContext *s, TCGReg r0, TCGReg r1, TCGReg r2, + TCGReg addrlo, TCGReg addrhi, int s_bits, + int mem_index, int is_load, uint8_t **label_ptr) { + int cmp_off = + (is_load + ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); + int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); uint16_t retranst; + TCGReg base = TCG_AREG0; + + /* Extract the page index, shifted into place for tlb index. */ + tcg_out32(s, (RLWINM + | RA(r0) + | RS(addrlo) + | SH(32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)) + | MB(32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS)) + | ME(31 - CPU_TLB_ENTRY_BITS))); + + /* Compensate for very large offsets. */ + if (add_off >= 0x8000) { + /* Most target env are smaller than 32k; none are larger than 64k. + Simplify the logic here merely to offset by 0x7ff0, giving us a + range just shy of 64k. Check this assumption. */ + QEMU_BUILD_BUG_ON(offsetof(CPUArchState, + tlb_table[NB_MMU_MODES - 1][1]) + > 0x7ff0 + 0x7fff); + tcg_out32(s, ADDI | RT(r1) | RA(base) | 0x7ff0); + base = r1; + cmp_off -= 0x7ff0; + add_off -= 0x7ff0; + } - tcg_out32 (s, (RLWINM - | RA (r0) - | RS (addr_reg) - | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)) - | MB (32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS)) - | ME (31 - CPU_TLB_ENTRY_BITS) - ) - ); - tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0)); - tcg_out32 (s, (LWZU - | RT (r1) - | RA (r0) - | offset1 - ) - ); - tcg_out32 (s, (RLWINM - | RA (r2) - | RS (addr_reg) - | SH (0) - | MB ((32 - s_bits) & 31) - | ME (31 - TARGET_PAGE_BITS) - ) - ); + /* Clear the non-page, non-alignment bits from the address. */ + tcg_out32(s, (RLWINM + | RA(r2) + | RS(addrlo) + | SH(0) + | MB((32 - s_bits) & 31) + | ME(31 - TARGET_PAGE_BITS))); - tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1)); -#if TARGET_LONG_BITS == 64 - tcg_out32 (s, LWZ | RT (r1) | RA (r0) | 4); - tcg_out32 (s, CMP | BF (6) | RA (addr_reg2) | RB (r1)); - tcg_out32 (s, CRAND | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ)); -#endif - *label_ptr = s->code_ptr; - retranst = ((uint16_t *) s->code_ptr)[1] & ~3; - tcg_out32 (s, BC | BI (7, CR_EQ) | retranst | BO_COND_FALSE); + tcg_out32(s, ADD | RT(r0) | RA(r0) | RB(base)); + base = r0; - /* r0 now contains &env->tlb_table[mem_index][index].addr_x */ - tcg_out32 (s, (LWZ - | RT (r0) - | RA (r0) - | offset2 - ) - ); - /* r0 = env->tlb_table[mem_index][index].addend */ - tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg)); - /* r0 = env->tlb_table[mem_index][index].addend + addr */ + /* Load the tlb comparator. */ + tcg_out32(s, LWZ | RT(r1) | RA(base) | (cmp_off & 0xffff)); + tcg_out32(s, CMP | BF(7) | RA(r2) | RB(r1)); + + if (TARGET_LONG_BITS == 64) { + tcg_out32(s, LWZ | RT(r1) | RA(base) | ((cmp_off + 4) & 0xffff)); + } + + /* Load the tlb addend for use on the fast path. + Do this asap to minimize load delay. */ + tcg_out32(s, LWZ | RT(r0) | RA(base) | (add_off & 0xffff)); + + if (TARGET_LONG_BITS == 64) { + tcg_out32(s, CMP | BF(6) | RA(addrhi) | RB(r1)); + tcg_out32(s, CRAND | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + } + + /* Use a conditional branch-and-link so that we load a pointer to + somewhere within the current opcode, for passing on to the helper. + This address cannot be used for a tail call, but it's shorter + than forming an address from scratch. */ + *label_ptr = s->code_ptr; + retranst = ((uint16_t *) s->code_ptr)[1] & ~3; + tcg_out32(s, BC | BI(7, CR_EQ) | retranst | BO_COND_FALSE | LK); } #endif static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, data_reg2, r0, r1, rbase, bswap; + TCGReg addrlo, datalo, datahi, rbase; + int bswap; #ifdef CONFIG_SOFTMMU - int mem_index, s_bits, r2, addr_reg2; + int mem_index; + TCGReg addrhi; uint8_t *label_ptr; #endif - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; - addr_reg = *args++; + datalo = *args++; + datahi = (opc == 3 ? *args++ : 0); + addrlo = *args++; #ifdef CONFIG_SOFTMMU -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#else - addr_reg2 = 0; -#endif + addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); mem_index = *args; - s_bits = opc & 3; - r0 = 3; - r1 = 4; - r2 = 0; - rbase = 0; - - tcg_out_tlb_check ( - s, r0, r1, r2, addr_reg, addr_reg2, s_bits, - offsetof (CPUArchState, tlb_table[mem_index][0].addr_read), - offsetof (CPUTLBEntry, addend) - offsetof (CPUTLBEntry, addr_read), - &label_ptr - ); + + tcg_out_tlb_check(s, TCG_REG_R3, TCG_REG_R4, TCG_REG_R0, addrlo, + addrhi, opc & 3, mem_index, 0, &label_ptr); + rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ - r0 = addr_reg; - r1 = 3; rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; #endif @@ -673,106 +686,72 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) switch (opc) { default: case 0: - tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0)); + tcg_out32(s, LBZX | TAB(datalo, rbase, addrlo)); break; case 0|4: - tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0)); - tcg_out32 (s, EXTSB | RA (data_reg) | RS (data_reg)); + tcg_out32(s, LBZX | TAB(datalo, rbase, addrlo)); + tcg_out32(s, EXTSB | RA(datalo) | RS(datalo)); break; case 1: - if (bswap) - tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0)); - else - tcg_out32 (s, LHZX | TAB (data_reg, rbase, r0)); + tcg_out32(s, (bswap ? LHBRX : LHZX) | TAB(datalo, rbase, addrlo)); break; case 1|4: if (bswap) { - tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0)); - tcg_out32 (s, EXTSH | RA (data_reg) | RS (data_reg)); + tcg_out32(s, LHBRX | TAB(datalo, rbase, addrlo)); + tcg_out32(s, EXTSH | RA(datalo) | RS(datalo)); + } else { + tcg_out32(s, LHAX | TAB(datalo, rbase, addrlo)); } - else tcg_out32 (s, LHAX | TAB (data_reg, rbase, r0)); break; case 2: - if (bswap) - tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); - else - tcg_out32 (s, LWZX | TAB (data_reg, rbase, r0)); + tcg_out32(s, (bswap ? LWBRX : LWZX) | TAB(datalo, rbase, addrlo)); break; case 3: if (bswap) { - tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); - tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); - tcg_out32 (s, LWBRX | TAB (data_reg2, rbase, r1)); - } - else { -#ifdef CONFIG_USE_GUEST_BASE - tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); - tcg_out32 (s, LWZX | TAB (data_reg2, rbase, r0)); - tcg_out32 (s, LWZX | TAB (data_reg, rbase, r1)); -#else - if (r0 == data_reg2) { - tcg_out32 (s, LWZ | RT (0) | RA (r0)); - tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4); - tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 0); - } - else { - tcg_out32 (s, LWZ | RT (data_reg2) | RA (r0)); - tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4); - } -#endif + tcg_out32(s, ADDI | RT(TCG_REG_R0) | RA(addrlo) | 4); + tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); + tcg_out32(s, LWBRX | TAB(datahi, rbase, TCG_REG_R0)); + } else if (rbase != 0) { + tcg_out32(s, ADDI | RT(TCG_REG_R0) | RA(addrlo) | 4); + tcg_out32(s, LWZX | TAB(datahi, rbase, addrlo)); + tcg_out32(s, LWZX | TAB(datalo, rbase, TCG_REG_R0)); + } else if (addrlo == datahi) { + tcg_out32(s, LWZ | RT(datalo) | RA(addrlo) | 4); + tcg_out32(s, LWZ | RT(datahi) | RA(addrlo)); + } else { + tcg_out32(s, LWZ | RT(datahi) | RA(addrlo)); + tcg_out32(s, LWZ | RT(datalo) | RA(addrlo) | 4); } break; } #ifdef CONFIG_SOFTMMU - add_qemu_ldst_label (s, - 1, - opc, - data_reg, - data_reg2, - addr_reg, - addr_reg2, - mem_index, - s->code_ptr, - label_ptr); + add_qemu_ldst_label(s, 1, opc, datalo, datahi, addrlo, + addrhi, mem_index, s->code_ptr, label_ptr); #endif } static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, r0, r1, data_reg, data_reg2, bswap, rbase; + TCGReg addrlo, datalo, datahi, rbase; + int bswap; #ifdef CONFIG_SOFTMMU - int mem_index, r2, addr_reg2; + int mem_index; + TCGReg addrhi; uint8_t *label_ptr; #endif - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; - addr_reg = *args++; + datalo = *args++; + datahi = (opc == 3 ? *args++ : 0); + addrlo = *args++; #ifdef CONFIG_SOFTMMU -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#else - addr_reg2 = 0; -#endif + addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); mem_index = *args; - r0 = 3; - r1 = 4; - r2 = 0; - rbase = 0; - - tcg_out_tlb_check ( - s, r0, r1, r2, addr_reg, addr_reg2, opc & 3, - offsetof (CPUArchState, tlb_table[mem_index][0].addr_write), - offsetof (CPUTLBEntry, addend) - offsetof (CPUTLBEntry, addr_write), - &label_ptr - ); + + tcg_out_tlb_check(s, TCG_REG_R3, TCG_REG_R4, TCG_REG_R0, addrlo, + addrhi, opc & 3, mem_index, 0, &label_ptr); + rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ - r0 = addr_reg; - r1 = 3; rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; #endif @@ -783,180 +762,132 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) #endif switch (opc) { case 0: - tcg_out32 (s, STBX | SAB (data_reg, rbase, r0)); + tcg_out32(s, STBX | SAB(datalo, rbase, addrlo)); break; case 1: - if (bswap) - tcg_out32 (s, STHBRX | SAB (data_reg, rbase, r0)); - else - tcg_out32 (s, STHX | SAB (data_reg, rbase, r0)); + tcg_out32(s, (bswap ? STHBRX : STHX) | SAB(datalo, rbase, addrlo)); break; case 2: - if (bswap) - tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0)); - else - tcg_out32 (s, STWX | SAB (data_reg, rbase, r0)); + tcg_out32(s, (bswap ? STWBRX : STWX) | SAB(datalo, rbase, addrlo)); break; case 3: if (bswap) { - tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); - tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0)); - tcg_out32 (s, STWBRX | SAB (data_reg2, rbase, r1)); - } - else { -#ifdef CONFIG_USE_GUEST_BASE - tcg_out32 (s, STWX | SAB (data_reg2, rbase, r0)); - tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); - tcg_out32 (s, STWX | SAB (data_reg, rbase, r1)); -#else - tcg_out32 (s, STW | RS (data_reg2) | RA (r0)); - tcg_out32 (s, STW | RS (data_reg) | RA (r0) | 4); -#endif + tcg_out32(s, ADDI | RT(TCG_REG_R0) | RA(addrlo) | 4); + tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); + tcg_out32(s, STWBRX | SAB(datahi, rbase, TCG_REG_R0)); + } else if (rbase != 0) { + tcg_out32(s, ADDI | RT(TCG_REG_R0) | RA(addrlo) | 4); + tcg_out32(s, STWX | SAB(datahi, rbase, addrlo)); + tcg_out32(s, STWX | SAB(datalo, rbase, TCG_REG_R0)); + } else { + tcg_out32(s, STW | RS(datahi) | RA(addrlo)); + tcg_out32(s, STW | RS(datalo) | RA(addrlo) | 4); } break; } #ifdef CONFIG_SOFTMMU - add_qemu_ldst_label (s, - 0, - opc, - data_reg, - data_reg2, - addr_reg, - addr_reg2, - mem_index, - s->code_ptr, - label_ptr); + add_qemu_ldst_label(s, 0, opc, datalo, datahi, addrlo, addrhi, + mem_index, s->code_ptr, label_ptr); #endif } #if defined(CONFIG_SOFTMMU) -static void tcg_out_qemu_ld_slow_path (TCGContext *s, TCGLabelQemuLdst *label) +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - int s_bits; - int ir; - int opc = label->opc; - int mem_index = label->mem_index; - int data_reg = label->datalo_reg; - int data_reg2 = label->datahi_reg; - int addr_reg = label->addrlo_reg; - uint8_t *raddr = label->raddr; - uint8_t **label_ptr = &label->label_ptr[0]; - - s_bits = opc & 3; - - /* resolve label address */ - reloc_pc14 (label_ptr[0], (tcg_target_long) s->code_ptr); - - /* slow path */ - ir = 4; -#if TARGET_LONG_BITS == 32 - tcg_out_mov (s, TCG_TYPE_I32, ir++, addr_reg); -#else + TCGReg ir, datalo, datahi; + + reloc_pc14 (l->label_ptr[0], (uintptr_t)s->code_ptr); + + ir = TCG_REG_R4; + if (TARGET_LONG_BITS == 32) { + tcg_out_mov(s, TCG_TYPE_I32, ir++, l->addrlo_reg); + } else { #ifdef TCG_TARGET_CALL_ALIGN_ARGS - ir |= 1; -#endif - tcg_out_mov (s, TCG_TYPE_I32, ir++, label->addrhi_reg); - tcg_out_mov (s, TCG_TYPE_I32, ir++, addr_reg); + ir |= 1; #endif - tcg_out_movi (s, TCG_TYPE_I32, ir, mem_index); - tcg_out_call (s, (tcg_target_long) ld_trampolines[s_bits], 1); - tcg_out32 (s, (tcg_target_long) raddr); - switch (opc) { + tcg_out_mov(s, TCG_TYPE_I32, ir++, l->addrhi_reg); + tcg_out_mov(s, TCG_TYPE_I32, ir++, l->addrlo_reg); + } + tcg_out_movi(s, TCG_TYPE_I32, ir++, l->mem_index); + tcg_out32(s, MFSPR | RT(ir++) | LR); + tcg_out_b(s, LK, (uintptr_t)ld_trampolines[l->opc & 3]); + + datalo = l->datalo_reg; + switch (l->opc) { case 0|4: - tcg_out32 (s, EXTSB | RA (data_reg) | RS (3)); + tcg_out32(s, EXTSB | RA(datalo) | RS(TCG_REG_R3)); break; case 1|4: - tcg_out32 (s, EXTSH | RA (data_reg) | RS (3)); + tcg_out32(s, EXTSH | RA(datalo) | RS(TCG_REG_R3)); break; case 0: case 1: case 2: - if (data_reg != 3) - tcg_out_mov (s, TCG_TYPE_I32, data_reg, 3); + tcg_out_mov(s, TCG_TYPE_I32, datalo, TCG_REG_R3); break; case 3: - if (data_reg == 3) { - if (data_reg2 == 4) { - tcg_out_mov (s, TCG_TYPE_I32, 0, 4); - tcg_out_mov (s, TCG_TYPE_I32, 4, 3); - tcg_out_mov (s, TCG_TYPE_I32, 3, 0); - } - else { - tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3); - tcg_out_mov (s, TCG_TYPE_I32, 3, 4); - } - } - else { - if (data_reg != 4) tcg_out_mov (s, TCG_TYPE_I32, data_reg, 4); - if (data_reg2 != 3) tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3); + datahi = l->datahi_reg; + if (datalo != TCG_REG_R3) { + tcg_out_mov(s, TCG_TYPE_I32, datalo, TCG_REG_R4); + tcg_out_mov(s, TCG_TYPE_I32, datahi, TCG_REG_R3); + } else if (datahi != TCG_REG_R4) { + tcg_out_mov(s, TCG_TYPE_I32, datahi, TCG_REG_R3); + tcg_out_mov(s, TCG_TYPE_I32, datalo, TCG_REG_R4); + } else { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R0, TCG_REG_R4); + tcg_out_mov(s, TCG_TYPE_I32, datahi, TCG_REG_R3); + tcg_out_mov(s, TCG_TYPE_I32, datalo, TCG_REG_R0); } break; } - /* Jump to the code corresponding to next IR of qemu_st */ - tcg_out_b (s, 0, (tcg_target_long) raddr); + tcg_out_b (s, 0, (uintptr_t)l->raddr); } -static void tcg_out_qemu_st_slow_path (TCGContext *s, TCGLabelQemuLdst *label) +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - int ir; - int opc = label->opc; - int mem_index = label->mem_index; - int data_reg = label->datalo_reg; - int data_reg2 = label->datahi_reg; - int addr_reg = label->addrlo_reg; - uint8_t *raddr = label->raddr; - uint8_t **label_ptr = &label->label_ptr[0]; - - /* resolve label address */ - reloc_pc14 (label_ptr[0], (tcg_target_long) s->code_ptr); - - /* slow path */ - ir = 4; -#if TARGET_LONG_BITS == 32 - tcg_out_mov (s, TCG_TYPE_I32, ir++, addr_reg); -#else + TCGReg ir, datalo; + + reloc_pc14 (l->label_ptr[0], (tcg_target_long) s->code_ptr); + + ir = TCG_REG_R4; + if (TARGET_LONG_BITS == 32) { + tcg_out_mov (s, TCG_TYPE_I32, ir++, l->addrlo_reg); + } else { #ifdef TCG_TARGET_CALL_ALIGN_ARGS - ir |= 1; -#endif - tcg_out_mov (s, TCG_TYPE_I32, ir++, label->addrhi_reg); - tcg_out_mov (s, TCG_TYPE_I32, ir++, addr_reg); + ir |= 1; #endif + tcg_out_mov (s, TCG_TYPE_I32, ir++, l->addrhi_reg); + tcg_out_mov (s, TCG_TYPE_I32, ir++, l->addrlo_reg); + } - switch (opc) { + datalo = l->datalo_reg; + switch (l->opc) { case 0: - tcg_out32 (s, (RLWINM - | RA (ir) - | RS (data_reg) - | SH (0) - | MB (24) - | ME (31))); + tcg_out32(s, (RLWINM | RA (ir) | RS (datalo) + | SH (0) | MB (24) | ME (31))); break; case 1: - tcg_out32 (s, (RLWINM - | RA (ir) - | RS (data_reg) - | SH (0) - | MB (16) - | ME (31))); + tcg_out32(s, (RLWINM | RA (ir) | RS (datalo) + | SH (0) | MB (16) | ME (31))); break; case 2: - tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg); + tcg_out_mov(s, TCG_TYPE_I32, ir, datalo); break; case 3: #ifdef TCG_TARGET_CALL_ALIGN_ARGS ir |= 1; #endif - tcg_out_mov (s, TCG_TYPE_I32, ir++, data_reg2); - tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg); + tcg_out_mov(s, TCG_TYPE_I32, ir++, l->datahi_reg); + tcg_out_mov(s, TCG_TYPE_I32, ir, datalo); break; } ir++; - tcg_out_movi (s, TCG_TYPE_I32, ir, mem_index); - tcg_out_call (s, (tcg_target_long) st_trampolines[opc], 1); - tcg_out32 (s, (tcg_target_long) raddr); - tcg_out_b (s, 0, (tcg_target_long) raddr); + tcg_out_movi(s, TCG_TYPE_I32, ir++, l->mem_index); + tcg_out32(s, MFSPR | RT(ir++) | LR); + tcg_out_b(s, LK, (uintptr_t)st_trampolines[l->opc]); + tcg_out_b(s, 0, (uintptr_t)l->raddr); } void tcg_out_tb_finalize(TCGContext *s) @@ -980,11 +911,8 @@ void tcg_out_tb_finalize(TCGContext *s) #ifdef CONFIG_SOFTMMU static void emit_ldst_trampoline (TCGContext *s, const void *ptr) { - tcg_out32 (s, MFSPR | RT (3) | LR); - tcg_out32 (s, ADDI | RT (3) | RA (3) | 4); - tcg_out32 (s, MTSPR | RS (3) | LR); tcg_out_mov (s, TCG_TYPE_I32, 3, TCG_AREG0); - tcg_out_b (s, 0, (tcg_target_long) ptr); + tcg_out_call (s, (tcg_target_long) ptr, 1, 0); } #endif @@ -1493,7 +1421,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, } break; case INDEX_op_call: - tcg_out_call (s, args[0], const_args[0]); + tcg_out_call (s, args[0], const_args[0], LK); break; case INDEX_op_movi_i32: tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); @@ -2018,7 +1946,7 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, { INDEX_op_qemu_ld32, { "r", "L" } }, - { INDEX_op_qemu_ld64, { "r", "r", "L" } }, + { INDEX_op_qemu_ld64, { "L", "L", "L" } }, { INDEX_op_qemu_st8, { "K", "K" } }, { INDEX_op_qemu_st16, { "K", "K" } }, @@ -2030,7 +1958,7 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, { INDEX_op_qemu_ld32, { "r", "L", "L" } }, - { INDEX_op_qemu_ld64, { "r", "L", "L", "L" } }, + { INDEX_op_qemu_ld64, { "L", "L", "L", "L" } }, { INDEX_op_qemu_st8, { "K", "K", "K" } }, { INDEX_op_qemu_st16, { "K", "K", "K" } }, diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c index 0bd1e0ce8c..332f4d8df1 100644 --- a/tcg/ppc64/tcg-target.c +++ b/tcg/ppc64/tcg-target.c @@ -31,13 +31,11 @@ static uint8_t *tb_ret_addr; -#define FAST_PATH - #if TARGET_LONG_BITS == 32 -#define LD_ADDR LWZU +#define LD_ADDR LWZ #define CMP_L 0 #else -#define LD_ADDR LDU +#define LD_ADDR LD #define CMP_L (1<<21) #endif @@ -99,7 +97,7 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { #endif static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R14, + TCG_REG_R14, /* call saved registers */ TCG_REG_R15, TCG_REG_R16, TCG_REG_R17, @@ -109,29 +107,24 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_R21, TCG_REG_R22, TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, TCG_REG_R28, TCG_REG_R29, TCG_REG_R30, TCG_REG_R31, -#ifdef __APPLE__ - TCG_REG_R2, -#endif - TCG_REG_R3, - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, -#ifndef __APPLE__ + TCG_REG_R12, /* call clobbered, non-arguments */ TCG_REG_R11, -#endif - TCG_REG_R12, - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27 + TCG_REG_R10, /* call clobbered, arguments */ + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_R7, + TCG_REG_R6, + TCG_REG_R5, + TCG_REG_R4, + TCG_REG_R3, }; static const int tcg_target_call_iarg_regs[] = { @@ -173,58 +166,74 @@ static const int tcg_target_callee_save_regs[] = { TCG_REG_R31 }; -static uint32_t reloc_pc24_val (void *pc, tcg_target_long target) +static inline bool in_range_b(tcg_target_long target) +{ + return target == sextract64(target, 0, 26); +} + +static uint32_t reloc_pc24_val(void *pc, tcg_target_long target) { tcg_target_long disp; - disp = target - (tcg_target_long) pc; - if ((disp << 38) >> 38 != disp) - tcg_abort (); + disp = target - (tcg_target_long)pc; + assert(in_range_b(disp)); return disp & 0x3fffffc; } -static void reloc_pc24 (void *pc, tcg_target_long target) +static void reloc_pc24(void *pc, tcg_target_long target) { - *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3fffffc) - | reloc_pc24_val (pc, target); + *(uint32_t *)pc = (*(uint32_t *)pc & ~0x3fffffc) + | reloc_pc24_val(pc, target); } -static uint16_t reloc_pc14_val (void *pc, tcg_target_long target) +static uint16_t reloc_pc14_val(void *pc, tcg_target_long target) { tcg_target_long disp; - disp = target - (tcg_target_long) pc; - if (disp != (int16_t) disp) - tcg_abort (); + disp = target - (tcg_target_long)pc; + if (disp != (int16_t) disp) { + tcg_abort(); + } return disp & 0xfffc; } -static void reloc_pc14 (void *pc, tcg_target_long target) +static void reloc_pc14(void *pc, tcg_target_long target) +{ + *(uint32_t *)pc = (*(uint32_t *)pc & ~0xfffc) | reloc_pc14_val(pc, target); +} + +static inline void tcg_out_b_noaddr(TCGContext *s, int insn) +{ + unsigned retrans = *(uint32_t *)s->code_ptr & 0x3fffffc; + tcg_out32(s, insn | retrans); +} + +static inline void tcg_out_bc_noaddr(TCGContext *s, int insn) { - *(uint32_t *) pc = (*(uint32_t *) pc & ~0xfffc) - | reloc_pc14_val (pc, target); + unsigned retrans = *(uint32_t *)s->code_ptr & 0xfffc; + tcg_out32(s, insn | retrans); } -static void patch_reloc (uint8_t *code_ptr, int type, - intptr_t value, intptr_t addend) +static void patch_reloc(uint8_t *code_ptr, int type, + intptr_t value, intptr_t addend) { value += addend; switch (type) { case R_PPC_REL14: - reloc_pc14 (code_ptr, value); + reloc_pc14(code_ptr, value); break; case R_PPC_REL24: - reloc_pc24 (code_ptr, value); + reloc_pc24(code_ptr, value); break; default: - tcg_abort (); + tcg_abort(); } } /* parse target specific constraints */ -static int target_parse_constraint (TCGArgConstraint *ct, const char **pct_str) +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) { const char *ct_str; @@ -232,29 +241,29 @@ static int target_parse_constraint (TCGArgConstraint *ct, const char **pct_str) switch (ct_str[0]) { case 'A': case 'B': case 'C': case 'D': ct->ct |= TCG_CT_REG; - tcg_regset_set_reg (ct->u.regs, 3 + ct_str[0] - 'A'); + tcg_regset_set_reg(ct->u.regs, 3 + ct_str[0] - 'A'); break; case 'r': ct->ct |= TCG_CT_REG; - tcg_regset_set32 (ct->u.regs, 0, 0xffffffff); + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); break; case 'L': /* qemu_ld constraint */ ct->ct |= TCG_CT_REG; - tcg_regset_set32 (ct->u.regs, 0, 0xffffffff); - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3); + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); #ifdef CONFIG_SOFTMMU - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4); - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R5); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); #endif break; case 'S': /* qemu_st constraint */ ct->ct |= TCG_CT_REG; - tcg_regset_set32 (ct->u.regs, 0, 0xffffffff); - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3); + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); #ifdef CONFIG_SOFTMMU - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4); - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R5); - tcg_regset_reset_reg (ct->u.regs, TCG_REG_R6); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); #endif break; case 'I': @@ -284,8 +293,8 @@ static int target_parse_constraint (TCGArgConstraint *ct, const char **pct_str) } /* test if a constant matches the constraint */ -static int tcg_target_const_match (tcg_target_long val, - const TCGArgConstraint *arg_ct) +static int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) { int ct = arg_ct->ct; if (ct & TCG_CT_CONST) { @@ -425,7 +434,7 @@ static int tcg_target_const_match (tcg_target_long val, #define STHX XO31(407) #define STWX XO31(151) -#define SPR(a,b) ((((a)<<5)|(b))<<11) +#define SPR(a, b) ((((a)<<5)|(b))<<11) #define LR SPR(8, 0) #define CTR SPR(9, 0) @@ -439,7 +448,7 @@ static int tcg_target_const_match (tcg_target_long val, #define SRADI XO31(413<<1) #define TW XO31( 4) -#define TRAP (TW | TO (31)) +#define TRAP (TW | TO(31)) #define RT(r) ((r)<<21) #define RS(r) ((r)<<21) @@ -467,9 +476,9 @@ static int tcg_target_const_match (tcg_target_long val, #define BB(n, c) (((c)+((n)*4))<<11) #define BC_(n, c) (((c)+((n)*4))<<6) -#define BO_COND_TRUE BO (12) -#define BO_COND_FALSE BO ( 4) -#define BO_ALWAYS BO (20) +#define BO_COND_TRUE BO(12) +#define BO_COND_FALSE BO( 4) +#define BO_ALWAYS BO(20) enum { CR_LT, @@ -479,16 +488,16 @@ enum { }; static const uint32_t tcg_to_bc[] = { - [TCG_COND_EQ] = BC | BI (7, CR_EQ) | BO_COND_TRUE, - [TCG_COND_NE] = BC | BI (7, CR_EQ) | BO_COND_FALSE, - [TCG_COND_LT] = BC | BI (7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GE] = BC | BI (7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LE] = BC | BI (7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GT] = BC | BI (7, CR_GT) | BO_COND_TRUE, - [TCG_COND_LTU] = BC | BI (7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GEU] = BC | BI (7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LEU] = BC | BI (7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, + [TCG_COND_EQ] = BC | BI(7, CR_EQ) | BO_COND_TRUE, + [TCG_COND_NE] = BC | BI(7, CR_EQ) | BO_COND_FALSE, + [TCG_COND_LT] = BC | BI(7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GE] = BC | BI(7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LE] = BC | BI(7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GT] = BC | BI(7, CR_GT) | BO_COND_TRUE, + [TCG_COND_LTU] = BC | BI(7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GEU] = BC | BI(7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LEU] = BC | BI(7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GTU] = BC | BI(7, CR_GT) | BO_COND_TRUE, }; /* The low bit here is set if the RA and RB fields must be inverted. */ @@ -508,15 +517,17 @@ static const uint32_t tcg_to_isel[] = { static inline void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - tcg_out32 (s, OR | SAB (arg, ret, arg)); + if (ret != arg) { + tcg_out32(s, OR | SAB(arg, ret, arg)); + } } static inline void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, int sh, int mb) { - sh = SH (sh & 0x1f) | (((sh >> 5) & 1) << 1); - mb = MB64 ((mb >> 5) | ((mb << 1) & 0x3f)); - tcg_out32 (s, op | RA (ra) | RS (rs) | sh | mb); + sh = SH(sh & 0x1f) | (((sh >> 5) & 1) << 1); + mb = MB64((mb >> 5) | ((mb << 1) & 0x3f)); + tcg_out32(s, op | RA(ra) | RS(rs) | sh | mb); } static inline void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, @@ -636,8 +647,8 @@ static void tcg_out_andi32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) } else if (mask_operand(c, &mb, &me)) { tcg_out_rlw(s, RLWINM, dst, src, 0, mb, me); } else { - tcg_out_movi(s, TCG_TYPE_I32, 0, c); - tcg_out32(s, AND | SAB(src, dst, 0)); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R0, c); + tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); } } @@ -658,8 +669,8 @@ static void tcg_out_andi64(TCGContext *s, TCGReg dst, TCGReg src, uint64_t c) tcg_out_rld(s, RLDICL, dst, src, 0, mb); } } else { - tcg_out_movi(s, TCG_TYPE_I64, 0, c); - tcg_out32(s, AND | SAB(src, dst, 0)); + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, c); + tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); } } @@ -686,123 +697,115 @@ static void tcg_out_xori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) tcg_out_zori32(s, dst, src, c, XORI, XORIS); } -static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target) +static void tcg_out_b(TCGContext *s, int mask, tcg_target_long target) { tcg_target_long disp; - disp = target - (tcg_target_long) s->code_ptr; - if ((disp << 38) >> 38 == disp) - tcg_out32 (s, B | (disp & 0x3fffffc) | mask); - else { - tcg_out_movi (s, TCG_TYPE_I64, 0, (tcg_target_long) target); - tcg_out32 (s, MTSPR | RS (0) | CTR); - tcg_out32 (s, BCCTR | BO_ALWAYS | mask); + disp = target - (tcg_target_long)s->code_ptr; + if (in_range_b(disp)) { + tcg_out32(s, B | (disp & 0x3fffffc) | mask); + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, (tcg_target_long)target); + tcg_out32(s, MTSPR | RS(TCG_REG_R0) | CTR); + tcg_out32(s, BCCTR | BO_ALWAYS | mask); } } -static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg) +static void tcg_out_call(TCGContext *s, tcg_target_long arg, int const_arg) { #ifdef __APPLE__ if (const_arg) { - tcg_out_b (s, LK, arg); - } - else { - tcg_out32 (s, MTSPR | RS (arg) | LR); - tcg_out32 (s, BCLR | BO_ALWAYS | LK); + tcg_out_b(s, LK, arg); + } else { + tcg_out32(s, MTSPR | RS(arg) | LR); + tcg_out32(s, BCLR | BO_ALWAYS | LK); } #else - int reg; + TCGReg reg = arg; + int ofs = 0; if (const_arg) { - reg = 2; - tcg_out_movi (s, TCG_TYPE_I64, reg, arg); + /* Look through the descriptor. If the branch is in range, and we + don't have to spend too much effort on building the toc. */ + intptr_t tgt = ((intptr_t *)arg)[0]; + intptr_t toc = ((intptr_t *)arg)[1]; + intptr_t diff = tgt - (intptr_t)s->code_ptr; + + if (in_range_b(diff) && toc == (uint32_t)toc) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R2, toc); + tcg_out_b(s, LK, tgt); + return; + } + + /* Fold the low bits of the constant into the addresses below. */ + ofs = (int16_t)arg; + if (ofs + 8 < 0x8000) { + arg -= ofs; + } else { + ofs = 0; + } + reg = TCG_REG_R2; + tcg_out_movi(s, TCG_TYPE_I64, reg, arg); } - else reg = arg; - tcg_out32 (s, LD | RT (0) | RA (reg)); - tcg_out32 (s, MTSPR | RA (0) | CTR); - tcg_out32 (s, LD | RT (11) | RA (reg) | 16); - tcg_out32 (s, LD | RT (2) | RA (reg) | 8); - tcg_out32 (s, BCCTR | BO_ALWAYS | LK); + tcg_out32(s, LD | TAI(TCG_REG_R0, reg, ofs)); + tcg_out32(s, MTSPR | RA(TCG_REG_R0) | CTR); + tcg_out32(s, LD | TAI(TCG_REG_R2, reg, ofs + 8)); + tcg_out32(s, BCCTR | BO_ALWAYS | LK); #endif } -static void tcg_out_ldst(TCGContext *s, TCGReg ret, TCGReg addr, - int offset, int op1, int op2) +static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, + TCGReg base, tcg_target_long offset) { - if (offset == (int16_t) offset) { - tcg_out32(s, op1 | TAI(ret, addr, offset)); - } else { - tcg_out_movi(s, TCG_TYPE_I64, 0, offset); - tcg_out32(s, op2 | TAB(ret, addr, 0)); - } -} + tcg_target_long orig = offset, l0, l1, extra = 0, align = 0; + TCGReg rs = TCG_REG_R2; -static void tcg_out_ldsta(TCGContext *s, TCGReg ret, TCGReg addr, - int offset, int op1, int op2) -{ - if (offset == (int16_t) (offset & ~3)) { - tcg_out32(s, op1 | TAI(ret, addr, offset)); - } else { - tcg_out_movi(s, TCG_TYPE_I64, 0, offset); - tcg_out32(s, op2 | TAB(ret, addr, 0)); - } -} - -#if defined (CONFIG_SOFTMMU) -/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, - int mmu_idx) */ -static const void * const qemu_ld_helpers[4] = { - helper_ldb_mmu, - helper_ldw_mmu, - helper_ldl_mmu, - helper_ldq_mmu, -}; + assert(rt != TCG_REG_R2 && base != TCG_REG_R2); -/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, - uintxx_t val, int mmu_idx) */ -static const void * const qemu_st_helpers[4] = { - helper_stb_mmu, - helper_stw_mmu, - helper_stl_mmu, - helper_stq_mmu, -}; + switch (opi) { + case LD: case LWA: + align = 3; + /* FALLTHRU */ + default: + if (rt != TCG_REG_R0) { + rs = rt; + } + break; + case STD: + align = 3; + break; + case STB: case STH: case STW: + break; + } -static void tcg_out_tlb_read(TCGContext *s, TCGReg r0, TCGReg r1, TCGReg r2, - TCGReg addr_reg, int s_bits, int offset) -{ -#if TARGET_LONG_BITS == 32 - tcg_out_ext32u(s, addr_reg, addr_reg); - - tcg_out_rlw(s, RLWINM, r0, addr_reg, - 32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), - 32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS), - 31 - CPU_TLB_ENTRY_BITS); - tcg_out32(s, ADD | TAB(r0, r0, TCG_AREG0)); - tcg_out32(s, LWZU | TAI(r1, r0, offset)); - tcg_out_rlw(s, RLWINM, r2, addr_reg, 0, - (32 - s_bits) & 31, 31 - TARGET_PAGE_BITS); -#else - tcg_out_rld (s, RLDICL, r0, addr_reg, - 64 - TARGET_PAGE_BITS, - 64 - CPU_TLB_BITS); - tcg_out_shli64(s, r0, r0, CPU_TLB_ENTRY_BITS); + /* For unaligned, or very large offsets, use the indexed form. */ + if (offset & align || offset != (int32_t)offset) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R2, orig); + tcg_out32(s, opx | TAB(rt, base, TCG_REG_R2)); + return; + } - tcg_out32(s, ADD | TAB(r0, r0, TCG_AREG0)); - tcg_out32(s, LD_ADDR | TAI(r1, r0, offset)); + l0 = (int16_t)offset; + offset = (offset - l0) >> 16; + l1 = (int16_t)offset; - if (!s_bits) { - tcg_out_rld (s, RLDICR, r2, addr_reg, 0, 63 - TARGET_PAGE_BITS); + if (l1 < 0 && orig >= 0) { + extra = 0x4000; + l1 = (int16_t)(offset - 0x4000); } - else { - tcg_out_rld (s, RLDICL, r2, addr_reg, - 64 - TARGET_PAGE_BITS, - TARGET_PAGE_BITS - s_bits); - tcg_out_rld (s, RLDICL, r2, r2, TARGET_PAGE_BITS, 0); + if (l1) { + tcg_out32(s, ADDIS | TAI(rs, base, l1)); + base = rs; + } + if (extra) { + tcg_out32(s, ADDIS | TAI(rs, base, extra)); + base = rs; + } + if (opi != ADDI || base != rt || l0 != 0) { + tcg_out32(s, opi | TAI(rt, base, l0)); } -#endif } -#endif static const uint32_t qemu_ldx_opc[8] = { #ifdef TARGET_WORDS_BIGENDIAN @@ -826,105 +829,251 @@ static const uint32_t qemu_exts_opc[4] = { EXTSB, EXTSH, EXTSW, 0 }; -static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) +#if defined (CONFIG_SOFTMMU) +/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, + * int mmu_idx, uintptr_t ra) + */ +static const void * const qemu_ld_helpers[4] = { + helper_ret_ldub_mmu, + helper_ret_lduw_mmu, + helper_ret_ldul_mmu, + helper_ret_ldq_mmu, +}; + +/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, + * uintxx_t val, int mmu_idx, uintptr_t ra) + */ +static const void * const qemu_st_helpers[4] = { + helper_ret_stb_mmu, + helper_ret_stw_mmu, + helper_ret_stl_mmu, + helper_ret_stq_mmu, +}; + +/* Perform the TLB load and compare. Places the result of the comparison + in CR7, loads the addend of the TLB into R3, and returns the register + containing the guest address (zero-extended into R4). Clobbers R0 and R2. */ + +static TCGReg tcg_out_tlb_read(TCGContext *s, int s_bits, TCGReg addr_reg, + int mem_index, bool is_read) { - TCGReg addr_reg, data_reg, r0, r1, rbase; - uint32_t insn, s_bits; -#ifdef CONFIG_SOFTMMU - TCGReg r2, ir; - int mem_index; - void *label1_ptr, *label2_ptr; -#endif + int cmp_off + = (is_read + ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); + int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); + TCGReg base = TCG_AREG0; + + /* Extract the page index, shifted into place for tlb index. */ + if (TARGET_LONG_BITS == 32) { + /* Zero-extend the address into a place helpful for further use. */ + tcg_out_ext32u(s, TCG_REG_R4, addr_reg); + addr_reg = TCG_REG_R4; + } else { + tcg_out_rld(s, RLDICL, TCG_REG_R3, addr_reg, + 64 - TARGET_PAGE_BITS, 64 - CPU_TLB_BITS); + } - data_reg = *args++; - addr_reg = *args++; - s_bits = opc & 3; + /* Compensate for very large offsets. */ + if (add_off >= 0x8000) { + /* Most target env are smaller than 32k; none are larger than 64k. + Simplify the logic here merely to offset by 0x7ff0, giving us a + range just shy of 64k. Check this assumption. */ + QEMU_BUILD_BUG_ON(offsetof(CPUArchState, + tlb_table[NB_MMU_MODES - 1][1]) + > 0x7ff0 + 0x7fff); + tcg_out32(s, ADDI | TAI(TCG_REG_R2, base, 0x7ff0)); + base = TCG_REG_R2; + cmp_off -= 0x7ff0; + add_off -= 0x7ff0; + } -#ifdef CONFIG_SOFTMMU - mem_index = *args; + /* Extraction and shifting, part 2. */ + if (TARGET_LONG_BITS == 32) { + tcg_out_rlw(s, RLWINM, TCG_REG_R3, addr_reg, + 32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), + 32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS), + 31 - CPU_TLB_ENTRY_BITS); + } else { + tcg_out_shli64(s, TCG_REG_R3, TCG_REG_R3, CPU_TLB_ENTRY_BITS); + } - r0 = 3; - r1 = 4; - r2 = 0; - rbase = 0; + tcg_out32(s, ADD | TAB(TCG_REG_R3, TCG_REG_R3, base)); - tcg_out_tlb_read (s, r0, r1, r2, addr_reg, s_bits, - offsetof (CPUArchState, tlb_table[mem_index][0].addr_read)); + /* Load the tlb comparator. */ + tcg_out32(s, LD_ADDR | TAI(TCG_REG_R2, TCG_REG_R3, cmp_off)); - tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1) | CMP_L); + /* Load the TLB addend for use on the fast path. Do this asap + to minimize any load use delay. */ + tcg_out32(s, LD | TAI(TCG_REG_R3, TCG_REG_R3, add_off)); - label1_ptr = s->code_ptr; -#ifdef FAST_PATH - tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE); -#endif + /* Clear the non-page, non-alignment bits from the address. */ + if (TARGET_LONG_BITS == 32) { + tcg_out_rlw(s, RLWINM, TCG_REG_R0, addr_reg, 0, + (32 - s_bits) & 31, 31 - TARGET_PAGE_BITS); + } else if (!s_bits) { + tcg_out_rld(s, RLDICR, TCG_REG_R0, addr_reg, 0, 63 - TARGET_PAGE_BITS); + } else { + tcg_out_rld(s, RLDICL, TCG_REG_R0, addr_reg, + 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - s_bits); + tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); + } + + tcg_out32(s, CMP | BF(7) | RA(TCG_REG_R0) | RB(TCG_REG_R2) | CMP_L); + + return addr_reg; +} + +/* Record the context of a call to the out of line helper code for the slow + path for a load or store, so that we can later generate the correct + helper code. */ +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, int opc, + int data_reg, int addr_reg, int mem_index, + uint8_t *raddr, uint8_t *label_ptr) +{ + int idx; + TCGLabelQemuLdst *label; - /* slow path */ - ir = 3; - tcg_out_mov (s, TCG_TYPE_I64, ir++, TCG_AREG0); - tcg_out_mov (s, TCG_TYPE_I64, ir++, addr_reg); - tcg_out_movi (s, TCG_TYPE_I64, ir++, mem_index); + if (s->nb_qemu_ldst_labels >= TCG_MAX_QEMU_LDST) { + tcg_abort(); + } + + idx = s->nb_qemu_ldst_labels++; + label = (TCGLabelQemuLdst *)&s->qemu_ldst_labels[idx]; + label->is_ld = is_ld; + label->opc = opc; + label->datalo_reg = data_reg; + label->addrlo_reg = addr_reg; + label->mem_index = mem_index; + label->raddr = raddr; + label->label_ptr[0] = label_ptr; +} + +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + int opc = lb->opc; + int s_bits = opc & 3; + + reloc_pc14(lb->label_ptr[0], (uintptr_t)s->code_ptr); + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_AREG0); - tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1); + /* If the address needed to be zero-extended, we'll have already + placed it in R4. The only remaining case is 64-bit guest. */ + tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R4, lb->addrlo_reg); + + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R5, lb->mem_index); + tcg_out32(s, MFSPR | RT(TCG_REG_R6) | LR); + + tcg_out_call(s, (tcg_target_long)qemu_ld_helpers[s_bits], 1); if (opc & 4) { - insn = qemu_exts_opc[s_bits]; - tcg_out32(s, insn | RA(data_reg) | RS(3)); - } else if (data_reg != 3) { - tcg_out_mov(s, TCG_TYPE_I64, data_reg, 3); + uint32_t insn = qemu_exts_opc[s_bits]; + tcg_out32(s, insn | RA(lb->datalo_reg) | RS(TCG_REG_R3)); + } else { + tcg_out_mov(s, TCG_TYPE_I64, lb->datalo_reg, TCG_REG_R3); } - label2_ptr = s->code_ptr; - tcg_out32 (s, B); - /* label1: fast path */ -#ifdef FAST_PATH - reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr); + tcg_out_b(s, 0, (uintptr_t)lb->raddr); +} + +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + int opc = lb->opc; + + reloc_pc14(lb->label_ptr[0], (uintptr_t)s->code_ptr); + + tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R3, TCG_AREG0); + + /* If the address needed to be zero-extended, we'll have already + placed it in R4. The only remaining case is 64-bit guest. */ + tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R4, lb->addrlo_reg); + + tcg_out_rld(s, RLDICL, TCG_REG_R5, lb->datalo_reg, + 0, 64 - (1 << (3 + opc))); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R6, lb->mem_index); + tcg_out32(s, MFSPR | RT(TCG_REG_R7) | LR); + + tcg_out_call(s, (tcg_target_long)qemu_st_helpers[opc], 1); + + tcg_out_b(s, 0, (uintptr_t)lb->raddr); +} + +void tcg_out_tb_finalize(TCGContext *s) +{ + int i, n = s->nb_qemu_ldst_labels; + + /* qemu_ld/st slow paths */ + for (i = 0; i < n; i++) { + TCGLabelQemuLdst *label = &s->qemu_ldst_labels[i]; + if (label->is_ld) { + tcg_out_qemu_ld_slow_path(s, label); + } else { + tcg_out_qemu_st_slow_path(s, label); + } + } +} +#endif /* SOFTMMU */ + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + TCGReg addr_reg, data_reg, rbase; + uint32_t insn, s_bits; +#ifdef CONFIG_SOFTMMU + int mem_index; + void *label_ptr; #endif - /* r0 now contains &env->tlb_table[mem_index][index].addr_read */ - tcg_out32(s, LD | TAI(r0, r0, - offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_read))); - /* r0 = env->tlb_table[mem_index][index].addend */ - tcg_out32(s, ADD | TAB(r0, r0, addr_reg)); - /* r0 = env->tlb_table[mem_index][index].addend + addr */ + data_reg = *args++; + addr_reg = *args++; + s_bits = opc & 3; + +#ifdef CONFIG_SOFTMMU + mem_index = *args; + addr_reg = tcg_out_tlb_read(s, s_bits, addr_reg, mem_index, true); + + /* Load a pointer into the current opcode w/conditional branch-link. */ + label_ptr = s->code_ptr; + tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + + rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ -#if TARGET_LONG_BITS == 32 - tcg_out_ext32u(s, addr_reg, addr_reg); -#endif - r0 = addr_reg; - r1 = 3; rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; + if (TARGET_LONG_BITS == 32) { + tcg_out_ext32u(s, TCG_REG_R2, addr_reg); + addr_reg = TCG_REG_R2; + } #endif insn = qemu_ldx_opc[opc]; if (!HAVE_ISA_2_06 && insn == LDBRX) { - tcg_out32(s, ADDI | TAI(r1, r0, 4)); - tcg_out32(s, LWBRX | TAB(data_reg, rbase, r0)); - tcg_out32(s, LWBRX | TAB( r1, rbase, r1)); - tcg_out_rld(s, RLDIMI, data_reg, r1, 32, 0); + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addr_reg, 4)); + tcg_out32(s, LWBRX | TAB(data_reg, rbase, addr_reg)); + tcg_out32(s, LWBRX | TAB(TCG_REG_R0, rbase, TCG_REG_R0)); + tcg_out_rld(s, RLDIMI, data_reg, TCG_REG_R0, 32, 0); } else if (insn) { - tcg_out32(s, insn | TAB(data_reg, rbase, r0)); + tcg_out32(s, insn | TAB(data_reg, rbase, addr_reg)); } else { insn = qemu_ldx_opc[s_bits]; - tcg_out32(s, insn | TAB(data_reg, rbase, r0)); + tcg_out32(s, insn | TAB(data_reg, rbase, addr_reg)); insn = qemu_exts_opc[s_bits]; - tcg_out32 (s, insn | RA(data_reg) | RS(data_reg)); + tcg_out32(s, insn | RA(data_reg) | RS(data_reg)); } #ifdef CONFIG_SOFTMMU - reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr); + add_qemu_ldst_label(s, true, opc, data_reg, addr_reg, mem_index, + s->code_ptr, label_ptr); #endif } -static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) { - TCGReg addr_reg, r0, r1, rbase, data_reg; + TCGReg addr_reg, rbase, data_reg; uint32_t insn; #ifdef CONFIG_SOFTMMU - TCGReg r2, ir; int mem_index; - void *label1_ptr, *label2_ptr; + void *label_ptr; #endif data_reg = *args++; @@ -933,158 +1082,122 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) #ifdef CONFIG_SOFTMMU mem_index = *args; - r0 = 3; - r1 = 4; - r2 = 0; - rbase = 0; - - tcg_out_tlb_read (s, r0, r1, r2, addr_reg, opc, - offsetof (CPUArchState, tlb_table[mem_index][0].addr_write)); - - tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1) | CMP_L); - - label1_ptr = s->code_ptr; -#ifdef FAST_PATH - tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE); -#endif - - /* slow path */ - ir = 3; - tcg_out_mov (s, TCG_TYPE_I64, ir++, TCG_AREG0); - tcg_out_mov (s, TCG_TYPE_I64, ir++, addr_reg); - tcg_out_rld (s, RLDICL, ir++, data_reg, 0, 64 - (1 << (3 + opc))); - tcg_out_movi (s, TCG_TYPE_I64, ir++, mem_index); + addr_reg = tcg_out_tlb_read(s, opc, addr_reg, mem_index, false); - tcg_out_call (s, (tcg_target_long) qemu_st_helpers[opc], 1); - - label2_ptr = s->code_ptr; - tcg_out32 (s, B); - - /* label1: fast path */ -#ifdef FAST_PATH - reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr); -#endif - - tcg_out32 (s, (LD - | RT (r0) - | RA (r0) - | (offsetof (CPUTLBEntry, addend) - - offsetof (CPUTLBEntry, addr_write)) - )); - /* r0 = env->tlb_table[mem_index][index].addend */ - tcg_out32(s, ADD | TAB(r0, r0, addr_reg)); - /* r0 = env->tlb_table[mem_index][index].addend + addr */ + /* Load a pointer into the current opcode w/conditional branch-link. */ + label_ptr = s->code_ptr; + tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ -#if TARGET_LONG_BITS == 32 - tcg_out_ext32u(s, addr_reg, addr_reg); -#endif - r1 = 3; - r0 = addr_reg; rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; + if (TARGET_LONG_BITS == 32) { + tcg_out_ext32u(s, TCG_REG_R2, addr_reg); + addr_reg = TCG_REG_R2; + } #endif insn = qemu_stx_opc[opc]; if (!HAVE_ISA_2_06 && insn == STDBRX) { - tcg_out32(s, STWBRX | SAB(data_reg, rbase, r0)); - tcg_out32(s, ADDI | TAI(r1, r0, 4)); - tcg_out_shri64(s, 0, data_reg, 32); - tcg_out32(s, STWBRX | SAB(0, rbase, r1)); + tcg_out32(s, STWBRX | SAB(data_reg, rbase, addr_reg)); + tcg_out32(s, ADDI | TAI(TCG_REG_R2, addr_reg, 4)); + tcg_out_shri64(s, TCG_REG_R0, data_reg, 32); + tcg_out32(s, STWBRX | SAB(TCG_REG_R0, rbase, TCG_REG_R2)); } else { - tcg_out32(s, insn | SAB(data_reg, rbase, r0)); + tcg_out32(s, insn | SAB(data_reg, rbase, addr_reg)); } #ifdef CONFIG_SOFTMMU - reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr); + add_qemu_ldst_label(s, false, opc, data_reg, addr_reg, mem_index, + s->code_ptr, label_ptr); #endif } -static void tcg_target_qemu_prologue (TCGContext *s) +#define FRAME_SIZE ((int) \ + ((8 /* back chain */ \ + + 8 /* CR */ \ + + 8 /* LR */ \ + + 8 /* compiler doubleword */ \ + + 8 /* link editor doubleword */ \ + + 8 /* TOC save area */ \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_NLONGS * sizeof(long) \ + + ARRAY_SIZE(tcg_target_callee_save_regs) * 8 \ + + 15) & ~15)) + +#define REG_SAVE_BOT (FRAME_SIZE - ARRAY_SIZE(tcg_target_callee_save_regs) * 8) + +static void tcg_target_qemu_prologue(TCGContext *s) { - int i, frame_size; -#ifndef __APPLE__ - uint64_t addr; -#endif + int i; - frame_size = 0 - + 8 /* back chain */ - + 8 /* CR */ - + 8 /* LR */ - + 8 /* compiler doubleword */ - + 8 /* link editor doubleword */ - + 8 /* TOC save area */ - + TCG_STATIC_CALL_ARGS_SIZE - + ARRAY_SIZE (tcg_target_callee_save_regs) * 8 - + CPU_TEMP_BUF_NLONGS * sizeof(long) - ; - frame_size = (frame_size + 15) & ~15; - - tcg_set_frame (s, TCG_REG_CALL_STACK, frame_size - - CPU_TEMP_BUF_NLONGS * sizeof (long), - CPU_TEMP_BUF_NLONGS * sizeof (long)); + tcg_set_frame(s, TCG_REG_CALL_STACK, + REG_SAVE_BOT - CPU_TEMP_BUF_NLONGS * sizeof(long), + CPU_TEMP_BUF_NLONGS * sizeof(long)); #ifndef __APPLE__ /* First emit adhoc function descriptor */ - addr = (uint64_t) s->code_ptr + 24; - tcg_out32 (s, addr >> 32); tcg_out32 (s, addr); /* entry point */ + tcg_out64(s, (uint64_t)s->code_ptr + 24); /* entry point */ s->code_ptr += 16; /* skip TOC and environment pointer */ #endif /* Prologue */ - tcg_out32 (s, MFSPR | RT (0) | LR); - tcg_out32 (s, STDU | RS (1) | RA (1) | (-frame_size & 0xffff)); - for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i) - tcg_out32 (s, (STD - | RS (tcg_target_callee_save_regs[i]) - | RA (1) - | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE) - ) - ); - tcg_out32 (s, STD | RS (0) | RA (1) | (frame_size + 16)); + tcg_out32(s, MFSPR | RT(TCG_REG_R0) | LR); + tcg_out32(s, STDU | SAI(TCG_REG_R1, TCG_REG_R1, -FRAME_SIZE)); + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { + tcg_out32(s, STD | SAI(tcg_target_callee_save_regs[i], 1, + REG_SAVE_BOT + i * 8)); + } + tcg_out32(s, STD | SAI(TCG_REG_R0, TCG_REG_R1, FRAME_SIZE + 16)); #ifdef CONFIG_USE_GUEST_BASE if (GUEST_BASE) { - tcg_out_movi (s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE); - tcg_regset_set_reg (s->reserved_regs, TCG_GUEST_BASE_REG); + tcg_out_movi(s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); } #endif - tcg_out_mov (s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tcg_out32 (s, MTSPR | RS (tcg_target_call_iarg_regs[1]) | CTR); - tcg_out32 (s, BCCTR | BO_ALWAYS); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out32(s, MTSPR | RS(tcg_target_call_iarg_regs[1]) | CTR); + tcg_out32(s, BCCTR | BO_ALWAYS); /* Epilogue */ tb_ret_addr = s->code_ptr; - for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i) - tcg_out32 (s, (LD - | RT (tcg_target_callee_save_regs[i]) - | RA (1) - | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE) - ) - ); - tcg_out32(s, LD | TAI(0, 1, frame_size + 16)); - tcg_out32(s, MTSPR | RS(0) | LR); - tcg_out32(s, ADDI | TAI(1, 1, frame_size)); + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { + tcg_out32(s, LD | TAI(tcg_target_callee_save_regs[i], TCG_REG_R1, + REG_SAVE_BOT + i * 8)); + } + tcg_out32(s, LD | TAI(TCG_REG_R0, TCG_REG_R1, FRAME_SIZE + 16)); + tcg_out32(s, MTSPR | RS(TCG_REG_R0) | LR); + tcg_out32(s, ADDI | TAI(TCG_REG_R1, TCG_REG_R1, FRAME_SIZE)); tcg_out32(s, BCLR | BO_ALWAYS); } -static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, - intptr_t arg2) +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, + TCGReg arg1, intptr_t arg2) { - if (type == TCG_TYPE_I32) - tcg_out_ldst (s, ret, arg1, arg2, LWZ, LWZX); - else - tcg_out_ldsta (s, ret, arg1, arg2, LD, LDX); + int opi, opx; + + if (type == TCG_TYPE_I32) { + opi = LWZ, opx = LWZX; + } else { + opi = LD, opx = LDX; + } + tcg_out_mem_long(s, opi, opx, ret, arg1, arg2); } -static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, - intptr_t arg2) +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) { - if (type == TCG_TYPE_I32) - tcg_out_ldst (s, arg, arg1, arg2, STW, STWX); - else - tcg_out_ldsta (s, arg, arg1, arg2, STD, STDX); + int opi, opx; + + if (type == TCG_TYPE_I32) { + opi = STW, opx = STWX; + } else { + opi = STD, opx = STDX; + } + tcg_out_mem_long(s, opi, opx, arg, arg1, arg2); } static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, @@ -1106,8 +1219,7 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, op = CMPI; imm = 1; break; - } - else if ((uint16_t) arg2 == arg2) { + } else if ((uint16_t) arg2 == arg2) { op = CMPLI; imm = 1; break; @@ -1148,7 +1260,7 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, break; default: - tcg_abort (); + tcg_abort(); } op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); @@ -1156,8 +1268,8 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, tcg_out32(s, op | RA(arg1) | (arg2 & 0xffff)); } else { if (const_arg2) { - tcg_out_movi(s, type, 0, arg2); - arg2 = 0; + tcg_out_movi(s, type, TCG_REG_R0, arg2); + arg2 = TCG_REG_R0; } tcg_out32(s, op | RA(arg1) | RB(arg2)); } @@ -1178,8 +1290,8 @@ static void tcg_out_setcond_ne0(TCGContext *s, TCGReg dst, TCGReg src) tcg_out32(s, ADDIC | TAI(dst, src, -1)); tcg_out32(s, SUBFE | TAB(dst, dst, src)); } else { - tcg_out32(s, ADDIC | TAI(0, src, -1)); - tcg_out32(s, SUBFE | TAB(dst, 0, src)); + tcg_out32(s, ADDIC | TAI(TCG_REG_R0, src, -1)); + tcg_out32(s, SUBFE | TAB(dst, TCG_REG_R0, src)); } } @@ -1292,13 +1404,13 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, case TCG_COND_GE: case TCG_COND_GEU: sh = 31; - crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_LT) | BB (7, CR_LT); + crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_LT) | BB(7, CR_LT); goto crtest; case TCG_COND_LE: case TCG_COND_LEU: sh = 31; - crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT); + crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_GT) | BB(7, CR_GT); crtest: tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); if (crop) { @@ -1309,22 +1421,19 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, break; default: - tcg_abort (); + tcg_abort(); } } -static void tcg_out_bc (TCGContext *s, int bc, int label_index) +static void tcg_out_bc(TCGContext *s, int bc, int label_index) { TCGLabel *l = &s->labels[label_index]; - if (l->has_value) - tcg_out32 (s, bc | reloc_pc14_val (s->code_ptr, l->u.value)); - else { - uint16_t val = *(uint16_t *) &s->code_ptr[2]; - - /* Thanks to Andrzej Zaborowski */ - tcg_out32 (s, bc | (val & 0xfffc)); - tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL14, label_index, 0); + if (l->has_value) { + tcg_out32(s, bc | reloc_pc14_val(s->code_ptr, l->u.value)); + } else { + tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, label_index, 0); + tcg_out_bc_noaddr(s, bc); } } @@ -1360,7 +1469,7 @@ static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, } /* V1 == 0 is handled by isel; V2 == 0 must be handled by hand. */ if (v2 == 0) { - tcg_out_movi(s, type, 0, 0); + tcg_out_movi(s, type, TCG_REG_R0, 0); } tcg_out32(s, isel | TAB(dest, v1, v2)); } else { @@ -1384,37 +1493,36 @@ static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, } } -void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) +void ppc_tb_set_jmp_target(unsigned long jmp_addr, unsigned long addr) { TCGContext s; unsigned long patch_size; s.code_ptr = (uint8_t *) jmp_addr; - tcg_out_b (&s, 0, addr); + tcg_out_b(&s, 0, addr); patch_size = s.code_ptr - (uint8_t *) jmp_addr; - flush_icache_range (jmp_addr, jmp_addr + patch_size); + flush_icache_range(jmp_addr, jmp_addr + patch_size); } -static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, - const int *const_args) +static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) { TCGArg a0, a1, a2; int c; switch (opc) { case INDEX_op_exit_tb: - tcg_out_movi (s, TCG_TYPE_I64, TCG_REG_R3, args[0]); - tcg_out_b (s, 0, (tcg_target_long) tb_ret_addr); + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R3, args[0]); + tcg_out_b(s, 0, (tcg_target_long)tb_ret_addr); break; case INDEX_op_goto_tb: if (s->tb_jmp_offset) { - /* direct jump method */ - + /* Direct jump method. */ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; s->code_ptr += 28; - } - else { - tcg_abort (); + } else { + /* Indirect jump method. */ + tcg_abort(); } s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; break; @@ -1423,83 +1531,70 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, TCGLabel *l = &s->labels[args[0]]; if (l->has_value) { - tcg_out_b (s, 0, l->u.value); - } - else { - uint32_t val = *(uint32_t *) s->code_ptr; - - /* Thanks to Andrzej Zaborowski */ - tcg_out32 (s, B | (val & 0x3fffffc)); - tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL24, args[0], 0); + tcg_out_b(s, 0, l->u.value); + } else { + tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, args[0], 0); + tcg_out_b_noaddr(s, B); } } break; case INDEX_op_call: - tcg_out_call (s, args[0], const_args[0]); + tcg_out_call(s, args[0], const_args[0]); break; case INDEX_op_movi_i32: - tcg_out_movi (s, TCG_TYPE_I32, args[0], args[1]); + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); break; case INDEX_op_movi_i64: - tcg_out_movi (s, TCG_TYPE_I64, args[0], args[1]); + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); break; case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: - tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX); + tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); break; case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: - tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX); - tcg_out32 (s, EXTSB | RS (args[0]) | RA (args[0])); + tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); + tcg_out32(s, EXTSB | RS(args[0]) | RA(args[0])); break; case INDEX_op_ld16u_i32: case INDEX_op_ld16u_i64: - tcg_out_ldst (s, args[0], args[1], args[2], LHZ, LHZX); + tcg_out_mem_long(s, LHZ, LHZX, args[0], args[1], args[2]); break; case INDEX_op_ld16s_i32: case INDEX_op_ld16s_i64: - tcg_out_ldst (s, args[0], args[1], args[2], LHA, LHAX); + tcg_out_mem_long(s, LHA, LHAX, args[0], args[1], args[2]); break; case INDEX_op_ld_i32: case INDEX_op_ld32u_i64: - tcg_out_ldst (s, args[0], args[1], args[2], LWZ, LWZX); + tcg_out_mem_long(s, LWZ, LWZX, args[0], args[1], args[2]); break; case INDEX_op_ld32s_i64: - tcg_out_ldsta (s, args[0], args[1], args[2], LWA, LWAX); + tcg_out_mem_long(s, LWA, LWAX, args[0], args[1], args[2]); break; case INDEX_op_ld_i64: - tcg_out_ldsta (s, args[0], args[1], args[2], LD, LDX); + tcg_out_mem_long(s, LD, LDX, args[0], args[1], args[2]); break; case INDEX_op_st8_i32: case INDEX_op_st8_i64: - tcg_out_ldst (s, args[0], args[1], args[2], STB, STBX); + tcg_out_mem_long(s, STB, STBX, args[0], args[1], args[2]); break; case INDEX_op_st16_i32: case INDEX_op_st16_i64: - tcg_out_ldst (s, args[0], args[1], args[2], STH, STHX); + tcg_out_mem_long(s, STH, STHX, args[0], args[1], args[2]); break; case INDEX_op_st_i32: case INDEX_op_st32_i64: - tcg_out_ldst (s, args[0], args[1], args[2], STW, STWX); + tcg_out_mem_long(s, STW, STWX, args[0], args[1], args[2]); break; case INDEX_op_st_i64: - tcg_out_ldsta (s, args[0], args[1], args[2], STD, STDX); + tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; case INDEX_op_add_i32: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - int32_t l, h; do_addi_32: - l = (int16_t)a2; - h = a2 - l; - if (h) { - tcg_out32(s, ADDIS | TAI(a0, a1, h >> 16)); - a1 = a0; - } - if (l || a0 != a1) { - tcg_out32(s, ADDI | TAI(a0, a1, l)); - } + tcg_out_mem_long(s, ADDI, ADD, a0, a1, (int32_t)a2); } else { tcg_out32(s, ADD | TAB(a0, a1, a2)); } @@ -1607,32 +1702,33 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, break; case INDEX_op_div_i32: - tcg_out32 (s, DIVW | TAB (args[0], args[1], args[2])); + tcg_out32(s, DIVW | TAB(args[0], args[1], args[2])); break; case INDEX_op_divu_i32: - tcg_out32 (s, DIVWU | TAB (args[0], args[1], args[2])); + tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); break; case INDEX_op_shl_i32: if (const_args[2]) { tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31 - args[2]); } else { - tcg_out32 (s, SLW | SAB (args[1], args[0], args[2])); + tcg_out32(s, SLW | SAB(args[1], args[0], args[2])); } break; case INDEX_op_shr_i32: if (const_args[2]) { tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], args[2], 31); } else { - tcg_out32 (s, SRW | SAB (args[1], args[0], args[2])); + tcg_out32(s, SRW | SAB(args[1], args[0], args[2])); } break; case INDEX_op_sar_i32: - if (const_args[2]) - tcg_out32 (s, SRAWI | RS (args[1]) | RA (args[0]) | SH (args[2])); - else - tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2])); + if (const_args[2]) { + tcg_out32(s, SRAWI | RS(args[1]) | RA(args[0]) | SH(args[2])); + } else { + tcg_out32(s, SRAW | SAB(args[1], args[0], args[2])); + } break; case INDEX_op_rotl_i32: if (const_args[2]) { @@ -1646,8 +1742,8 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, if (const_args[2]) { tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31); } else { - tcg_out32(s, SUBFIC | TAI(0, args[2], 32)); - tcg_out32(s, RLWNM | SAB(args[1], args[0], 0) + tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 32)); + tcg_out32(s, RLWNM | SAB(args[1], args[0], TCG_REG_R0) | MB(0) | ME(31)); } break; @@ -1664,43 +1760,19 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, case INDEX_op_neg_i32: case INDEX_op_neg_i64: - tcg_out32 (s, NEG | RT (args[0]) | RA (args[1])); + tcg_out32(s, NEG | RT(args[0]) | RA(args[1])); break; case INDEX_op_not_i32: case INDEX_op_not_i64: - tcg_out32 (s, NOR | SAB (args[1], args[0], args[1])); + tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); break; case INDEX_op_add_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - int32_t l0, h1, h2; do_addi_64: - /* We can always split any 32-bit signed constant into 3 pieces. - Note the positive 0x80000000 coming from the sub_i64 path, - handled with the same code we need for eg 0x7fff8000. */ - assert(a2 == (int32_t)a2 || a2 == 0x80000000); - l0 = (int16_t)a2; - h1 = a2 - l0; - h2 = 0; - if (h1 < 0 && (int64_t)a2 > 0) { - h2 = 0x40000000; - h1 = a2 - h2 - l0; - } - assert((TCGArg)h2 + h1 + l0 == a2); - - if (h2) { - tcg_out32(s, ADDIS | TAI(a0, a1, h2 >> 16)); - a1 = a0; - } - if (h1) { - tcg_out32(s, ADDIS | TAI(a0, a1, h1 >> 16)); - a1 = a0; - } - if (l0 || a0 != a1) { - tcg_out32(s, ADDI | TAI(a0, a1, l0)); - } + tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); } else { tcg_out32(s, ADD | TAB(a0, a1, a2)); } @@ -1722,24 +1794,26 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, break; case INDEX_op_shl_i64: - if (const_args[2]) + if (const_args[2]) { tcg_out_shli64(s, args[0], args[1], args[2]); - else - tcg_out32 (s, SLD | SAB (args[1], args[0], args[2])); + } else { + tcg_out32(s, SLD | SAB(args[1], args[0], args[2])); + } break; case INDEX_op_shr_i64: - if (const_args[2]) + if (const_args[2]) { tcg_out_shri64(s, args[0], args[1], args[2]); - else - tcg_out32 (s, SRD | SAB (args[1], args[0], args[2])); + } else { + tcg_out32(s, SRD | SAB(args[1], args[0], args[2])); + } break; case INDEX_op_sar_i64: if (const_args[2]) { - int sh = SH (args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1); - tcg_out32 (s, SRADI | RA (args[0]) | RS (args[1]) | sh); + int sh = SH(args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1); + tcg_out32(s, SRADI | RA(args[0]) | RS(args[1]) | sh); + } else { + tcg_out32(s, SRAD | SAB(args[1], args[0], args[2])); } - else - tcg_out32 (s, SRAD | SAB (args[1], args[0], args[2])); break; case INDEX_op_rotl_i64: if (const_args[2]) { @@ -1752,8 +1826,8 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, if (const_args[2]) { tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 0); } else { - tcg_out32(s, SUBFIC | TAI(0, args[2], 64)); - tcg_out32(s, RLDCL | SAB(args[1], args[0], 0) | MB64(0)); + tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 64)); + tcg_out32(s, RLDCL | SAB(args[1], args[0], TCG_REG_R0) | MB64(0)); } break; @@ -1766,45 +1840,45 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, } break; case INDEX_op_div_i64: - tcg_out32 (s, DIVD | TAB (args[0], args[1], args[2])); + tcg_out32(s, DIVD | TAB(args[0], args[1], args[2])); break; case INDEX_op_divu_i64: - tcg_out32 (s, DIVDU | TAB (args[0], args[1], args[2])); + tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); break; case INDEX_op_qemu_ld8u: - tcg_out_qemu_ld (s, args, 0); + tcg_out_qemu_ld(s, args, 0); break; case INDEX_op_qemu_ld8s: - tcg_out_qemu_ld (s, args, 0 | 4); + tcg_out_qemu_ld(s, args, 0 | 4); break; case INDEX_op_qemu_ld16u: - tcg_out_qemu_ld (s, args, 1); + tcg_out_qemu_ld(s, args, 1); break; case INDEX_op_qemu_ld16s: - tcg_out_qemu_ld (s, args, 1 | 4); + tcg_out_qemu_ld(s, args, 1 | 4); break; case INDEX_op_qemu_ld32: case INDEX_op_qemu_ld32u: - tcg_out_qemu_ld (s, args, 2); + tcg_out_qemu_ld(s, args, 2); break; case INDEX_op_qemu_ld32s: - tcg_out_qemu_ld (s, args, 2 | 4); + tcg_out_qemu_ld(s, args, 2 | 4); break; case INDEX_op_qemu_ld64: - tcg_out_qemu_ld (s, args, 3); + tcg_out_qemu_ld(s, args, 3); break; case INDEX_op_qemu_st8: - tcg_out_qemu_st (s, args, 0); + tcg_out_qemu_st(s, args, 0); break; case INDEX_op_qemu_st16: - tcg_out_qemu_st (s, args, 1); + tcg_out_qemu_st(s, args, 1); break; case INDEX_op_qemu_st32: - tcg_out_qemu_st (s, args, 2); + tcg_out_qemu_st(s, args, 2); break; case INDEX_op_qemu_st64: - tcg_out_qemu_st (s, args, 3); + tcg_out_qemu_st(s, args, 3); break; case INDEX_op_ext8s_i32: @@ -1819,16 +1893,16 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, c = EXTSW; goto gen_ext; gen_ext: - tcg_out32 (s, c | RS (args[1]) | RA (args[0])); + tcg_out32(s, c | RS(args[1]) | RA(args[0])); break; case INDEX_op_setcond_i32: - tcg_out_setcond (s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], - const_args[2]); + tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], + const_args[2]); break; case INDEX_op_setcond_i64: - tcg_out_setcond (s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], - const_args[2]); + tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], + const_args[2]); break; case INDEX_op_bswap16_i32: @@ -1870,9 +1944,9 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, break; case INDEX_op_bswap64_i64: - a0 = args[0], a1 = args[1], a2 = 0; + a0 = args[0], a1 = args[1], a2 = TCG_REG_R0; if (a0 == a1) { - a0 = 0; + a0 = TCG_REG_R0; a2 = a1; } @@ -1980,8 +2054,8 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, break; default: - tcg_dump_ops (s); - tcg_abort (); + tcg_dump_ops(s); + tcg_abort(); } } @@ -2109,7 +2183,7 @@ static const TCGTargetOpDef ppc_op_defs[] = { { -1 }, }; -static void tcg_target_init (TCGContext *s) +static void tcg_target_init(TCGContext *s) { #ifdef CONFIG_GETAUXVAL unsigned long hwcap = getauxval(AT_HWCAP); @@ -2118,13 +2192,11 @@ static void tcg_target_init (TCGContext *s) } #endif - tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); - tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); - tcg_regset_set32 (tcg_target_call_clobber_regs, 0, + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); + tcg_regset_set32(tcg_target_call_clobber_regs, 0, (1 << TCG_REG_R0) | -#ifdef __APPLE__ (1 << TCG_REG_R2) | -#endif (1 << TCG_REG_R3) | (1 << TCG_REG_R4) | (1 << TCG_REG_R5) | @@ -2134,16 +2206,65 @@ static void tcg_target_init (TCGContext *s) (1 << TCG_REG_R9) | (1 << TCG_REG_R10) | (1 << TCG_REG_R11) | - (1 << TCG_REG_R12) - ); + (1 << TCG_REG_R12)); - tcg_regset_clear (s->reserved_regs); - tcg_regset_set_reg (s->reserved_regs, TCG_REG_R0); - tcg_regset_set_reg (s->reserved_regs, TCG_REG_R1); -#ifndef __APPLE__ - tcg_regset_set_reg (s->reserved_regs, TCG_REG_R2); + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* tcg temp */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* mem temp */ +#ifdef __APPLE__ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R11); /* ??? */ #endif - tcg_regset_set_reg (s->reserved_regs, TCG_REG_R13); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ + + tcg_add_target_add_op_defs(ppc_op_defs); +} + +typedef struct { + DebugFrameCIE cie; + DebugFrameFDEHeader fde; + uint8_t fde_def_cfa[4]; + uint8_t fde_reg_ofs[ARRAY_SIZE(tcg_target_callee_save_regs) * 2 + 3]; +} DebugFrame; + +/* We're expecting a 2 byte uleb128 encoded value. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); + +#define ELF_HOST_MACHINE EM_PPC64 + +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = 0x78, /* sleb128 -8 */ + .cie.return_column = 65, + + /* Total FDE size does not include the "len" member. */ + .fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, fde.cie_offset), + + .fde_def_cfa = { + 12, 1, /* DW_CFA_def_cfa r1, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde_reg_ofs = { + 0x11, 65, 0x7e, /* DW_CFA_offset_extended_sf, lr, 16 */ + } +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + uint8_t *p = &debug_frame.fde_reg_ofs[3]; + int i; + + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i, p += 2) { + p[0] = 0x80 + tcg_target_callee_save_regs[i]; + p[1] = (FRAME_SIZE - (REG_SAVE_BOT + i * 8)) / 8; + } + + debug_frame.fde.func_start = (tcg_target_long) buf; + debug_frame.fde.func_len = buf_size; - tcg_add_target_add_op_defs (ppc_op_defs); + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); } diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h index 2edf858733..1ff2922bbe 100644 --- a/tcg/sparc/tcg-target.h +++ b/tcg/sparc/tcg-target.h @@ -153,7 +153,7 @@ typedef enum { static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { uintptr_t p; - for (p = start & -8; p < (stop + 7) & -8; p += 8) { + for (p = start & -8; p < ((stop + 7) & -8); p += 8) { __asm__ __volatile__("flush\t%0" : : "r" (p)); } } @@ -1085,7 +1085,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tmp8 = helper_ldb_mmu(env, taddr, tci_read_i(&tb_ptr)); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); tmp8 = *(uint8_t *)(host_addr + GUEST_BASE); #endif tci_write_reg8(t0, tmp8); @@ -1097,7 +1096,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tmp8 = helper_ldb_mmu(env, taddr, tci_read_i(&tb_ptr)); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); tmp8 = *(uint8_t *)(host_addr + GUEST_BASE); #endif tci_write_reg8s(t0, tmp8); @@ -1109,7 +1107,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tmp16 = helper_ldw_mmu(env, taddr, tci_read_i(&tb_ptr)); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); tmp16 = tswap16(*(uint16_t *)(host_addr + GUEST_BASE)); #endif tci_write_reg16(t0, tmp16); @@ -1121,7 +1118,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tmp16 = helper_ldw_mmu(env, taddr, tci_read_i(&tb_ptr)); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); tmp16 = tswap16(*(uint16_t *)(host_addr + GUEST_BASE)); #endif tci_write_reg16s(t0, tmp16); @@ -1134,7 +1130,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tmp32 = helper_ldl_mmu(env, taddr, tci_read_i(&tb_ptr)); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); tmp32 = tswap32(*(uint32_t *)(host_addr + GUEST_BASE)); #endif tci_write_reg32(t0, tmp32); @@ -1146,7 +1141,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tmp32 = helper_ldl_mmu(env, taddr, tci_read_i(&tb_ptr)); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); tmp32 = tswap32(*(uint32_t *)(host_addr + GUEST_BASE)); #endif tci_write_reg32s(t0, tmp32); @@ -1159,7 +1153,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tmp32 = helper_ldl_mmu(env, taddr, tci_read_i(&tb_ptr)); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); tmp32 = tswap32(*(uint32_t *)(host_addr + GUEST_BASE)); #endif tci_write_reg32(t0, tmp32); @@ -1174,7 +1167,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tmp64 = helper_ldq_mmu(env, taddr, tci_read_i(&tb_ptr)); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); tmp64 = tswap64(*(uint64_t *)(host_addr + GUEST_BASE)); #endif tci_write_reg(t0, tmp64); @@ -1190,7 +1182,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) helper_stb_mmu(env, taddr, t0, t2); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); *(uint8_t *)(host_addr + GUEST_BASE) = t0; #endif break; @@ -1202,7 +1193,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) helper_stw_mmu(env, taddr, t0, t2); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); *(uint16_t *)(host_addr + GUEST_BASE) = tswap16(t0); #endif break; @@ -1214,7 +1204,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) helper_stl_mmu(env, taddr, t0, t2); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); *(uint32_t *)(host_addr + GUEST_BASE) = tswap32(t0); #endif break; @@ -1226,7 +1215,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) helper_stq_mmu(env, taddr, tmp64, t2); #else host_addr = (tcg_target_ulong)taddr; - assert(taddr == host_addr); *(uint64_t *)(host_addr + GUEST_BASE) = tswap64(tmp64); #endif break; diff --git a/tests/.gitignore b/tests/.gitignore index d11cc22373..ae5280ef68 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,6 +5,7 @@ check-qjson check-qlist check-qstring test-aio +test-throttle test-cutils test-hbitmap test-iov diff --git a/tests/Makefile b/tests/Makefile index c13fefc314..994fef1839 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -174,6 +174,7 @@ tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) +tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o # QTest rules @@ -252,7 +253,7 @@ check-report.html: check-report.xml # Other tests .PHONY: check-tests/qemu-iotests-quick.sh -check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) +check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) tests/qemu-iotests/socket_scm_helper$(EXESUF) $< .PHONY: check-tests/test-qapi.py diff --git a/tests/qemu-iotests/001 b/tests/qemu-iotests/001 index bd88dde879..4e1646941b 100755 --- a/tests/qemu-iotests/001 +++ b/tests/qemu-iotests/001 @@ -48,15 +48,15 @@ _make_test_img $size echo echo "== reading whole image ==" -$QEMU_IO -c "read 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== rewriting whole image ==" -$QEMU_IO -c "write -P 0xa 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== verify pattern ==" -$QEMU_IO -c "read -P 0xa 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io # success, all done diff --git a/tests/qemu-iotests/002 b/tests/qemu-iotests/002 index 51d0a8f4ad..6a865aac73 100755 --- a/tests/qemu-iotests/002 +++ b/tests/qemu-iotests/002 @@ -48,36 +48,36 @@ _make_test_img $size echo echo "== reading whole image ==" -$QEMU_IO -c "read -p 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -p 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== rewriting whole image ==" -$QEMU_IO -c "write -pP 0xa 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -pP 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== verify pattern ==" -$QEMU_IO -c "read -pP 0xa 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -pP 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "unaligned pwrite" -$QEMU_IO -c 'write -pP 0xab 66 42' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'write -pP 0xac 512 288' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'write -pP 0xad 800 224' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'write -pP 0xae 66000 128k' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'write -pP 0xaf 256k 42' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xab 66 42' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xac 512 288' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xad 800 224' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xae 66000 128k' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xaf 256k 42' "$TEST_IMG" | _filter_qemu_io echo echo "verify pattern" -$QEMU_IO -c 'read -pP 0xa 0 66' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xab 66 42' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xa 108 404' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xac 512 288' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xad 800 224' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xa 1k 64976' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xae 66000 128k' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xa 197072 65072' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xaf 256k 42' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'read -pP 0xa 262186 470' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 0 66' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xab 66 42' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 108 404' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xac 512 288' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xad 800 224' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 1k 64976' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xae 66000 128k' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 197072 65072' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xaf 256k 42' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 262186 470' "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/003 b/tests/qemu-iotests/003 index ee25fb8078..98638d4ce7 100755 --- a/tests/qemu-iotests/003 +++ b/tests/qemu-iotests/003 @@ -50,27 +50,27 @@ _make_test_img $size echo echo "== reading whole image ==" -$QEMU_IO -c "readv 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "readv 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== rewriting whole image ==" -$QEMU_IO -c "writev -P 0xa 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "writev -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== verify pattern ==" -$QEMU_IO -c "readv -P 0xa 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "readv -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== vectored write ==" $QEMU_IO -c "writev -P 0xb $offset $chunksize $chunksize \ $chunksize $chunksize $chunksize $chunksize $chunksize" \ - $TEST_IMG | _filter_qemu_io + "$TEST_IMG" | _filter_qemu_io echo echo "== verify pattern ==" $QEMU_IO -c "readv -P 0xb $offset $chunksize $chunksize \ $chunksize $chunksize $chunksize $chunksize $chunksize" \ - $TEST_IMG | _filter_qemu_io + "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/004 b/tests/qemu-iotests/004 index c76451c5a7..651072ef89 100755 --- a/tests/qemu-iotests/004 +++ b/tests/qemu-iotests/004 @@ -51,51 +51,51 @@ _make_test_img $size echo echo "write before image boundary" -$QEMU_IO -c "write $pre_offset 1M" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write $pre_offset 1M" "$TEST_IMG" | _filter_qemu_io echo echo "write into image boundary" -$QEMU_IO -c "write $pre_offset 4M" $TEST_IMG +$QEMU_IO -c "write $pre_offset 4M" "$TEST_IMG" echo echo "write at image boundary" -$QEMU_IO -c "write $size 4096" $TEST_IMG +$QEMU_IO -c "write $size 4096" "$TEST_IMG" echo echo "write past image boundary" -$QEMU_IO -c "write $past_offset 4096" $TEST_IMG +$QEMU_IO -c "write $past_offset 4096" "$TEST_IMG" echo echo "pwrite past image boundary" -$QEMU_IO -c "write -p $past_offset 4096" $TEST_IMG +$QEMU_IO -c "write -p $past_offset 4096" "$TEST_IMG" echo echo "writev past image boundary" -$QEMU_IO -c "writev $past_offset 4096" $TEST_IMG +$QEMU_IO -c "writev $past_offset 4096" "$TEST_IMG" echo echo "read before image boundary" -$QEMU_IO -c "read $pre_offset 1M" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read $pre_offset 1M" "$TEST_IMG" | _filter_qemu_io echo echo "read into image boundary" -$QEMU_IO -c "read $pre_offset 4M" $TEST_IMG +$QEMU_IO -c "read $pre_offset 4M" "$TEST_IMG" echo echo "read at image boundary" -$QEMU_IO -c "read $size 4096" $TEST_IMG +$QEMU_IO -c "read $size 4096" "$TEST_IMG" echo echo "read past image boundary" -$QEMU_IO -c "read $past_offset 4096" $TEST_IMG +$QEMU_IO -c "read $past_offset 4096" "$TEST_IMG" echo echo "pread past image boundary" -$QEMU_IO -c "read -p $past_offset 4096" $TEST_IMG +$QEMU_IO -c "read -p $past_offset 4096" "$TEST_IMG" echo echo "readv past image boundary" -$QEMU_IO -c "readv $past_offset 4096" $TEST_IMG +$QEMU_IO -c "readv $past_offset 4096" "$TEST_IMG" # success, all done diff --git a/tests/qemu-iotests/005 b/tests/qemu-iotests/005 index b7970e3b58..9abcb84e4b 100755 --- a/tests/qemu-iotests/005 +++ b/tests/qemu-iotests/005 @@ -61,11 +61,11 @@ _make_test_img 5000G echo echo "small read" -$QEMU_IO -c "read 1024 4096" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read 1024 4096" "$TEST_IMG" | _filter_qemu_io echo echo "small write" -$QEMU_IO -c "write 8192 4096" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write 8192 4096" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/007 b/tests/qemu-iotests/007 index c454f2c8ec..fe1a743806 100755 --- a/tests/qemu-iotests/007 +++ b/tests/qemu-iotests/007 @@ -30,7 +30,7 @@ status=1 # failure is the default! _cleanup() { -# _cleanup_test_img + _cleanup_test_img true } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -50,7 +50,7 @@ _make_test_img 1M for i in `seq 1 10`; do echo "savevm $i" - $QEMU -nographic -hda $TEST_IMG -serial none -monitor stdio >/dev/null 2>&1 <<EOF + $QEMU -nographic -hda "$TEST_IMG" -serial none -monitor stdio >/dev/null 2>&1 <<EOF savevm test-$i quit EOF diff --git a/tests/qemu-iotests/008 b/tests/qemu-iotests/008 index 2c53bac925..2d28efd428 100755 --- a/tests/qemu-iotests/008 +++ b/tests/qemu-iotests/008 @@ -48,15 +48,15 @@ _make_test_img $size echo echo "== reading whole image ==" -$QEMU_IO -c "aio_read 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "aio_read 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== rewriting whole image ==" -$QEMU_IO -c "aio_write -P 0xa 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "aio_write -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== verify pattern ==" -$QEMU_IO -c "aio_read -P 0xa 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "aio_read -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io # success, all done diff --git a/tests/qemu-iotests/009 b/tests/qemu-iotests/009 index 25368c819b..57a43f5a16 100755 --- a/tests/qemu-iotests/009 +++ b/tests/qemu-iotests/009 @@ -57,7 +57,7 @@ $QEMU_IO \ -c "write 4k 4k" \ -c "write 9M 4k" \ -c "read -P 65 -s 4k -l 4k 2044k 8k" \ -$TEST_IMG | _filter_qemu_io +"$TEST_IMG" | _filter_qemu_io echo echo "checking image for errors" diff --git a/tests/qemu-iotests/010 b/tests/qemu-iotests/010 index 7b5792934a..896a0058ff 100755 --- a/tests/qemu-iotests/010 +++ b/tests/qemu-iotests/010 @@ -59,7 +59,7 @@ $QEMU_IO \ -c "write -P 165 2044k 4k" \ -c "write -P 99 8M 4k" \ -c "read -P 165 2044k 8k" \ -$TEST_IMG | _filter_qemu_io +"$TEST_IMG" | _filter_qemu_io echo echo "checking image for errors" diff --git a/tests/qemu-iotests/011 b/tests/qemu-iotests/011 index b03df6887d..1c5158af43 100755 --- a/tests/qemu-iotests/011 +++ b/tests/qemu-iotests/011 @@ -60,7 +60,7 @@ for i in `seq 1 10`; do # Note that we filter away the actual offset. That's because qemu # may re-order the two aio requests. We only want to make sure the # filesystem isn't corrupted afterwards anyway. - $QEMU_IO -c "aio_write $off1 1M" -c "aio_write $off2 1M" $TEST_IMG | \ + $QEMU_IO -c "aio_write $off1 1M" -c "aio_write $off2 1M" "$TEST_IMG" | \ _filter_qemu_io | \ sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g' done diff --git a/tests/qemu-iotests/012 b/tests/qemu-iotests/012 index 4052956cd9..7c5b6892d3 100755 --- a/tests/qemu-iotests/012 +++ b/tests/qemu-iotests/012 @@ -50,11 +50,11 @@ _make_test_img $size echo echo "== mark image read-only" -chmod a-w $TEST_IMG +chmod a-w "$TEST_IMG" echo echo "== read from read-only image" -$QEMU_IO -r -c "read 0 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -r -c "read 0 512" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/013 b/tests/qemu-iotests/013 index ce40d5c5b6..389f4b8156 100755 --- a/tests/qemu-iotests/013 +++ b/tests/qemu-iotests/013 @@ -65,8 +65,8 @@ done echo "Compressing image" echo -mv $TEST_IMG $TEST_IMG.orig -$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c $TEST_IMG.orig $TEST_IMG +mv "$TEST_IMG" "$TEST_IMG.orig" +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c "$TEST_IMG.orig" "$TEST_IMG" echo "Testing compressed image" echo diff --git a/tests/qemu-iotests/014 b/tests/qemu-iotests/014 index a6d0aea7c0..0edeb4b6f5 100755 --- a/tests/qemu-iotests/014 +++ b/tests/qemu-iotests/014 @@ -61,7 +61,7 @@ done # With snapshots for i in `seq 1 3`; do - $QEMU_IMG snapshot -c test$i $TEST_IMG + $QEMU_IMG snapshot -c test$i "$TEST_IMG" for offset in $TEST_OFFSETS; do echo With snapshot test$i, offset $offset for op in $TEST_OPS; do diff --git a/tests/qemu-iotests/015 b/tests/qemu-iotests/015 index 44c134f948..099d75723c 100755 --- a/tests/qemu-iotests/015 +++ b/tests/qemu-iotests/015 @@ -61,19 +61,19 @@ _make_test_img $size # Create two snapshots which fill the image with two different patterns echo "creating first snapshot" -$QEMU_IO -c "aio_write -P 123 0 $size" $TEST_IMG | _filter_qemu_io -$QEMU_IMG snapshot -c snap1 $TEST_IMG +$QEMU_IO -c "aio_write -P 123 0 $size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -c snap1 "$TEST_IMG" echo "creating second snapshot" -$QEMU_IO -c "aio_write -P 165 0 $size" $TEST_IMG | _filter_qemu_io -$QEMU_IMG snapshot -c snap2 $TEST_IMG +$QEMU_IO -c "aio_write -P 165 0 $size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -c snap2 "$TEST_IMG" # Now check the pattern echo "checking first snapshot" -$QEMU_IMG snapshot -a snap1 $TEST_IMG -$QEMU_IO -c "aio_read -P 123 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IMG snapshot -a snap1 "$TEST_IMG" +$QEMU_IO -c "aio_read -P 123 0 $size" "$TEST_IMG" | _filter_qemu_io echo "checking second snapshot" -$QEMU_IMG snapshot -a snap2 $TEST_IMG -$QEMU_IO -c "aio_read -P 165 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IMG snapshot -a snap2 "$TEST_IMG" +$QEMU_IO -c "aio_read -P 165 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "checking image for errors" diff --git a/tests/qemu-iotests/016 b/tests/qemu-iotests/016 index a1467b8a3c..b87a32bc27 100755 --- a/tests/qemu-iotests/016 +++ b/tests/qemu-iotests/016 @@ -48,21 +48,21 @@ _make_test_img $size echo echo "== reading at EOF ==" -$QEMU_IO -g -c "read -P 0 $size 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -g -c "read -P 0 $size 512" "$TEST_IMG" | _filter_qemu_io echo echo "== reading far past EOF ==" -$QEMU_IO -g -c "read -P 0 256M 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -g -c "read -P 0 256M 512" "$TEST_IMG" | _filter_qemu_io echo echo "== writing at EOF ==" -$QEMU_IO -g -c "write -P 66 $size 512" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 66 $size 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -g -c "write -P 66 $size 512" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 66 $size 512" "$TEST_IMG" | _filter_qemu_io echo echo "== writing far past EOF ==" -$QEMU_IO -g -c "write -P 66 256M 512" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 66 256M 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -g -c "write -P 66 256M 512" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 66 256M 512" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018 index 453ce61e75..15fcfe5670 100755 --- a/tests/qemu-iotests/018 +++ b/tests/qemu-iotests/018 @@ -66,7 +66,7 @@ echo "Creating test image with backing file" echo TEST_IMG=$TEST_IMG_SAVE -_make_test_img -b $TEST_IMG.base 6G +_make_test_img -b "$TEST_IMG.base" 6G echo "Filling test image" echo @@ -80,8 +80,8 @@ for offset in $TEST_OFFSETS; do done _check_test_img -mv $TEST_IMG $TEST_IMG.orig -$QEMU_IMG convert -O $IMGFMT $TEST_IMG.orig $TEST_IMG +mv "$TEST_IMG" "$TEST_IMG.orig" +$QEMU_IMG convert -O $IMGFMT "$TEST_IMG.orig" "$TEST_IMG" echo "Reading" echo diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019 index 8872b30350..cd3582cf6f 100755 --- a/tests/qemu-iotests/019 +++ b/tests/qemu-iotests/019 @@ -33,8 +33,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.base - rm -f $TEST_IMG.orig + rm -f "$TEST_IMG.base" + rm -f "$TEST_IMG.orig" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -68,8 +68,8 @@ _check_test_img echo "Creating test image with backing file" echo -mv $TEST_IMG $TEST_IMG.base -_make_test_img -b $TEST_IMG.base 6G +mv "$TEST_IMG" "$TEST_IMG.base" +_make_test_img -b "$TEST_IMG.base" 6G echo "Filling test image" echo @@ -83,7 +83,7 @@ for offset in $TEST_OFFSETS; do done _check_test_img -mv $TEST_IMG $TEST_IMG.orig +mv "$TEST_IMG" "$TEST_IMG.orig" @@ -95,7 +95,7 @@ for backing_option in "-B $TEST_IMG.base" "-o backing_file=$TEST_IMG.base"; do echo echo Testing conversion with $backing_option | _filter_testdir | _filter_imgfmt echo - $QEMU_IMG convert -O $IMGFMT $backing_option $TEST_IMG.orig $TEST_IMG + $QEMU_IMG convert -O $IMGFMT $backing_option "$TEST_IMG.orig" "$TEST_IMG" echo "Checking if backing clusters are allocated when they shouldn't" echo diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 index 2fb0ff87f2..b3c86d844e 100755 --- a/tests/qemu-iotests/020 +++ b/tests/qemu-iotests/020 @@ -31,8 +31,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.base - rm -f $TEST_IMG.orig + rm -f "$TEST_IMG.base" + rm -f "$TEST_IMG.orig" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -65,8 +65,8 @@ _check_test_img echo "Creating test image with backing file" echo -mv $TEST_IMG $TEST_IMG.base -_make_test_img -b $TEST_IMG.base 6G +mv "$TEST_IMG" "$TEST_IMG.base" +_make_test_img -b "$TEST_IMG.base" 6G echo "Filling test image" echo @@ -80,8 +80,8 @@ for offset in $TEST_OFFSETS; do done _check_test_img -$QEMU_IMG commit $TEST_IMG -mv $TEST_IMG.base $TEST_IMG +$QEMU_IMG commit "$TEST_IMG" +mv "$TEST_IMG.base" "$TEST_IMG" echo "Reading from the backing file" echo diff --git a/tests/qemu-iotests/021 b/tests/qemu-iotests/021 index 6da79ebbbe..1c69024ccb 100755 --- a/tests/qemu-iotests/021 +++ b/tests/qemu-iotests/021 @@ -53,7 +53,7 @@ for pattern in $INVALID_PATTERNS; do for op in $TEST_OPS; do echo echo "== testing $op -P $pattern ==" - $QEMU_IO -c "$op -P $pattern 0 4096" $TEST_IMG | _filter_qemu_io + $QEMU_IO -c "$op -P $pattern 0 4096" "$TEST_IMG" | _filter_qemu_io done done diff --git a/tests/qemu-iotests/023 b/tests/qemu-iotests/023 index 4f31b56589..090ed23dec 100755 --- a/tests/qemu-iotests/023 +++ b/tests/qemu-iotests/023 @@ -71,8 +71,8 @@ for CLUSTER_SIZE in $CLUSTER_SIZES; do echo "Compressing image" echo - mv $TEST_IMG $TEST_IMG.orig - $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c $TEST_IMG.orig $TEST_IMG + mv "$TEST_IMG" "$TEST_IMG.orig" + $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c "$TEST_IMG.orig" "$TEST_IMG" echo "Testing compressed image" echo diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index 554b74b2d3..be974f02a2 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -31,8 +31,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_DIR/t.$IMGFMT.base_old - rm -f $TEST_DIR/t.$IMGFMT.base_new + rm -f "$TEST_DIR/t.$IMGFMT.base_old" + rm -f "$TEST_DIR/t.$IMGFMT.base_new" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -62,19 +62,19 @@ echo _make_test_img 1G io_pattern writev 0 $CLUSTER_SIZE $((2 * CLUSTER_SIZE)) 8 0x11 -mv $TEST_IMG $TEST_IMG.base_old +mv "$TEST_IMG" "$TEST_IMG.base_old" echo "Creating new backing file" echo _make_test_img 1G io_pattern writev 0 $((2 * CLUSTER_SIZE)) $((4 * CLUSTER_SIZE)) 4 0x22 -mv $TEST_IMG $TEST_IMG.base_new +mv "$TEST_IMG" "$TEST_IMG.base_new" echo "Creating COW image" echo -_make_test_img -b $TEST_IMG.base_old 1G +_make_test_img -b "$TEST_IMG.base_old" 1G io_pattern writev 0 $((4 * CLUSTER_SIZE)) 0 1 0x33 io_pattern writev $((8 * CLUSTER_SIZE)) $((4 * CLUSTER_SIZE)) 0 1 0x33 @@ -100,7 +100,7 @@ io_pattern readv $((15 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00 echo echo Rebase and test again echo -$QEMU_IMG rebase -b $TEST_IMG.base_new $TEST_IMG +$QEMU_IMG rebase -b "$TEST_IMG.base_new" "$TEST_IMG" io_pattern readv $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x33 io_pattern readv $((1 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x33 io_pattern readv $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x33 diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025 index 7062aa6f36..a7241ccc95 100755 --- a/tests/qemu-iotests/025 +++ b/tests/qemu-iotests/025 @@ -56,7 +56,7 @@ _check_test_img echo echo "=== Resizing image" -$QEMU_IO $TEST_IMG <<EOF +$QEMU_IO "$TEST_IMG" <<EOF length truncate $big_size length @@ -65,7 +65,7 @@ _check_test_img echo echo "=== Verifying image size after reopen" -$QEMU_IO -c "length" $TEST_IMG +$QEMU_IO -c "length" "$TEST_IMG" echo echo "=== Verifying resized image" diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index 107a3ff2f6..ebe29d0168 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -31,7 +31,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm $TEST_DIR/blkdebug.conf + rm "$TEST_DIR/blkdebug.conf" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -75,7 +75,7 @@ for imm in off; do for once in on off; do for vmstate in "" "-b"; do -cat > $TEST_DIR/blkdebug.conf <<EOF +cat > "$TEST_DIR/blkdebug.conf" <<EOF [inject-error] event = "$event" errno = "$errno" @@ -90,16 +90,16 @@ echo "Event: $event; errno: $errno; imm: $imm; once: $once; write $vmstate" # We want to catch a simple L2 update, not the allocation of the first L2 table if [ "$event" == "l2_update" ]; then - $QEMU_IO -c "write $vmstate 0 512" $TEST_IMG > /dev/null 2>&1 + $QEMU_IO -c "write $vmstate 0 512" "$TEST_IMG" > /dev/null 2>&1 fi -$QEMU_IO -c "write $vmstate 0 128k " $BLKDBG_TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io # l2_load is not called on allocation, so issue a second write # Reads are another path to trigger l2_load, so do a read, too if [ "$event" == "l2_load" ]; then - $QEMU_IO -c "write $vmstate 0 128k " $BLKDBG_TEST_IMG | _filter_qemu_io - $QEMU_IO -c "read $vmstate 0 128k " $BLKDBG_TEST_IMG | _filter_qemu_io + $QEMU_IO -c "write $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io + $QEMU_IO -c "read $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io fi _check_test_img 2>&1 | grep -v "refcount=1 reference=0" @@ -133,7 +133,7 @@ for imm in off; do for once in on off; do for vmstate in "" "-b"; do -cat > $TEST_DIR/blkdebug.conf <<EOF +cat > "$TEST_DIR/blkdebug.conf" <<EOF [inject-error] event = "$event" errno = "$errno" @@ -145,7 +145,7 @@ _make_test_img 1G echo echo "Event: $event; errno: $errno; imm: $imm; once: $once; write $vmstate" -$QEMU_IO -c "write $vmstate 0 64M" $BLKDBG_TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write $vmstate 0 64M" "$BLKDBG_TEST_IMG" | _filter_qemu_io _check_test_img 2>&1 | grep -v "refcount=1 reference=0" @@ -172,7 +172,7 @@ for errno in 5 28; do for imm in off; do for once in on off; do -cat > $TEST_DIR/blkdebug.conf <<EOF +cat > "$TEST_DIR/blkdebug.conf" <<EOF [inject-error] event = "$event" errno = "$errno" @@ -184,7 +184,7 @@ _make_test_img 1G echo echo "Event: $event; errno: $errno; imm: $imm; once: $once" -$QEMU_IO -c "write -b 0 64k" $BLKDBG_TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -b 0 64k" "$BLKDBG_TEST_IMG" | _filter_qemu_io _check_test_img 2>&1 | grep -v "refcount=1 reference=0" diff --git a/tests/qemu-iotests/027 b/tests/qemu-iotests/027 index 7d90481832..3fa81b83bb 100755 --- a/tests/qemu-iotests/027 +++ b/tests/qemu-iotests/027 @@ -54,23 +54,23 @@ _make_test_img $size # Otherwise an L2 table could get in the way after the data cluster. echo echo "== writing first cluster to populate metadata ==" -$QEMU_IO -c "write -pP 0xde $cluster_size $cluster_size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -pP 0xde $cluster_size $cluster_size" "$TEST_IMG" | _filter_qemu_io echo echo "== writing at sub-cluster granularity ==" -$QEMU_IO -c "write -pP 0xa $subcluster_offset $subcluster_size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -pP 0xa $subcluster_offset $subcluster_size" "$TEST_IMG" | _filter_qemu_io echo echo "== verify pattern ==" -$QEMU_IO -c "read -pP 0xa $subcluster_offset $subcluster_size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -pP 0xa $subcluster_offset $subcluster_size" "$TEST_IMG" | _filter_qemu_io echo echo "== verify zeroes before sub-cluster pattern ==" -$QEMU_IO -c "read -pP 0 -l $subcluster_offset 0 $subcluster_size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -pP 0 -l $subcluster_offset 0 $subcluster_size" "$TEST_IMG" | _filter_qemu_io echo echo "== verify zeroes after sub-cluster pattern ==" -$QEMU_IO -c "read -pP 0 -l 512 -s $subcluster_size $subcluster_offset $(( subcluster_size + 512 ))" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -pP 0 -l 512 -s $subcluster_size $subcluster_offset $(( subcluster_size + 512 ))" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index b091ba9f07..93a9fa6e83 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -71,8 +71,8 @@ _check_test_img echo "Creating test image with backing file" echo -mv $TEST_IMG $TEST_IMG.base -_make_test_img -b $TEST_IMG.base $image_size +mv "$TEST_IMG" "$TEST_IMG.base" +_make_test_img -b "$TEST_IMG.base" $image_size echo "Filling test image" echo @@ -97,7 +97,7 @@ io_zero readv $(( offset + 32 * 1024 )) 512 1024 32 _check_test_img # Rebase it on top of its base image -$QEMU_IMG rebase -b $TEST_IMG.base $TEST_IMG +$QEMU_IMG rebase -b "$TEST_IMG.base" "$TEST_IMG" _check_test_img diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index 0ad5e45f88..b424726fc4 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -47,16 +47,16 @@ _supported_os Linux CLUSTER_SIZE=65536 _make_test_img 64M -$QEMU_IMG snapshot -c foo $TEST_IMG -$QEMU_IO -c 'write -b 0 4k' $TEST_IMG | _filter_qemu_io -$QEMU_IMG snapshot -a foo $TEST_IMG +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IO -c 'write -b 0 4k' "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -a foo "$TEST_IMG" _check_test_img CLUSTER_SIZE=1024 _make_test_img 16M -$QEMU_IMG snapshot -c foo $TEST_IMG -$QEMU_IO -c 'write -b 0 4M' $TEST_IMG | _filter_qemu_io -$QEMU_IMG snapshot -a foo $TEST_IMG +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IO -c 'write -b 0 4M' "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -a foo "$TEST_IMG" _check_test_img # success, all done diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index 2d5e3b12d1..c9070b0513 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -56,22 +56,22 @@ for IMGOPTS in "compat=0.10" "compat=1.1"; do echo === Create image with unknown header extension === echo _make_test_img 64M - ./qcow2.py $TEST_IMG add-header-ext 0x12345678 "This is a test header extension" - ./qcow2.py $TEST_IMG dump-header + ./qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension" + ./qcow2.py "$TEST_IMG" dump-header _check_test_img echo echo === Rewrite header with no backing file === echo - $QEMU_IMG rebase -u -b "" $TEST_IMG - ./qcow2.py $TEST_IMG dump-header + $QEMU_IMG rebase -u -b "" "$TEST_IMG" + ./qcow2.py "$TEST_IMG" dump-header _check_test_img echo echo === Add a backing file and format === echo - $QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device $TEST_IMG - ./qcow2.py $TEST_IMG dump-header + $QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device "$TEST_IMG" + ./qcow2.py "$TEST_IMG" dump-header done # success, all done diff --git a/tests/qemu-iotests/032 b/tests/qemu-iotests/032 index 7155568a4f..b1ba5c3218 100755 --- a/tests/qemu-iotests/032 +++ b/tests/qemu-iotests/032 @@ -55,12 +55,12 @@ _make_test_img 64M # Allocate every other cluster so that afterwards a big write request will # actually loop a while and issue many I/O requests for the lower layer -for i in $(seq 0 128 4096); do echo "write ${i}k 64k"; done | $QEMU_IO $TEST_IMG | _filter_qemu_io +for i in $(seq 0 128 4096); do echo "write ${i}k 64k"; done | $QEMU_IO "$TEST_IMG" | _filter_qemu_io echo echo === AIO request during close === echo -$QEMU_IO -c "aio_write 0 4M" -c "close" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "aio_write 0 4M" -c "close" "$TEST_IMG" | _filter_qemu_io _check_test_img # success, all done diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 index 9aee0784f6..ea3351c3e7 100755 --- a/tests/qemu-iotests/033 +++ b/tests/qemu-iotests/033 @@ -48,24 +48,24 @@ _make_test_img $size echo echo "== preparing image ==" -$QEMU_IO -c "write -P 0xa 0x200 0x400" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0xa 0x20000 0x600" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -z 0x400 0x20000" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xa 0x200 0x400" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0xa 0x20000 0x600" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io echo echo "== verifying patterns (1) ==" -$QEMU_IO -c "read -P 0xa 0x200 0x200" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x0 0x400 0x20000" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0xa 0x20400 0x200" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 0x200 0x200" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 0x20400 0x200" "$TEST_IMG" | _filter_qemu_io echo echo "== rewriting zeroes ==" -$QEMU_IO -c "write -P 0xb 0x10000 0x10000" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -z 0x10000 0x10000" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xb 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io echo echo "== verifying patterns (2) ==" -$QEMU_IO -c "read -P 0x0 0x400 0x20000" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034 index 8254df82ba..67f1959690 100755 --- a/tests/qemu-iotests/034 +++ b/tests/qemu-iotests/034 @@ -49,63 +49,63 @@ echo echo "== creating backing file for COW tests ==" _make_test_img $size -$QEMU_IO -c "write -P 0x55 0 1M" $TEST_IMG | _filter_qemu_io -mv $TEST_IMG $TEST_IMG.base +$QEMU_IO -c "write -P 0x55 0 1M" "$TEST_IMG" | _filter_qemu_io +mv "$TEST_IMG" "$TEST_IMG.base" -_make_test_img -b $TEST_IMG.base 6G +_make_test_img -b "$TEST_IMG.base" 6G echo echo "== zero write with backing file ==" -$QEMU_IO -c "write -z 64k 192k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -z 513k 13k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -z 64k 192k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 513k 13k" "$TEST_IMG" | _filter_qemu_io _check_test_img echo echo "== verifying patterns (3) ==" -$QEMU_IO -c "read -P 0x55 0 64k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x0 64k 192k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x55 256k 257k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x0 513k 13k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x55 526k 498k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 0 64k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 64k 192k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 256k 257k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 513k 13k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 526k 498k" "$TEST_IMG" | _filter_qemu_io echo echo "== overwriting zero cluster ==" -$QEMU_IO -c "write -P 0xa 60k 8k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0xb 64k 8k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0xc 76k 4k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0xd 252k 8k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0xe 248k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xa 60k 8k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0xb 64k 8k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0xc 76k 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0xd 252k 8k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0xe 248k 8k" "$TEST_IMG" | _filter_qemu_io _check_test_img echo echo "== verifying patterns (4) ==" -$QEMU_IO -c "read -P 0x55 0 60k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0xa 60k 4k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0xb 64k 8k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x0 72k 4k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0xc 76k 4k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x0 80k 168k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0xe 248k 8k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0xd 256k 4k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x55 260k 64k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 0 60k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 60k 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xb 64k 8k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 72k 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xc 76k 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 80k 168k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xe 248k 8k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xd 256k 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 260k 64k" "$TEST_IMG" | _filter_qemu_io echo echo "== re-zeroing overwritten area ==" -$QEMU_IO -c "write -z 64k 192k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -z 64k 192k" "$TEST_IMG" | _filter_qemu_io _check_test_img echo echo "== verifying patterns (5) ==" -$QEMU_IO -c "read -P 0x55 0 60k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0xa 60k 4k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x0 64k 192k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0xd 256k 4k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x55 260k 253k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x0 513k 13k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x55 526k 498k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 0 60k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 60k 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 64k 192k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xd 256k 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 260k 253k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 513k 13k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 526k 498k" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/035 b/tests/qemu-iotests/035 index 9d2d3472e7..ebe9b8c925 100755 --- a/tests/qemu-iotests/035 +++ b/tests/qemu-iotests/035 @@ -59,7 +59,7 @@ function generate_requests() { done } -generate_requests | $QEMU_IO $TEST_IMG | _filter_qemu_io |\ +generate_requests | $QEMU_IO "$TEST_IMG" | _filter_qemu_io |\ sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g' echo diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index 4dbfc5724c..e049a645e7 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -53,15 +53,15 @@ IMGOPTS="compat=1.1" echo === Create image with unknown autoclear feature bit === echo _make_test_img 64M -./qcow2.py $TEST_IMG set-feature-bit autoclear 63 -./qcow2.py $TEST_IMG dump-header +./qcow2.py "$TEST_IMG" set-feature-bit autoclear 63 +./qcow2.py "$TEST_IMG" dump-header echo echo === Repair image === echo _check_test_img -r all -./qcow2.py $TEST_IMG dump-header +./qcow2.py "$TEST_IMG" dump-header # success, all done echo "*** done" diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037 index c11460b92f..743bae33d3 100755 --- a/tests/qemu-iotests/037 +++ b/tests/qemu-iotests/037 @@ -66,50 +66,50 @@ function backing_io() done } -backing_io 0 256 write | $QEMU_IO $TEST_IMG | _filter_qemu_io +backing_io 0 256 write | $QEMU_IO "$TEST_IMG" | _filter_qemu_io -mv $TEST_IMG $TEST_IMG.base +mv "$TEST_IMG" "$TEST_IMG.base" -_make_test_img -b $TEST_IMG.base 6G +_make_test_img -b "$TEST_IMG.base" 6G echo echo "== COW in a single cluster ==" -$QEMU_IO -c "write -P 0x77 0 2k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0x88 6k 2k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0x99 9k 2k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0x77 0 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x88 6k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x99 9k 2k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0x77 0 2k" $TEST_IMG | _filter_qemu_io -backing_io $((2 * 1024)) 8 read | $QEMU_IO $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x88 6k 2k" $TEST_IMG | _filter_qemu_io -backing_io $((8 * 1024)) 2 read | $QEMU_IO $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x99 9k 2k" $TEST_IMG | _filter_qemu_io -backing_io $((11 * 1024)) 2 read | $QEMU_IO $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x77 0 2k" "$TEST_IMG" | _filter_qemu_io +backing_io $((2 * 1024)) 8 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x88 6k 2k" "$TEST_IMG" | _filter_qemu_io +backing_io $((8 * 1024)) 2 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x99 9k 2k" "$TEST_IMG" | _filter_qemu_io +backing_io $((11 * 1024)) 2 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io echo echo "== COW in two-cluster allocations ==" -$QEMU_IO -c "write -P 0x77 16k 6k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0x88 26k 6k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0x99 33k 5k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0x77 16k 6k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x88 26k 6k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x99 33k 5k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0x77 16k 6k" $TEST_IMG | _filter_qemu_io -backing_io $((22 * 1024)) 8 read | $QEMU_IO $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x88 26k 6k" $TEST_IMG | _filter_qemu_io -backing_io $((32 * 1024)) 2 read | $QEMU_IO $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x99 33k 5k" $TEST_IMG | _filter_qemu_io -backing_io $((38 * 1024)) 4 read | $QEMU_IO $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x77 16k 6k" "$TEST_IMG" | _filter_qemu_io +backing_io $((22 * 1024)) 8 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x88 26k 6k" "$TEST_IMG" | _filter_qemu_io +backing_io $((32 * 1024)) 2 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x99 33k 5k" "$TEST_IMG" | _filter_qemu_io +backing_io $((38 * 1024)) 4 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io echo echo "== COW in multi-cluster allocations ==" -$QEMU_IO -c "write -P 0x77 48k 15k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0x88 66k 14k" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "write -P 0x99 83k 15k" $TEST_IMG | _filter_qemu_io - -$QEMU_IO -c "read -P 0x77 48k 15k" $TEST_IMG | _filter_qemu_io -backing_io $((63 * 1024)) 6 read | $QEMU_IO $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x88 66k 14k" $TEST_IMG | _filter_qemu_io -backing_io $((80 * 1024)) 6 read | $QEMU_IO $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0x99 83k 15k" $TEST_IMG | _filter_qemu_io -backing_io $((98 * 1024)) 4 read | $QEMU_IO $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0x77 48k 15k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x88 66k 14k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x99 83k 15k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0x77 48k 15k" "$TEST_IMG" | _filter_qemu_io +backing_io $((63 * 1024)) 6 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x88 66k 14k" "$TEST_IMG" | _filter_qemu_io +backing_io $((80 * 1024)) 6 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x99 83k 15k" "$TEST_IMG" | _filter_qemu_io +backing_io $((98 * 1024)) 4 read | $QEMU_IO "$TEST_IMG" | _filter_qemu_io _check_test_img diff --git a/tests/qemu-iotests/038 b/tests/qemu-iotests/038 index 36125eab1e..7bb7906e7f 100755 --- a/tests/qemu-iotests/038 +++ b/tests/qemu-iotests/038 @@ -66,11 +66,11 @@ function backing_io() done } -backing_io 0 256 write | $QEMU_IO $TEST_IMG | _filter_qemu_io +backing_io 0 256 write | $QEMU_IO "$TEST_IMG" | _filter_qemu_io -mv $TEST_IMG $TEST_IMG.base +mv "$TEST_IMG" "$TEST_IMG.base" -_make_test_img -b $TEST_IMG.base 6G +_make_test_img -b "$TEST_IMG.base" 6G echo echo "== Some concurrent requests touching the same cluster ==" @@ -94,8 +94,9 @@ function overlay_io() echo aio_write -P 0x90 4080k 80k } -overlay_io | $QEMU_IO $TEST_IMG | _filter_qemu_io |\ - sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g' +overlay_io | $QEMU_IO "$TEST_IMG" | _filter_qemu_io |\ + sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g' \ + -e 's/qemu-io> //g' | paste - - | sort | tr '\t' '\n' echo echo "== Verify image content ==" @@ -123,7 +124,7 @@ function verify_io() done } -verify_io | $QEMU_IO $TEST_IMG | _filter_qemu_io +verify_io | $QEMU_IO "$TEST_IMG" | _filter_qemu_io _check_test_img diff --git a/tests/qemu-iotests/038.out b/tests/qemu-iotests/038.out index 9cd0cd8771..96c2f849bb 100644 --- a/tests/qemu-iotests/038.out +++ b/tests/qemu-iotests/038.out @@ -517,7 +517,7 @@ qemu-io> wrote 65536/65536 bytes at offset 16711680 qemu-io> Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' == Some concurrent requests touching the same cluster == -qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> wrote 65536/65536 bytes at offset XXX +wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -577,8 +577,6 @@ wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 81920/81920 bytes at offset XXX -80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset XXX @@ -645,8 +643,6 @@ wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 81920/81920 bytes at offset XXX -80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset XXX @@ -705,6 +701,10 @@ wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset XXX 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 81920/81920 bytes at offset XXX +80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 81920/81920 bytes at offset XXX +80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Verify image content == qemu-io> read 4096/4096 bytes at offset 2064384 diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index ae3517575c..f85b4ce63f 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -54,10 +54,10 @@ echo "== Checking that image is clean on shutdown ==" IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img $size -$QEMU_IO -c "write -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0x5a 0 512" ""$TEST_IMG"" | _filter_qemu_io # The dirty bit must not be set -./qcow2.py $TEST_IMG dump-header | grep incompatible_features +./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img echo @@ -68,20 +68,20 @@ _make_test_img $size old_ulimit=$(ulimit -c) ulimit -c 0 # do not produce a core dump on abort(3) -$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io ulimit -c "$old_ulimit" # The dirty bit must be set -./qcow2.py $TEST_IMG dump-header | grep incompatible_features +./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img echo echo "== Read-only access must still work ==" -$QEMU_IO -r -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -r -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must be set -./qcow2.py $TEST_IMG dump-header | grep incompatible_features +./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features echo echo "== Repairing the image file must succeed ==" @@ -89,12 +89,12 @@ echo "== Repairing the image file must succeed ==" _check_test_img -r all # The dirty bit must not be set -./qcow2.py $TEST_IMG dump-header | grep incompatible_features +./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features echo echo "== Data should still be accessible after repair ==" -$QEMU_IO -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io echo echo "== Opening a dirty image read/write should repair it ==" @@ -104,16 +104,16 @@ _make_test_img $size old_ulimit=$(ulimit -c) ulimit -c 0 # do not produce a core dump on abort(3) -$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io ulimit -c "$old_ulimit" # The dirty bit must be set -./qcow2.py $TEST_IMG dump-header | grep incompatible_features +./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features -$QEMU_IO -c "write 0 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must not be set -./qcow2.py $TEST_IMG dump-header | grep incompatible_features +./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features echo echo "== Creating an image file with lazy_refcounts=off ==" @@ -123,11 +123,11 @@ _make_test_img $size old_ulimit=$(ulimit -c) ulimit -c 0 # do not produce a core dump on abort(3) -$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io ulimit -c "$old_ulimit" # The dirty bit must not be set since lazy_refcounts=off -./qcow2.py $TEST_IMG dump-header | grep incompatible_features +./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img # success, all done diff --git a/tests/qemu-iotests/042 b/tests/qemu-iotests/042 index 16b2fdbd5e..94ce3a9cc3 100755 --- a/tests/qemu-iotests/042 +++ b/tests/qemu-iotests/042 @@ -48,27 +48,27 @@ echo "== Creating zero size image ==" _make_test_img 0 _check_test_img -mv $TEST_IMG $TEST_IMG.orig +mv "$TEST_IMG" "$TEST_IMG.orig" echo echo "== Converting the image ==" -$QEMU_IMG convert -O $IMGFMT $TEST_IMG.orig $TEST_IMG +$QEMU_IMG convert -O $IMGFMT "$TEST_IMG.orig" "$TEST_IMG" _check_test_img echo echo "== Converting the image, compressed ==" if [ "$IMGFMT" == "qcow2" ]; then - $QEMU_IMG convert -c -O $IMGFMT $TEST_IMG.orig $TEST_IMG + $QEMU_IMG convert -c -O $IMGFMT "$TEST_IMG.orig" "$TEST_IMG" fi _check_test_img echo echo "== Rebasing the image ==" -$QEMU_IMG rebase -u -b $TEST_IMG.orig $TEST_IMG -$QEMU_IMG rebase -b $TEST_IMG.orig $TEST_IMG +$QEMU_IMG rebase -u -b "$TEST_IMG.orig" "$TEST_IMG" +$QEMU_IMG rebase -b "$TEST_IMG.orig" "$TEST_IMG" _check_test_img # success, all done diff --git a/tests/qemu-iotests/043 b/tests/qemu-iotests/043 index 478773d102..d7f12319b3 100755 --- a/tests/qemu-iotests/043 +++ b/tests/qemu-iotests/043 @@ -31,7 +31,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.[123].base + rm -f "$TEST_IMG".[123].base } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -47,39 +47,39 @@ _supported_os Linux size=128M _make_test_img $size -$QEMU_IMG rebase -u -b $TEST_IMG $TEST_IMG +$QEMU_IMG rebase -u -b "$TEST_IMG" "$TEST_IMG" echo echo "== backing file references self ==" _img_info --backing-chain _make_test_img $size -mv $TEST_IMG $TEST_IMG.base -_make_test_img -b $TEST_IMG.base $size -$QEMU_IMG rebase -u -b $TEST_IMG $TEST_IMG.base +mv "$TEST_IMG" "$TEST_IMG.base" +_make_test_img -b "$TEST_IMG.base" $size +$QEMU_IMG rebase -u -b "$TEST_IMG" "$TEST_IMG.base" echo echo "== parent references self ==" _img_info --backing-chain _make_test_img $size -mv $TEST_IMG $TEST_IMG.1.base -_make_test_img -b $TEST_IMG.1.base $size -mv $TEST_IMG $TEST_IMG.2.base -_make_test_img -b $TEST_IMG.2.base $size -mv $TEST_IMG $TEST_IMG.3.base -_make_test_img -b $TEST_IMG.3.base $size -$QEMU_IMG rebase -u -b $TEST_IMG.2.base $TEST_IMG.1.base +mv "$TEST_IMG" "$TEST_IMG.1.base" +_make_test_img -b "$TEST_IMG.1.base" $size +mv "$TEST_IMG" "$TEST_IMG.2.base" +_make_test_img -b "$TEST_IMG.2.base" $size +mv "$TEST_IMG" "$TEST_IMG.3.base" +_make_test_img -b "$TEST_IMG.3.base" $size +$QEMU_IMG rebase -u -b "$TEST_IMG.2.base" "$TEST_IMG.1.base" echo echo "== ancestor references another ancestor ==" _img_info --backing-chain _make_test_img $size -mv $TEST_IMG $TEST_IMG.1.base -_make_test_img -b $TEST_IMG.1.base $size -mv $TEST_IMG $TEST_IMG.2.base -_make_test_img -b $TEST_IMG.2.base $size +mv "$TEST_IMG" "$TEST_IMG.1.base" +_make_test_img -b "$TEST_IMG.1.base" $size +mv "$TEST_IMG" "$TEST_IMG.2.base" +_make_test_img -b "$TEST_IMG.2.base" $size echo echo "== finite chain of length 3 (human) ==" diff --git a/tests/qemu-iotests/045 b/tests/qemu-iotests/045 index 2b6f1af27a..6be8fc4912 100755 --- a/tests/qemu-iotests/045 +++ b/tests/qemu-iotests/045 @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Tests for fdsets. +# Tests for fdsets and getfd. # # Copyright (C) 2012 IBM Corp. # @@ -125,5 +125,54 @@ class TestFdSets(iotests.QMPTestCase): 'No file descriptor supplied via SCM_RIGHTS') self.vm.shutdown() +# Add fd at runtime, there are two ways: monitor related or fdset related +class TestSCMFd(iotests.QMPTestCase): + def setUp(self): + self.vm = iotests.VM() + qemu_img('create', '-f', iotests.imgfmt, image0, '128K') + # Add an unused monitor, to verify it works fine when two monitor + # instances present + self.vm.add_monitor_telnet("0",4445) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(image0) + + def _send_fd_by_SCM(self): + ret = self.vm.send_fd_scm(image0) + self.assertEqual(ret, 0, 'Failed to send fd with UNIX SCM') + + def test_add_fd(self): + self._send_fd_by_SCM() + result = self.vm.qmp('add-fd', fdset_id=2, opaque='image0:r') + self.assert_qmp(result, 'return/fdset-id', 2) + + def test_getfd(self): + self._send_fd_by_SCM() + result = self.vm.qmp('getfd', fdname='image0:r') + self.assert_qmp(result, 'return', {}) + + def test_getfd_invalid_fdname(self): + self._send_fd_by_SCM() + result = self.vm.qmp('getfd', fdname='0image0:r') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Parameter 'fdname' expects a name not starting with a digit") + + def test_closefd(self): + self._send_fd_by_SCM() + result = self.vm.qmp('getfd', fdname='image0:r') + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('closefd', fdname='image0:r') + self.assert_qmp(result, 'return', {}) + + def test_closefd_fd_not_found(self): + fdname = 'image0:r' + result = self.vm.qmp('closefd', fdname=fdname) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "File descriptor named '%s' not found" % fdname) + if __name__ == '__main__': iotests.main(supported_fmts=['raw']) diff --git a/tests/qemu-iotests/045.out b/tests/qemu-iotests/045.out index 3f8a935a08..e56cae021b 100644 --- a/tests/qemu-iotests/045.out +++ b/tests/qemu-iotests/045.out @@ -1,5 +1,5 @@ -...... +........... ---------------------------------------------------------------------- -Ran 6 tests +Ran 11 tests OK diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046 index 987bfff8fa..3f17ceb1b9 100755 --- a/tests/qemu-iotests/046 +++ b/tests/qemu-iotests/046 @@ -66,11 +66,11 @@ function backing_io() done } -backing_io 0 32 write | $QEMU_IO $TEST_IMG | _filter_qemu_io +backing_io 0 32 write | $QEMU_IO "$TEST_IMG" | _filter_qemu_io -mv $TEST_IMG $TEST_IMG.base +mv "$TEST_IMG" "$TEST_IMG.base" -_make_test_img -b $TEST_IMG.base 6G +_make_test_img -b "$TEST_IMG.base" 6G echo echo "== Some concurrent requests touching the same cluster ==" @@ -185,7 +185,7 @@ aio_flush EOF } -overlay_io | $QEMU_IO blkdebug::$TEST_IMG | _filter_qemu_io |\ +overlay_io | $QEMU_IO blkdebug::"$TEST_IMG" | _filter_qemu_io |\ sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g' echo @@ -252,7 +252,7 @@ function verify_io() echo read -P 17 0x11c000 0x4000 } -verify_io | $QEMU_IO $TEST_IMG | _filter_qemu_io +verify_io | $QEMU_IO "$TEST_IMG" | _filter_qemu_io _check_test_img diff --git a/tests/qemu-iotests/047 b/tests/qemu-iotests/047 index 0cf36b434f..c35cd096b8 100755 --- a/tests/qemu-iotests/047 +++ b/tests/qemu-iotests/047 @@ -66,7 +66,7 @@ read -P 0x55 1M 128k EOF } -qemu_io_cmds | $QEMU_IO $TEST_IMG | _filter_qemu_io +qemu_io_cmds | $QEMU_IO "$TEST_IMG" | _filter_qemu_io _check_test_img # success, all done diff --git a/tests/qemu-iotests/048 b/tests/qemu-iotests/048 index 7cce049d2d..9b9d118ef3 100755 --- a/tests/qemu-iotests/048 +++ b/tests/qemu-iotests/048 @@ -31,13 +31,13 @@ _cleanup() { echo "Cleanup" _cleanup_test_img - rm ${TEST_IMG2} + rm "${TEST_IMG2}" } trap "_cleanup; exit \$status" 0 1 2 3 15 _compare() { - $QEMU_IMG compare "$@" $TEST_IMG ${TEST_IMG2} + $QEMU_IMG compare "$@" "$TEST_IMG" "${TEST_IMG2}" echo $? } @@ -59,12 +59,12 @@ _make_test_img $size io_pattern write 524288 $CLUSTER_SIZE $CLUSTER_SIZE 4 45 # Compare identical images -cp $TEST_IMG ${TEST_IMG2} +cp "$TEST_IMG" "${TEST_IMG2}" _compare _compare -q # Compare images with different size -$QEMU_IMG resize $TEST_IMG +512M +$QEMU_IMG resize "$TEST_IMG" +512M _compare _compare -s diff --git a/tests/qemu-iotests/049 b/tests/qemu-iotests/049 index 6c6017e2d2..93aa0ea55f 100755 --- a/tests/qemu-iotests/049 +++ b/tests/qemu-iotests/049 @@ -63,13 +63,13 @@ sizes+="1024.0 1024.0b 1.5k 1.5K 1.5M 1.5G 1.5T" echo "== 1. Traditional size parameter ==" echo for s in $sizes; do - test_qemu_img create -f $IMGFMT $TEST_IMG $s + test_qemu_img create -f $IMGFMT "$TEST_IMG" $s done echo "== 2. Specifying size via -o ==" echo for s in $sizes; do - test_qemu_img create -f $IMGFMT -o size=$s $TEST_IMG + test_qemu_img create -f $IMGFMT -o size=$s "$TEST_IMG" done echo "== 3. Invalid sizes ==" @@ -77,8 +77,8 @@ echo sizes="-1024 -1k 1kilobyte foobar" for s in $sizes; do - test_qemu_img create -f $IMGFMT $TEST_IMG -- $s - test_qemu_img create -f $IMGFMT -o size=$s $TEST_IMG + test_qemu_img create -f $IMGFMT "$TEST_IMG" -- $s + test_qemu_img create -f $IMGFMT -o size=$s "$TEST_IMG" done echo "== Check correct interpretation of suffixes for cluster size ==" @@ -87,35 +87,35 @@ sizes="1024 1024b 1k 1K 1M " sizes+="1024.0 1024.0b 0.5k 0.5K 0.5M" for s in $sizes; do - test_qemu_img create -f $IMGFMT -o cluster_size=$s $TEST_IMG 64M + test_qemu_img create -f $IMGFMT -o cluster_size=$s "$TEST_IMG" 64M done echo "== Check compat level option ==" echo -test_qemu_img create -f $IMGFMT -o compat=0.10 $TEST_IMG 64M -test_qemu_img create -f $IMGFMT -o compat=1.1 $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o compat=0.10 "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT -o compat=1.1 "$TEST_IMG" 64M -test_qemu_img create -f $IMGFMT -o compat=0.42 $TEST_IMG 64M -test_qemu_img create -f $IMGFMT -o compat=foobar $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o compat=0.42 "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT -o compat=foobar "$TEST_IMG" 64M echo "== Check preallocation option ==" echo -test_qemu_img create -f $IMGFMT -o preallocation=off $TEST_IMG 64M -test_qemu_img create -f $IMGFMT -o preallocation=metadata $TEST_IMG 64M -test_qemu_img create -f $IMGFMT -o preallocation=1234 $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o preallocation=off "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT -o preallocation=metadata "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT -o preallocation=1234 "$TEST_IMG" 64M echo "== Check encryption option ==" echo -test_qemu_img create -f $IMGFMT -o encryption=off $TEST_IMG 64M -test_qemu_img create -f $IMGFMT -o encryption=on $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o encryption=off "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT -o encryption=on "$TEST_IMG" 64M echo "== Check lazy_refcounts option (only with v3) ==" echo -test_qemu_img create -f $IMGFMT -o compat=1.1,lazy_refcounts=off $TEST_IMG 64M -test_qemu_img create -f $IMGFMT -o compat=1.1,lazy_refcounts=on $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o compat=1.1,lazy_refcounts=off "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT -o compat=1.1,lazy_refcounts=on "$TEST_IMG" 64M -test_qemu_img create -f $IMGFMT -o compat=0.10,lazy_refcounts=off $TEST_IMG 64M -test_qemu_img create -f $IMGFMT -o compat=0.10,lazy_refcounts=on $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o compat=0.10,lazy_refcounts=off "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT -o compat=0.10,lazy_refcounts=on "$TEST_IMG" 64M # success, all done echo "*** done" diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index d2f0efe16d..ceb23289fd 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -96,7 +96,7 @@ qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 qemu-img: qcow2 doesn't support shrinking images yet -qemu-img: Formatting or formatting option not supported for file format 'qcow2' +qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k @@ -104,7 +104,7 @@ qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 qemu-img: qcow2 doesn't support shrinking images yet -qemu-img: Formatting or formatting option not supported for file format 'qcow2' +qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte @@ -120,7 +120,7 @@ qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 qemu-img: Parameter 'size' expects a size -qemu-img: Invalid options for file format 'qcow2'. +qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2'. == Check correct interpretation of suffixes for cluster size == @@ -163,13 +163,11 @@ qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M -Invalid compatibility level: '0.42' -qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument +qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M -Invalid compatibility level: 'foobar' -qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument +qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off == Check preallocation option == @@ -181,8 +179,7 @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M -Invalid preallocation mode: '1234' -qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument +qemu-img: TEST_DIR/t.qcow2: Invalid preallocation mode: '1234' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off == Check encryption option == @@ -205,8 +202,7 @@ qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M -Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument +qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on *** done diff --git a/tests/qemu-iotests/050 b/tests/qemu-iotests/050 index 05793e2d4b..07802bc49c 100755 --- a/tests/qemu-iotests/050 +++ b/tests/qemu-iotests/050 @@ -31,8 +31,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.old - rm -f $TEST_IMG.new + rm -f "$TEST_IMG.old" + rm -f "$TEST_IMG.new" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -53,21 +53,21 @@ echo "== Creating images ==" size=10M _make_test_img $size -$QEMU_IO -c "write -P 0x40 0 1048576" $TEST_IMG | _filter_qemu_io -mv $TEST_IMG $TEST_IMG.old +$QEMU_IO -c "write -P 0x40 0 1048576" "$TEST_IMG" | _filter_qemu_io +mv "$TEST_IMG" "$TEST_IMG.old" _make_test_img $size -$QEMU_IO -c "write -P 0x5a 0 1048576" $TEST_IMG | _filter_qemu_io -mv $TEST_IMG $TEST_IMG.new +$QEMU_IO -c "write -P 0x5a 0 1048576" "$TEST_IMG" | _filter_qemu_io +mv "$TEST_IMG" "$TEST_IMG.new" -_make_test_img -b $TEST_IMG.old $size -$QEMU_IO -c "write -z 0 1048576" $TEST_IMG | _filter_qemu_io +_make_test_img -b "$TEST_IMG.old" $size +$QEMU_IO -c "write -z 0 1048576" "$TEST_IMG" | _filter_qemu_io echo echo "== Rebasing the image ==" -$QEMU_IMG rebase -b $TEST_IMG.new $TEST_IMG -$QEMU_IO -c "read -P 0x00 0 1048576" $TEST_IMG | _filter_qemu_io +$QEMU_IMG rebase -b "$TEST_IMG.new" "$TEST_IMG" +$QEMU_IO -c "read -P 0x00 0 1048576" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 1f39c6ad21..356c3756f4 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -45,7 +45,14 @@ _supported_os Linux function do_run_qemu() { echo Testing: "$@" - echo quit | $QEMU -nographic -monitor stdio -serial none "$@" + ( + if ! test -t 0; then + while read cmd; do + echo $cmd + done + fi + echo quit + ) | $QEMU -nographic -monitor stdio -serial none "$@" echo } @@ -57,26 +64,34 @@ function run_qemu() size=128M _make_test_img $size +cp $TEST_IMG $TEST_IMG.orig +mv $TEST_IMG $TEST_IMG.base +_make_test_img -b $TEST_IMG.base $size echo echo === Unknown option === echo -run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt= -run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=on -run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=1234 -run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=foo +run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt= +run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=on +run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=1234 +run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=foo +echo +echo === Overriding backing file === +echo + +echo "info block" | run_qemu -drive file=$TEST_IMG,driver=qcow2,backing.file.filename=$TEST_IMG.orig -nodefaults echo echo === Enable and disable lazy refcounting on the command line, plus some invalid values === echo -run_qemu -drive file=$TEST_IMG,format=qcow2,lazy-refcounts=on -run_qemu -drive file=$TEST_IMG,format=qcow2,lazy-refcounts=off -run_qemu -drive file=$TEST_IMG,format=qcow2,lazy-refcounts= -run_qemu -drive file=$TEST_IMG,format=qcow2,lazy-refcounts=42 -run_qemu -drive file=$TEST_IMG,format=qcow2,lazy-refcounts=foo +run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts=on +run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts=off +run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts= +run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts=42 +run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts=foo echo @@ -85,8 +100,8 @@ echo _make_test_img -ocompat=0.10 $size -run_qemu -drive file=$TEST_IMG,format=qcow2,lazy-refcounts=on -run_qemu -drive file=$TEST_IMG,format=qcow2,lazy-refcounts=off +run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts=on +run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts=off echo echo === No medium === @@ -112,21 +127,21 @@ echo echo === Read-only === echo -run_qemu -drive file=$TEST_IMG,if=floppy,readonly=on -run_qemu -drive file=$TEST_IMG,if=ide,media=cdrom,readonly=on -run_qemu -drive file=$TEST_IMG,if=scsi,media=cdrom,readonly=on +run_qemu -drive file="$TEST_IMG",if=floppy,readonly=on +run_qemu -drive file="$TEST_IMG",if=ide,media=cdrom,readonly=on +run_qemu -drive file="$TEST_IMG",if=scsi,media=cdrom,readonly=on -run_qemu -drive file=$TEST_IMG,if=ide,readonly=on -run_qemu -drive file=$TEST_IMG,if=virtio,readonly=on -run_qemu -drive file=$TEST_IMG,if=scsi,readonly=on +run_qemu -drive file="$TEST_IMG",if=ide,readonly=on +run_qemu -drive file="$TEST_IMG",if=virtio,readonly=on +run_qemu -drive file="$TEST_IMG",if=scsi,readonly=on -run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-cd,drive=disk -run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk +run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-cd,drive=disk +run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk -run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-drive,drive=disk -run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-hd,drive=disk -run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk -run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk +run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-drive,drive=disk +run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-hd,drive=disk +run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk +run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk echo echo === Cache modes === @@ -146,8 +161,8 @@ echo echo === Specifying the protocol layer === echo -run_qemu -drive file=$TEST_IMG,file.driver=file -run_qemu -drive file=$TEST_IMG,file.driver=qcow2 +run_qemu -drive file="$TEST_IMG",file.driver=file +run_qemu -drive file="$TEST_IMG",file.driver=qcow2 echo echo === Parsing protocol from file name === diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 86e989cc6a..04bb236655 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -1,23 +1,30 @@ QA output created by 051 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' === Unknown option === Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt= -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' + + +=== Overriding backing file === + +Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig -nodefaults +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) i[K[Din[K[D[Dinf[K[D[D[Dinfo[K[D[D[D[Dinfo [K[D[D[D[D[Dinfo b[K[D[D[D[D[D[Dinfo bl[K[D[D[D[D[D[D[Dinfo blo[K[D[D[D[D[D[D[D[Dinfo bloc[K[D[D[D[D[D[D[D[D[Dinfo block[K +ide0-hd0: TEST_DIR/t.qcow2 (qcow2) + Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) + [not inserted](qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K === Enable and disable lazy refcounting on the command line, plus some invalid values === @@ -31,24 +38,20 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts= -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: Parameter 'lazy-refcounts' expects 'on' or 'off' -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: Parameter 'lazy-refcounts' expects 'on' or 'off' -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: Parameter 'lazy-refcounts' expects 'on' or 'off' -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off' === With version 2 images enabling lazy refcounts must fail === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off QEMU X.Y.Z monitor - type 'help' for more information
@@ -208,21 +211,18 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Can't use 'qcow2' as a block driver for the protocol level -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Invalid argument +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Can't use 'qcow2' as a block driver for the protocol level === Parsing protocol from file name === Testing: -hda foo:bar -QEMU_PROG: -hda foo:bar: Unknown protocol -QEMU_PROG: -hda foo:bar: could not open disk image foo:bar: No such file or directory +QEMU_PROG: -hda foo:bar: could not open disk image foo:bar: Unknown protocol Testing: -drive file=foo:bar -QEMU_PROG: -drive file=foo:bar: Unknown protocol -QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: No such file or directory +QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol Testing: -drive file.filename=foo:bar -QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: No such file or directory +QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open 'foo:bar': No such file or directory *** done diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052 index 14a5126635..f5f9683e68 100755 --- a/tests/qemu-iotests/052 +++ b/tests/qemu-iotests/052 @@ -41,6 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt generic _supported_proto generic _supported_os Linux +_unsupported_qemu_io_options --nocache size=128M @@ -48,12 +49,12 @@ _make_test_img $size echo echo "== reading whole image ==" -$QEMU_IO -s -c "read 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -s -c "read 0 $size" "$TEST_IMG" | _filter_qemu_io echo echo "== writing whole image does not modify image ==" -$QEMU_IO -s -c "write -P 0xa 0 $size" $TEST_IMG | _filter_qemu_io -$QEMU_IO -c "read -P 0 0 $size" $TEST_IMG | _filter_qemu_io +$QEMU_IO -s -c "write -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 0 $size" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/053 b/tests/qemu-iotests/053 index bc56992582..e589e5f126 100755 --- a/tests/qemu-iotests/053 +++ b/tests/qemu-iotests/053 @@ -30,7 +30,7 @@ status=1 # failure is the default! _cleanup() { - rm -f $TEST_IMG.orig + rm -f "$TEST_IMG.orig" _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -47,13 +47,13 @@ echo echo "== Creating single sector image ==" _make_test_img 512 -$QEMU_IO -c "write -P0xa 0 512" $TEST_IMG | _filter_qemu_io -mv $TEST_IMG $TEST_IMG.orig +$QEMU_IO -c "write -P0xa 0 512" "$TEST_IMG" | _filter_qemu_io +mv "$TEST_IMG" "$TEST_IMG.orig" echo echo "== Converting the image, compressed ==" -$QEMU_IMG convert -c -O $IMGFMT $TEST_IMG.orig $TEST_IMG +$QEMU_IMG convert -c -O $IMGFMT "$TEST_IMG.orig" "$TEST_IMG" _check_test_img echo @@ -64,7 +64,7 @@ _img_info | grep '^virtual size:' echo echo "== Verifying the compressed image ==" -$QEMU_IO -c "read -P0xa 0 512" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P0xa 0 512" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/054 b/tests/qemu-iotests/054 index b36042958c..5a0d1b16c2 100755 --- a/tests/qemu-iotests/054 +++ b/tests/qemu-iotests/054 @@ -49,7 +49,7 @@ _make_test_img $((1024*1024))T echo echo "creating too large image (1 EB) using qcow2.py" _make_test_img 4G -./qcow2.py $TEST_IMG set-header size $((1024 ** 6)) +./qcow2.py "$TEST_IMG" set-header size $((1024 ** 6)) _check_test_img # success, all done diff --git a/tests/qemu-iotests/054.out b/tests/qemu-iotests/054.out index 2f357c271d..7161d6e50b 100644 --- a/tests/qemu-iotests/054.out +++ b/tests/qemu-iotests/054.out @@ -1,10 +1,10 @@ QA output created by 054 creating too large image (1 EB) -qemu-img: The image size is too large for file format 'qcow2' (try using a larger cluster size) +qemu-img: TEST_DIR/t.IMGFMT: The image size is too large for file format 'IMGFMT' (try using a larger cluster size) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976 creating too large image (1 EB) using qcow2.py Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 -qemu-img: Could not open 'TEST_DIR/t.qcow2': File too large +qemu-img: Could not open 'TEST_DIR/t.qcow2': Image is too big *** done diff --git a/tests/qemu-iotests/057 b/tests/qemu-iotests/057 new file mode 100755 index 0000000000..9cdd582e39 --- /dev/null +++ b/tests/qemu-iotests/057 @@ -0,0 +1,259 @@ +#!/usr/bin/env python +# +# Tests for internal snapshot. +# +# Copyright (C) 2013 IBM, Inc. +# +# Based on 055. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import time +import os +import iotests +from iotests import qemu_img, qemu_io + +test_drv_base_name = 'drive' + +class ImageSnapshotTestCase(iotests.QMPTestCase): + image_len = 120 * 1024 * 1024 # MB + + def __init__(self, *args): + self.expect = [] + super(ImageSnapshotTestCase, self).__init__(*args) + + def _setUp(self, test_img_base_name, image_num): + self.vm = iotests.VM() + for i in range(0, image_num): + filename = '%s%d' % (test_img_base_name, i) + img = os.path.join(iotests.test_dir, filename) + device = '%s%d' % (test_drv_base_name, i) + qemu_img('create', '-f', iotests.imgfmt, img, str(self.image_len)) + self.vm.add_drive(img) + self.expect.append({'image': img, 'device': device, + 'snapshots': [], + 'snapshots_name_counter': 0}) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + for dev_expect in self.expect: + os.remove(dev_expect['image']) + + def createSnapshotInTransaction(self, snapshot_num, abort = False): + actions = [] + for dev_expect in self.expect: + num = dev_expect['snapshots_name_counter'] + for j in range(0, snapshot_num): + name = '%s_sn%d' % (dev_expect['device'], num) + num = num + 1 + if abort == False: + dev_expect['snapshots'].append({'name': name}) + dev_expect['snapshots_name_counter'] = num + actions.append({ + 'type': 'blockdev-snapshot-internal-sync', + 'data': { 'device': dev_expect['device'], + 'name': name }, + }) + + if abort == True: + actions.append({ + 'type': 'abort', + 'data': {}, + }) + + result = self.vm.qmp('transaction', actions = actions) + + if abort == True: + self.assert_qmp(result, 'error/class', 'GenericError') + else: + self.assert_qmp(result, 'return', {}) + + def verifySnapshotInfo(self): + result = self.vm.qmp('query-block') + + # Verify each expected result + for dev_expect in self.expect: + # 1. Find the returned image value and snapshot info + image_result = None + for device in result['return']: + if device['device'] == dev_expect['device']: + image_result = device['inserted']['image'] + break + self.assertTrue(image_result != None) + # Do not consider zero snapshot case now + sn_list_result = image_result['snapshots'] + sn_list_expect = dev_expect['snapshots'] + + # 2. Verify it with expect + self.assertTrue(len(sn_list_result) == len(sn_list_expect)) + + for sn_expect in sn_list_expect: + sn_result = None + for sn in sn_list_result: + if sn_expect['name'] == sn['name']: + sn_result = sn + break + self.assertTrue(sn_result != None) + # Fill in the detail info + sn_expect.update(sn_result) + + def deleteSnapshot(self, device, id = None, name = None): + sn_list_expect = None + sn_expect = None + + self.assertTrue(id != None or name != None) + + # Fill in the detail info include ID + self.verifySnapshotInfo() + + #find the expected snapshot list + for dev_expect in self.expect: + if dev_expect['device'] == device: + sn_list_expect = dev_expect['snapshots'] + break + self.assertTrue(sn_list_expect != None) + + if id != None and name != None: + for sn in sn_list_expect: + if sn['id'] == id and sn['name'] == name: + sn_expect = sn + result = \ + self.vm.qmp('blockdev-snapshot-delete-internal-sync', + device = device, + id = id, + name = name) + break + elif id != None: + for sn in sn_list_expect: + if sn['id'] == id: + sn_expect = sn + result = \ + self.vm.qmp('blockdev-snapshot-delete-internal-sync', + device = device, + id = id) + break + else: + for sn in sn_list_expect: + if sn['name'] == name: + sn_expect = sn + result = \ + self.vm.qmp('blockdev-snapshot-delete-internal-sync', + device = device, + name = name) + break + + self.assertTrue(sn_expect != None) + + self.assert_qmp(result, 'return', sn_expect) + sn_list_expect.remove(sn_expect) + +class TestSingleTransaction(ImageSnapshotTestCase): + def setUp(self): + self._setUp('test_a.img', 1) + + def test_create(self): + self.createSnapshotInTransaction(1) + self.verifySnapshotInfo() + + def test_error_name_empty(self): + actions = [{'type': 'blockdev-snapshot-internal-sync', + 'data': { 'device': self.expect[0]['device'], + 'name': '' }, + }] + result = self.vm.qmp('transaction', actions = actions) + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_error_device(self): + actions = [{'type': 'blockdev-snapshot-internal-sync', + 'data': { 'device': 'drive_error', + 'name': 'a' }, + }] + result = self.vm.qmp('transaction', actions = actions) + self.assert_qmp(result, 'error/class', 'DeviceNotFound') + + def test_error_exist(self): + self.createSnapshotInTransaction(1) + self.verifySnapshotInfo() + actions = [{'type': 'blockdev-snapshot-internal-sync', + 'data': { 'device': self.expect[0]['device'], + 'name': self.expect[0]['snapshots'][0] }, + }] + result = self.vm.qmp('transaction', actions = actions) + self.assert_qmp(result, 'error/class', 'GenericError') + +class TestMultipleTransaction(ImageSnapshotTestCase): + def setUp(self): + self._setUp('test_b.img', 2) + + def test_create(self): + self.createSnapshotInTransaction(3) + self.verifySnapshotInfo() + + def test_abort(self): + self.createSnapshotInTransaction(2) + self.verifySnapshotInfo() + self.createSnapshotInTransaction(3, abort = True) + self.verifySnapshotInfo() + +class TestSnapshotDelete(ImageSnapshotTestCase): + def setUp(self): + self._setUp('test_c.img', 1) + + def test_delete_with_id(self): + self.createSnapshotInTransaction(2) + self.verifySnapshotInfo() + self.deleteSnapshot(self.expect[0]['device'], + id = self.expect[0]['snapshots'][0]['id']) + self.verifySnapshotInfo() + + def test_delete_with_name(self): + self.createSnapshotInTransaction(3) + self.verifySnapshotInfo() + self.deleteSnapshot(self.expect[0]['device'], + name = self.expect[0]['snapshots'][1]['name']) + self.verifySnapshotInfo() + + def test_delete_with_id_and_name(self): + self.createSnapshotInTransaction(4) + self.verifySnapshotInfo() + self.deleteSnapshot(self.expect[0]['device'], + id = self.expect[0]['snapshots'][2]['id'], + name = self.expect[0]['snapshots'][2]['name']) + self.verifySnapshotInfo() + + + def test_error_device(self): + result = self.vm.qmp('blockdev-snapshot-delete-internal-sync', + device = 'drive_error', + id = '0') + self.assert_qmp(result, 'error/class', 'DeviceNotFound') + + def test_error_no_id_and_name(self): + result = self.vm.qmp('blockdev-snapshot-delete-internal-sync', + device = self.expect[0]['device']) + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_error_snapshot_not_exist(self): + self.createSnapshotInTransaction(2) + self.verifySnapshotInfo() + result = self.vm.qmp('blockdev-snapshot-delete-internal-sync', + device = self.expect[0]['device'], + id = self.expect[0]['snapshots'][0]['id'], + name = self.expect[0]['snapshots'][1]['name']) + self.assert_qmp(result, 'error/class', 'GenericError') + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2']) diff --git a/tests/qemu-iotests/057.out b/tests/qemu-iotests/057.out new file mode 100644 index 0000000000..281b69efea --- /dev/null +++ b/tests/qemu-iotests/057.out @@ -0,0 +1,5 @@ +............ +---------------------------------------------------------------------- +Ran 12 tests + +OK diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index b03429dd01..dd6addfaab 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -51,20 +51,25 @@ echo "=== Testing invalid granularity ===" echo _make_test_img 64M poke_file "$TEST_IMG" "$granularity_offset" "\xff\xff\xff\xff\xff\xff\xff\xff" -{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir echo "=== Testing too big L2 table size ===" echo _make_test_img 64M poke_file "$TEST_IMG" "$grain_table_size_offset" "\xff\xff\xff\xff" -{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir echo "=== Testing too big L1 table size ===" echo _make_test_img 64M poke_file "$TEST_IMG" "$capacity_offset" "\xff\xff\xff\xff" poke_file "$TEST_IMG" "$grain_table_size_offset" "\x01\x00\x00\x00" -{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "=== Testing monolithicFlat creation and opening ===" +echo +IMGOPTS="subformat=monolithicFlat" _make_test_img 2G +$QEMU_IMG info $TEST_IMG | _filter_testdir # success, all done echo "*** done" diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 9e715e5a95..9159dbec60 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -3,18 +3,25 @@ QA output created by 059 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 invalid granularity, image may be corrupt -qemu-io: can't open device TEST_DIR/t.vmdk +qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type no file open, try 'help open' === Testing too big L2 table size === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 L2 table size too big -qemu-io: can't open device TEST_DIR/t.vmdk +qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type no file open, try 'help open' === Testing too big L1 table size === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 L1 size too big -qemu-io: can't open device TEST_DIR/t.vmdk +qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type no file open, try 'help open' +=== Testing monolithicFlat creation and opening === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 +image: TEST_DIR/t.vmdk +file format: vmdk +virtual size: 2.0G (2147483648 bytes) +disk size: 4.0K *** done diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index 65bb09f023..9bbc43b706 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -71,7 +71,7 @@ $QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features # Try to open the image R/W (which should fail) -$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | sed -e "s/can't open device .*$/can't open device/" +$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir | _filter_imgfmt # Try to open it RO (which should succeed) $QEMU_IO -c "read 0 512" -r "$TEST_IMG" | _filter_qemu_io diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index ca4583a4a4..648f7437a2 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -11,8 +11,7 @@ incompatible_features 0x0 qcow2: Preventing invalid write on metadata (overlaps with active L1 table); image marked as corrupt. write failed: Input/output error incompatible_features 0x2 -qcow2: Image is corrupt; cannot be opened read/write. -qemu-io: can't open device +qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write no file open, try 'help open' read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 new file mode 100755 index 0000000000..fa9319da26 --- /dev/null +++ b/tests/qemu-iotests/061 @@ -0,0 +1,215 @@ +#!/bin/bash +# +# Test case for image option amendment in qcow2. +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This tests qocw2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + +echo +echo "=== Testing version downgrade with zero expansion ===" +echo +IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M +$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io +_check_test_img + +echo +echo "=== Testing dirty version downgrade ===" +echo +IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M +$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io +_check_test_img + +echo +echo "=== Testing version downgrade with unknown compat/autoclear flags ===" +echo +IMGOPTS="compat=1.1" _make_test_img 64M +./qcow2.py "$TEST_IMG" set-feature-bit compatible 42 +./qcow2.py "$TEST_IMG" set-feature-bit autoclear 42 +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +./qcow2.py "$TEST_IMG" dump-header +_check_test_img + +echo +echo "=== Testing version upgrade and resize ===" +echo +IMGOPTS="compat=0.10" _make_test_img 64M +$QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG" +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io +_check_test_img + +echo +echo "=== Testing dirty lazy_refcounts=off ===" +echo +IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M +$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG" +./qcow2.py "$TEST_IMG" dump-header +$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io +_check_test_img + +echo +echo "=== Testing backing file ===" +echo +IMGOPTS="compat=1.1" _make_test_img 64M +IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M +$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" "$TEST_IMG" +$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io +_check_test_img + +echo +echo "=== Testing invalid configurations ===" +echo +IMGOPTS="compat=0.10" _make_test_img 64M +$QEMU_IMG amend -o "lazy_refcounts=on" "$TEST_IMG" +$QEMU_IMG amend -o "compat=1.1" "$TEST_IMG" # actually valid +$QEMU_IMG amend -o "compat=0.10,lazy_refcounts=on" "$TEST_IMG" +$QEMU_IMG amend -o "compat=0.42" "$TEST_IMG" +$QEMU_IMG amend -o "foo=bar" "$TEST_IMG" +$QEMU_IMG amend -o "cluster_size=1k" "$TEST_IMG" +$QEMU_IMG amend -o "encryption=on" "$TEST_IMG" +$QEMU_IMG amend -o "preallocation=on" "$TEST_IMG" + +echo +echo "=== Testing correct handling of unset value ===" +echo +IMGOPTS="compat=1.1,cluster_size=1k" _make_test_img 64M +echo "Should work:" +$QEMU_IMG amend -o "lazy_refcounts=on" "$TEST_IMG" +echo "Should not work:" # Just to know which of these tests actually fails +$QEMU_IMG amend -o "cluster_size=64k" "$TEST_IMG" + +echo +echo "=== Testing zero expansion on inactive clusters ===" +echo +IMGOPTS="compat=1.1" _make_test_img 64M +$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -a foo "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io + +echo +echo "=== Testing zero expansion on shared L2 table ===" +echo +IMGOPTS="compat=1.1" _make_test_img 64M +$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -a foo "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io + +echo +echo "=== Testing zero expansion on backed image ===" +echo +IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M +$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io +IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M +$QEMU_IO -c "read -P 0x2a 0 128k" -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0 0 64k" -c "read -P 0x2a 64k 64k" "$TEST_IMG" | _filter_qemu_io + +echo +echo "=== Testing zero expansion on backed inactive clusters ===" +echo +IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M +$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io +IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M +$QEMU_IO -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IO -c "write -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -a foo "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0 0 64k" -c "read -P 0x2a 64k 64k" "$TEST_IMG" | _filter_qemu_io + +echo +echo "=== Testing zero expansion on backed image with shared L2 table ===" +echo +IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M +$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io +IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M +$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -a foo "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io + +echo +echo "=== Testing preallocated zero expansion on full image ===" +echo +IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG" _make_test_img 64M +$QEMU_IO -c "write -P 0x2a 0 64M" "$TEST_IMG" -c "write -z 0 64M" | _filter_qemu_io +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +_check_test_img +$QEMU_IO -c "read -P 0 0 64M" "$TEST_IMG" | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out new file mode 100644 index 0000000000..4027e0077e --- /dev/null +++ b/tests/qemu-iotests/061.out @@ -0,0 +1,387 @@ +QA output created by 061 + +=== Testing version downgrade with zero expansion === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x1 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +magic 0x514649fb +version 2 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 + +Header extension: +magic 0x6803f857 +length 144 +data <binary> + +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +=== Testing dirty version downgrade === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x1 +compatible_features 0x1 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Repairing cluster 5 refcount=0 reference=1 +Repairing cluster 6 refcount=0 reference=1 +magic 0x514649fb +version 2 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 + +Header extension: +magic 0x6803f857 +length 144 +data <binary> + +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +=== Testing version downgrade with unknown compat/autoclear flags === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x40000000000 +autoclear_features 0x40000000000 +refcount_order 4 +header_length 104 + +magic 0x514649fb +version 2 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 + +Header extension: +magic 0x6803f857 +length 144 +data <binary> + +No errors were found on the image. + +=== Testing version upgrade and resize === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 65536/65536 bytes at offset 44040192 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +magic 0x514649fb +version 2 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 + +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 134217728 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x1 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0x6803f857 +length 144 +data <binary> + +read 65536/65536 bytes at offset 44040192 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +=== Testing dirty lazy_refcounts=off === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x1 +compatible_features 0x1 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Repairing cluster 5 refcount=0 reference=1 +Repairing cluster 6 refcount=0 reference=1 +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0x6803f857 +length 144 +data <binary> + +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +=== Testing backing file === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +=== Testing invalid configurations === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +qemu-img: Error while amending options: Invalid argument +Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +qemu-img: Error while amending options: Invalid argument +Unknown compatibility level 0.42. +qemu-img: Error while amending options: Invalid argument +Unknown option 'foo' +qemu-img: Invalid options for file format 'qcow2' +Changing the cluster size is not supported. +qemu-img: Error while amending options: Operation not supported +Changing the encryption flag is not supported. +qemu-img: Error while amending options: Operation not supported +Cannot change preallocation mode. +qemu-img: Error while amending options: Operation not supported + +=== Testing correct handling of unset value === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Should work: +Should not work: +Changing the cluster size is not supported. +qemu-img: Error while amending options: Operation not supported + +=== Testing zero expansion on inactive clusters === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing zero expansion on shared L2 table === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing zero expansion on backed image === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing zero expansion on backed inactive clusters === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing zero expansion on backed image with shared L2 table === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing preallocated zero expansion on full image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +read 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 index de0cbbd8bb..2ab8f20e02 100755 --- a/tests/qemu-iotests/063 +++ b/tests/qemu-iotests/063 @@ -32,7 +32,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.orig $TEST_IMG.raw $TEST_IMG.raw2 + rm -f "$TEST_IMG.orig" "$TEST_IMG.raw" "$TEST_IMG.raw2" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -49,47 +49,47 @@ _make_test_img 4M echo "== Testing conversion with -n fails with no target file ==" # check .orig file does not exist -rm -f $TEST_IMG.orig -if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n $TEST_IMG $TEST_IMG.orig >/dev/null 2>&1; then +rm -f "$TEST_IMG.orig" +if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG" "$TEST_IMG.orig" >/dev/null 2>&1; then exit 1 fi echo "== Testing conversion with -n succeeds with a target file ==" -rm -f $TEST_IMG.orig -cp $TEST_IMG $TEST_IMG.orig -if ! $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n $TEST_IMG $TEST_IMG.orig ; then +rm -f "$TEST_IMG.orig" +cp "$TEST_IMG" "$TEST_IMG.orig" +if ! $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG" "$TEST_IMG.orig" ; then exit 1 fi echo "== Testing conversion to raw is the same after conversion with -n ==" # compare the raw files -if ! $QEMU_IMG convert -f $IMGFMT -O raw $TEST_IMG $TEST_IMG.raw1 ; then +if ! $QEMU_IMG convert -f $IMGFMT -O raw "$TEST_IMG" "$TEST_IMG.raw1" ; then exit 1 fi -if ! $QEMU_IMG convert -f $IMGFMT -O raw $TEST_IMG.orig $TEST_IMG.raw2 ; then +if ! $QEMU_IMG convert -f $IMGFMT -O raw "$TEST_IMG.orig" "$TEST_IMG.raw2" ; then exit 1 fi -if ! cmp $TEST_IMG.raw1 $TEST_IMG.raw2 ; then +if ! cmp "$TEST_IMG.raw1" "$TEST_IMG.raw2" ; then exit 1 fi echo "== Testing conversion back to original format ==" -if ! $QEMU_IMG convert -f raw -O $IMGFMT -n $TEST_IMG.raw2 $TEST_IMG ; then +if ! $QEMU_IMG convert -f raw -O $IMGFMT -n "$TEST_IMG.raw2" "$TEST_IMG" ; then exit 1 fi _check_test_img echo "== Testing conversion to a smaller file fails ==" -rm -f $TEST_IMG.orig -mv $TEST_IMG $TEST_IMG.orig +rm -f "$TEST_IMG.orig" +mv "$TEST_IMG" "$TEST_IMG.orig" _make_test_img 2M -if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n $TEST_IMG.orig $TEST_IMG >/dev/null 2>&1; then +if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG.orig" "$TEST_IMG" >/dev/null 2>&1; then exit 1 fi -rm -f $TEST_IMG.orig $TEST_IMG.raw $TEST_IMG.raw2 +rm -f "$TEST_IMG.orig" "$TEST_IMG.raw" "$TEST_IMG.raw2" echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 4ecf497d8e..f5f328f5f5 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -164,6 +164,7 @@ QEMU_IO -- $QEMU_IO IMGFMT -- $FULL_IMGFMT_DETAILS IMGPROTO -- $FULL_IMGPROTO_DETAILS PLATFORM -- $FULL_HOST_DETAILS +SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER EOF #MKFS_OPTIONS -- $FULL_MKFS_OPTIONS diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index d794e624e7..d90a8bca8b 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -125,6 +125,17 @@ fi export TEST_DIR +if [ -z "$SAMPLE_IMG_DIR" ]; then + SAMPLE_IMG_DIR=`pwd`/sample_images +fi + +if [ ! -d "$SAMPLE_IMG_DIR" ]; then + echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory" + exit 1 +fi + +export SAMPLE_IMG_DIR + _readlink() { if [ $# -ne 1 ]; then diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 88fecf7870..1b22db04f4 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -91,6 +91,18 @@ _set_default_imgopts() fi } +_use_sample_img() +{ + SAMPLE_IMG_FILE="${1%\.bz2}" + TEST_IMG="$TEST_DIR/$SAMPLE_IMG_FILE" + bzcat "$SAMPLE_IMG_DIR/$1" > "$TEST_IMG" + if [ $? -ne 0 ] + then + echo "_use_sample_img error, cannot extract '$SAMPLE_IMG_DIR/$1'" + exit 1 + fi +} + _make_test_img() { # extra qemu-img options can be added by tests @@ -123,7 +135,7 @@ _make_test_img() fi # XXX(hch): have global image options? - $QEMU_IMG create -f $IMGFMT $extra_img_options $img_name $image_size | \ + $QEMU_IMG create -f $IMGFMT $extra_img_options $img_name $image_size 2>&1 | \ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ @@ -152,20 +164,24 @@ _cleanup_test_img() nbd) kill $QEMU_NBD_PID - rm -f $TEST_IMG_FILE + rm -f "$TEST_IMG_FILE" ;; file) - rm -f $TEST_DIR/t.$IMGFMT - rm -f $TEST_DIR/t.$IMGFMT.orig - rm -f $TEST_DIR/t.$IMGFMT.base + rm -f "$TEST_DIR/t.$IMGFMT" + rm -f "$TEST_DIR/t.$IMGFMT.orig" + rm -f "$TEST_DIR/t.$IMGFMT.base" + if [ -n "$SAMPLE_IMG_FILE" ] + then + rm -f "$TEST_DIR/$SAMPLE_IMG_FILE" + fi ;; rbd) - rbd rm $TEST_DIR/t.$IMGFMT > /dev/null + rbd rm "$TEST_DIR/t.$IMGFMT" > /dev/null ;; sheepdog) - collie vdi delete $TEST_DIR/t.$IMGFMT + collie vdi delete "$TEST_DIR/t.$IMGFMT" ;; esac @@ -173,7 +189,7 @@ _cleanup_test_img() _check_test_img() { - $QEMU_IMG check "$@" -f $IMGFMT $TEST_IMG 2>&1 | _filter_testdir | \ + $QEMU_IMG check "$@" -f $IMGFMT "$TEST_IMG" 2>&1 | _filter_testdir | \ sed -e '/allocated.*fragmented.*compressed clusters/d' \ -e 's/qemu-img: This image format does not support checks/No errors were found on the image./' \ -e '/Image end offset: [0-9]\+/d' @@ -181,7 +197,7 @@ _check_test_img() _img_info() { - $QEMU_IMG info "$@" $TEST_IMG 2>&1 | \ + $QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 316b1dd75c..1ad02e5a2c 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -63,7 +63,9 @@ 054 rw auto 055 rw auto 056 rw auto backing +057 rw auto 059 rw auto 060 rw auto +061 rw auto 062 rw auto 063 rw auto diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 33ad0ecb92..376d6e8ffe 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -21,7 +21,7 @@ import re import subprocess import string import unittest -import sys; sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'QMP')) +import sys; sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts', 'qmp')) import qmp import struct @@ -38,6 +38,8 @@ imgfmt = os.environ.get('IMGFMT', 'raw') imgproto = os.environ.get('IMGPROTO', 'file') test_dir = os.environ.get('TEST_DIR', '/var/tmp') +socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper') + def qemu_img(*args): '''Run qemu-img and return the exit code''' devnull = open('/dev/null', 'r+') @@ -80,6 +82,12 @@ class VM(object): '-display', 'none', '-vga', 'none'] self._num_drives = 0 + # This can be used to add an unused monitor instance. + def add_monitor_telnet(self, ip, port): + args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port) + self._args.append('-monitor') + self._args.append(args) + def add_drive(self, path, opts=''): '''Add a virtio-blk drive to the VM''' options = ['if=virtio', @@ -112,6 +120,21 @@ class VM(object): self._args.append(','.join(options)) return self + def send_fd_scm(self, fd_file_path): + # In iotest.py, the qmp should always use unix socket. + assert self._qmp.is_scm_available() + bin = socket_scm_helper + if os.path.exists(bin) == False: + print "Scm help program does not present, path '%s'." % bin + return -1 + fd_param = ["%s" % bin, + "%d" % self._qmp.get_sock_fd(), + "%s" % fd_file_path] + devnull = open('/dev/null', 'rb') + p = subprocess.Popen(fd_param, stdin=devnull, stdout=sys.stdout, + stderr=sys.stderr) + return p.wait() + def launch(self): '''Launch the VM and establish a QMP connection''' devnull = open('/dev/null', 'rb') diff --git a/tests/qemu-iotests/sample_images/README b/tests/qemu-iotests/sample_images/README new file mode 100644 index 0000000000..507af5f5ff --- /dev/null +++ b/tests/qemu-iotests/sample_images/README @@ -0,0 +1,8 @@ +This is for small sample images to be used with qemu-iotests, intended for +non-native formats that QEMU supports for compatibility. The idea is to use +the native tool to create the sample image. + +For instance, a VHDX image in this directory would be an image created not by +QEMU itself, but rather created by Hyper-V. + +Sample images added here must be compressed with bzip2. diff --git a/tests/qemu-iotests/socket_scm_helper.c b/tests/qemu-iotests/socket_scm_helper.c new file mode 100644 index 0000000000..0e2b2859af --- /dev/null +++ b/tests/qemu-iotests/socket_scm_helper.c @@ -0,0 +1,135 @@ +/* + * SCM_RIGHTS with unix socket help program for test + * + * Copyright IBM, Inc. 2013 + * + * Authors: + * Wenchao Xia <xiawenc@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include <stdio.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +/* #define SOCKET_SCM_DEBUG */ + +/* + * @fd and @fd_to_send will not be checked for validation in this function, + * a blank will be sent as iov data to notify qemu. + */ +static int send_fd(int fd, int fd_to_send) +{ + struct msghdr msg; + struct iovec iov[1]; + int ret; + char control[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + + memset(&msg, 0, sizeof(msg)); + memset(control, 0, sizeof(control)); + + /* Send a blank to notify qemu */ + iov[0].iov_base = (void *)" "; + iov[0].iov_len = 1; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msg); + + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + do { + ret = sendmsg(fd, &msg, 0); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + fprintf(stderr, "Failed to send msg, reason: %s\n", strerror(errno)); + } + + return ret; +} + +/* Convert string to fd number. */ +static int get_fd_num(const char *fd_str) +{ + int sock; + char *err; + + errno = 0; + sock = strtol(fd_str, &err, 10); + if (errno) { + fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n", + strerror(errno)); + return -1; + } + if (!*fd_str || *err || sock < 0) { + fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str); + return -1; + } + + return sock; +} + +/* + * To make things simple, the caller needs to specify: + * 1. socket fd. + * 2. path of the file to be sent. + */ +int main(int argc, char **argv, char **envp) +{ + int sock, fd, ret; + +#ifdef SOCKET_SCM_DEBUG + int i; + for (i = 0; i < argc; i++) { + fprintf(stderr, "Parameter %d: %s\n", i, argv[i]); + } +#endif + + if (argc != 3) { + fprintf(stderr, + "Usage: %s < socket-fd > < file-path >\n", + argv[0]); + return EXIT_FAILURE; + } + + + sock = get_fd_num(argv[1]); + if (sock < 0) { + return EXIT_FAILURE; + } + + /* Now only open a file in readonly mode for test purpose. If more precise + control is needed, use python script in file operation, which is + supposed to fork and exec this program. */ + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open file '%s'\n", argv[2]); + return EXIT_FAILURE; + } + + ret = send_fd(sock, fd); + if (ret < 0) { + close(fd); + return EXIT_FAILURE; + } + + close(fd); + return EXIT_SUCCESS; +} diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c index 39be046ec7..15a885e882 100644 --- a/tests/test-coroutine.c +++ b/tests/test-coroutine.c @@ -182,17 +182,17 @@ static void perf_nesting(void) unsigned int i, maxcycles, maxnesting; double duration; - maxcycles = 100000000; + maxcycles = 10000; maxnesting = 1000; Coroutine *root; - NestData nd = { - .n_enter = 0, - .n_return = 0, - .max = maxnesting, - }; g_test_timer_start(); for (i = 0; i < maxcycles; i++) { + NestData nd = { + .n_enter = 0, + .n_return = 0, + .max = maxnesting, + }; root = qemu_coroutine_create(nest); qemu_coroutine_enter(root, &nd); } @@ -202,6 +202,38 @@ static void perf_nesting(void) maxcycles, maxnesting, duration); } +/* + * Yield benchmark + */ + +static void coroutine_fn yield_loop(void *opaque) +{ + unsigned int *counter = opaque; + + while ((*counter) > 0) { + (*counter)--; + qemu_coroutine_yield(); + } +} + +static void perf_yield(void) +{ + unsigned int i, maxcycles; + double duration; + + maxcycles = 100000000; + i = maxcycles; + Coroutine *coroutine = qemu_coroutine_create(yield_loop); + + g_test_timer_start(); + while (i > 0) { + qemu_coroutine_enter(coroutine, &i); + } + duration = g_test_timer_elapsed(); + + g_test_message("Yield %u iterations: %f s\n", + maxcycles, duration); +} int main(int argc, char **argv) { @@ -214,6 +246,7 @@ int main(int argc, char **argv) if (g_test_perf()) { g_test_add_func("/perf/lifecycle", perf_lifecycle); g_test_add_func("/perf/nesting", perf_nesting); + g_test_add_func("/perf/yield", perf_yield); } return g_test_run(); } diff --git a/trace-events b/trace-events index d4dba24551..8695e9e5b7 100644 --- a/trace-events +++ b/trace-events @@ -1109,7 +1109,6 @@ qemu_spice_wakeup(uint32_t qid) "%d" qemu_spice_create_update(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "lr %d -> %d, tb -> %d -> %d" # hw/display/qxl-render.c -qxl_render_blit_guest_primary_initialized(void) "" qxl_render_blit(int32_t stride, int32_t left, int32_t right, int32_t top, int32_t bottom) "stride=%d [%d, %d, %d, %d]" qxl_render_guest_primary_resized(int32_t width, int32_t height, int32_t stride, int32_t bytes_pp, int32_t bits_pp) "%dx%d, stride %d, bpp %d, depth %d" qxl_render_update_area_done(void *cookie) "%p" @@ -1122,7 +1121,7 @@ spapr_pci_rtas_ibm_query_interrupt_source_number(unsigned ioa, unsigned intr) "q spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) "@%"PRIx64"<=%"PRIx64" IRQ %u" spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u" -# hw/ppc/xics.c +# hw/intc/xics.c xics_icp_check_ipi(int server, uint8_t mfrr) "CPU %d can take IPI mfrr=%#x" xics_icp_accept(uint32_t old_xirr, uint32_t new_xirr) "icp_accept: XIRR %#"PRIx32"->%#"PRIx32 xics_icp_eoi(int server, uint32_t xirr, uint32_t new_xirr) "icp_eoi: server %d given XIRR %#"PRIx32" new XIRR %#"PRIx32 @@ -1167,9 +1166,9 @@ virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *dev migrate_set_state(int new_state) "new state %d" # kvm-all.c -kvm_ioctl(int type, void *arg) "type %d, arg %p" -kvm_vm_ioctl(int type, void *arg) "type %d, arg %p" -kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type %d, arg %p" +kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" +kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p" +kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p" kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d" # memory.c diff --git a/translate-all.c b/translate-all.c index 2c923c644b..e7aff928b6 100644 --- a/translate-all.c +++ b/translate-all.c @@ -1679,8 +1679,9 @@ static int dump_region(void *priv, abi_ulong start, /* dump memory mappings */ void page_dump(FILE *f) { - (void) fprintf(f, "%-8s %-8s %-8s %s\n", - "start", "end", "size", "prot"); + const int length = sizeof(abi_ulong) * 2; + (void) fprintf(f, "%-*s %-*s %-*s %s\n", + length, "start", length, "end", length, "size", "prot"); walk_memory_regions(f, dump_region); } diff --git a/util/iov.c b/util/iov.c index f705586808..bb46c04e4d 100644 --- a/util/iov.c +++ b/util/iov.c @@ -181,13 +181,11 @@ ssize_t iov_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, assert(iov[niov].iov_len > tail); orig_len = iov[niov].iov_len; iov[niov++].iov_len = tail; - } - - ret = do_send_recv(sockfd, iov, niov, do_send); - - /* Undo the changes above before checking for errors */ - if (tail) { + ret = do_send_recv(sockfd, iov, niov, do_send); + /* Undo the changes above before checking for errors */ iov[niov-1].iov_len = orig_len; + } else { + ret = do_send_recv(sockfd, iov, niov, do_send); } if (offset) { iov[0].iov_base -= offset; diff --git a/util/osdep.c b/util/osdep.c index 685c8ae889..62072b4be3 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -207,6 +207,13 @@ int qemu_open(const char *name, int flags, ...) } #endif +#ifdef O_DIRECT + if (ret == -1 && errno == EINVAL && (flags & O_DIRECT)) { + error_report("file system may not support O_DIRECT"); + errno = EINVAL; /* in case it was clobbered */ + } +#endif /* O_DIRECT */ + return ret; } diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 3dc8b1b074..253bc3df2e 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -112,9 +112,7 @@ void *qemu_anon_ram_alloc(size_t size) size_t offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; if (ptr == MAP_FAILED) { - fprintf(stderr, "Failed to allocate %zu B: %s\n", - size, strerror(errno)); - abort(); + return NULL; } ptr += offset; diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 961fbf5e3d..983b7a2375 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -65,10 +65,7 @@ void *qemu_anon_ram_alloc(size_t size) /* FIXME: this is not exactly optimal solution since VirtualAlloc has 64Kb granularity, but at least it guarantees us that the memory is page aligned. */ - if (!size) { - abort(); - } - ptr = qemu_oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); + ptr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); trace_qemu_anon_ram_alloc(size, ptr); return ptr; } @@ -1719,14 +1719,14 @@ static pid_t shutdown_pid; static int powerdown_requested; static int debug_requested; static int suspend_requested; -static int wakeup_requested; +static WakeupReason wakeup_reason; static NotifierList powerdown_notifiers = NOTIFIER_LIST_INITIALIZER(powerdown_notifiers); static NotifierList suspend_notifiers = NOTIFIER_LIST_INITIALIZER(suspend_notifiers); static NotifierList wakeup_notifiers = NOTIFIER_LIST_INITIALIZER(wakeup_notifiers); -static uint32_t wakeup_reason_mask = ~0; +static uint32_t wakeup_reason_mask = ~(1 << QEMU_WAKEUP_REASON_NONE); static RunState vmstop_requested = RUN_STATE_MAX; int qemu_shutdown_requested_get(void) @@ -1776,11 +1776,9 @@ static int qemu_suspend_requested(void) return r; } -static int qemu_wakeup_requested(void) +static WakeupReason qemu_wakeup_requested(void) { - int r = wakeup_requested; - wakeup_requested = 0; - return r; + return wakeup_reason; } static int qemu_powerdown_requested(void) @@ -1897,8 +1895,7 @@ void qemu_system_wakeup_request(WakeupReason reason) return; } runstate_set(RUN_STATE_RUNNING); - notifier_list_notify(&wakeup_notifiers, &reason); - wakeup_requested = 1; + wakeup_reason = reason; qemu_notify_event(); } @@ -1990,6 +1987,8 @@ static bool main_loop_should_exit(void) pause_all_vcpus(); cpu_synchronize_all_states(); qemu_system_reset(VMRESET_SILENT); + notifier_list_notify(&wakeup_notifiers, &wakeup_reason); + wakeup_reason = QEMU_WAKEUP_REASON_NONE; resume_all_vcpus(); monitor_protocol_event(QEVENT_WAKEUP, NULL); } @@ -98,6 +98,7 @@ typedef struct XenIOState { Notifier exit; Notifier suspend; + Notifier wakeup; } XenIOState; /* Xen specific function for piix pci */ @@ -613,13 +614,13 @@ static ioreq_t *cpu_get_ioreq(XenIOState *state) } if (port != -1) { - for (i = 0; i < smp_cpus; i++) { + for (i = 0; i < max_cpus; i++) { if (state->ioreq_local_port[i] == port) { break; } } - if (i == smp_cpus) { + if (i == max_cpus) { hw_error("Fatal error while trying to get io event!\n"); } @@ -1060,6 +1061,11 @@ static void xen_read_physmap(XenIOState *state) free(entries); } +static void xen_wakeup_notifier(Notifier *notifier, void *data) +{ + xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); +} + int xen_hvm_init(MemoryRegion **ram_memory) { int i, rc; @@ -1089,6 +1095,9 @@ int xen_hvm_init(MemoryRegion **ram_memory) state->suspend.notify = xen_suspend_notifier; qemu_register_suspend_notifier(&state->suspend); + state->wakeup.notify = xen_wakeup_notifier; + qemu_register_wakeup_notifier(&state->wakeup); + xc_get_hvm_param(xen_xc, xen_domid, HVM_PARAM_IOREQ_PFN, &ioreq_pfn); DPRINTF("shared page at pfn %lx\n", ioreq_pfn); state->shared_page = xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, @@ -1106,10 +1115,10 @@ int xen_hvm_init(MemoryRegion **ram_memory) hw_error("map buffered IO page returned error %d", errno); } - state->ioreq_local_port = g_malloc0(smp_cpus * sizeof (evtchn_port_t)); + state->ioreq_local_port = g_malloc0(max_cpus * sizeof (evtchn_port_t)); /* FIXME: how about if we overflow the page here? */ - for (i = 0; i < smp_cpus; i++) { + for (i = 0; i < max_cpus; i++) { rc = xc_evtchn_bind_interdomain(state->xce_handle, xen_domid, xen_vcpu_eport(state->shared_page, i)); if (rc == -1) { |