diff options
387 files changed, 16283 insertions, 7620 deletions
diff --git a/.gitignore b/.gitignore index d2c5c2f6b8..8e1b73f622 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ fsdev/virtfs-proxy-helper.pod *.la *.pc .libs +.sdk *.swp *.orig .pc @@ -3,6 +3,7 @@ # Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> +Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162> Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162> Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162> diff --git a/MAINTAINERS b/MAINTAINERS index d128ed035a..fbe1fd9489 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -50,8 +50,7 @@ Descriptions of section entries: General Project Administration ------------------------------ -M: Anthony Liguori <anthony@codemonkey.ws> -M: Paul Brook <paul@codesourcery.com> +M: Anthony Liguori <aliguori@amazon.com> Guest CPU cores (TCG): ---------------------- @@ -62,7 +61,6 @@ F: target-alpha/ F: hw/alpha/ ARM -M: Paul Brook <paul@codesourcery.com> M: Peter Maydell <peter.maydell@linaro.org> S: Maintained F: target-arm/ @@ -83,8 +81,7 @@ F: hw/lm32/ F: hw/char/lm32_* M68K -M: Paul Brook <paul@codesourcery.com> -S: Odd Fixes +S: Orphan F: target-m68k/ F: hw/m68k/ @@ -248,7 +245,6 @@ F: hw/*/imx* F: hw/arm/kzm.c Integrator CP -M: Paul Brook <paul@codesourcery.com> M: Peter Maydell <peter.maydell@linaro.org> S: Maintained F: hw/arm/integratorcp.c @@ -274,7 +270,6 @@ S: Maintained F: hw/arm/palm.c Real View -M: Paul Brook <paul@codesourcery.com> M: Peter Maydell <peter.maydell@linaro.org> S: Maintained F: hw/arm/realview* @@ -285,13 +280,11 @@ S: Maintained F: hw/arm/spitz.c Stellaris -M: Paul Brook <paul@codesourcery.com> M: Peter Maydell <peter.maydell@linaro.org> S: Maintained F: hw/*/stellaris* Versatile PB -M: Paul Brook <paul@codesourcery.com> M: Peter Maydell <peter.maydell@linaro.org> S: Maintained F: hw/*/versatile* @@ -327,18 +320,15 @@ F: hw/lm32/milkymist.c M68K Machines ------------- an5206 -M: Paul Brook <paul@codesourcery.com> -S: Maintained +S: Orphan F: hw/m68k/an5206.c dummy_m68k -M: Paul Brook <paul@codesourcery.com> -S: Maintained +S: Orphan F: hw/m68k/dummy_m68k.c mcf5208 -M: Paul Brook <paul@codesourcery.com> -S: Maintained +S: Orphan F: hw/m68k/mcf5208.c MicroBlaze Machines @@ -509,7 +499,7 @@ F: hw/unicore32/ X86 Machines ------------ PC -M: Anthony Liguori <anthony@codemonkey.ws> +M: Anthony Liguori <aliguori@amazon.com> S: Supported F: hw/i386/pc.[ch] F: hw/i386/pc_piix.c @@ -567,8 +557,7 @@ F: hw/scsi/* T: git git://github.com/bonzini/qemu.git scsi-next LSI53C895A -M: Paul Brook <paul@codesourcery.com> -S: Odd Fixes +S: Orphan F: hw/scsi/lsi53c895a.c SSI @@ -593,7 +582,7 @@ S: Supported F: hw/*/*vhost* virtio -M: Anthony Liguori <anthony@codemonkey.ws> +M: Anthony Liguori <aliguori@amazon.com> S: Supported F: hw/*/virtio* @@ -638,6 +627,7 @@ Subsystems ---------- Audio M: Vassili Karpov (malc) <av1474@comtv.ru> +M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained F: audio/ F: hw/audio/ @@ -651,7 +641,7 @@ F: block/ F: hw/block/ Character Devices -M: Anthony Liguori <anthony@codemonkey.ws> +M: Anthony Liguori <aliguori@amazon.com> S: Maintained F: qemu-char.c @@ -689,7 +679,7 @@ F: audio/spiceaudio.c F: hw/display/qxl* Graphics -M: Anthony Liguori <anthony@codemonkey.ws> +M: Anthony Liguori <aliguori@amazon.com> S: Maintained F: ui/ @@ -699,7 +689,7 @@ S: Odd Fixes F: ui/cocoa.m Main loop -M: Anthony Liguori <anthony@codemonkey.ws> +M: Anthony Liguori <aliguori@amazon.com> S: Supported F: vl.c @@ -711,7 +701,7 @@ F: hmp.c F: hmp-commands.hx Network device layer -M: Anthony Liguori <anthony@codemonkey.ws> +M: Anthony Liguori <aliguori@amazon.com> M: Stefan Hajnoczi <stefanha@redhat.com> S: Maintained F: net/ @@ -766,6 +756,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 +838,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 @@ -235,8 +235,9 @@ clean: rm -f qemu-options.def find . -name '*.[oda]' -type f -exec rm -f {} + find . -name '*.l[oa]' -type f -exec rm -f {} + - rm -f $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ - rm -Rf .libs + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ + 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 @@ -272,6 +274,7 @@ distclean: clean for d in $(TARGET_DIRS); do \ rm -rf $$d || exit 1 ; \ done + rm -Rf .sdk if test -f pixman/config.log; then make -C pixman distclean; fi if test -f dtc/version_gen.h; then make $(DTC_MAKE_ARGS) clean; fi @@ -303,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" @@ -397,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/Makefile.objs b/Makefile.objs index f46a4cdd6a..2b6c1fe2a8 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -109,6 +109,7 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo # FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed # by libqemuutil.a. These should be moved to a separate .json schema. qga-obj-y = qga/ qapi-types.o qapi-visit.o +qga-vss-dll-obj-y = qga/ vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) @@ -120,6 +121,7 @@ nested-vars += \ stub-obj-y \ util-obj-y \ qga-obj-y \ + qga-vss-dll-obj-y \ block-obj-y \ common-obj-y dummy := $(call unnest-vars) 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 e47e1399bb..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 } @@ -1113,9 +1118,6 @@ int qemu_uuid_parse(const char *str, uint8_t *uuid) if (ret != 16) { return -1; } -#ifdef TARGET_I386 - smbios_add_field(1, offsetof(struct smbios_type_1, uuid), uuid, 16); -#endif return 0; } @@ -1134,12 +1136,10 @@ void do_acpitable_option(const QemuOpts *opts) #endif } -void do_smbios_option(const char *optarg) +void do_smbios_option(QemuOpts *opts) { #ifdef TARGET_I386 - if (smbios_entry_add(optarg) < 0) { - exit(1); - } + smbios_entry_add(opts); #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 } diff --git a/block-migration.c b/block-migration.c index f803f2006f..daf9ec1eab 100644 --- a/block-migration.c +++ b/block-migration.c @@ -336,8 +336,8 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs) bmds->completed_sectors = 0; bmds->shared_base = block_mig_state.shared_base; alloc_aio_bitmap(bmds); - drive_get_ref(drive_get_by_blockdev(bs)); bdrv_set_in_use(bs, 1); + bdrv_ref(bs); block_mig_state.total_sector_sum += sectors; @@ -575,7 +575,7 @@ static void blk_mig_cleanup(void) while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); bdrv_set_in_use(bmds->bs, 0); - drive_put_ref(drive_get_by_blockdev(bmds->bs)); + bdrv_unref(bmds->bs); g_free(bmds->aio_bitmap); g_free(bmds); } @@ -86,13 +86,6 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque); static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors); -static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors, - bool is_write, double elapsed_time, uint64_t *wait); -static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write, - double elapsed_time, uint64_t *wait); -static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors, - bool is_write, int64_t *wait); - static QTAILQ_HEAD(, BlockDriverState) bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states); @@ -123,69 +116,101 @@ int is_windows_drive(const char *filename) #endif /* throttling disk I/O limits */ -void bdrv_io_limits_disable(BlockDriverState *bs) +void bdrv_set_io_limits(BlockDriverState *bs, + ThrottleConfig *cfg) { - bs->io_limits_enabled = false; + int i; + + throttle_config(&bs->throttle_state, cfg); + + for (i = 0; i < 2; i++) { + qemu_co_enter_next(&bs->throttled_reqs[i]); + } +} + +/* this function drain all the throttled IOs */ +static bool bdrv_start_throttled_reqs(BlockDriverState *bs) +{ + bool drained = false; + bool enabled = bs->io_limits_enabled; + int i; - do {} while (qemu_co_enter_next(&bs->throttled_reqs)); + bs->io_limits_enabled = false; - if (bs->block_timer) { - timer_del(bs->block_timer); - timer_free(bs->block_timer); - bs->block_timer = NULL; + for (i = 0; i < 2; i++) { + while (qemu_co_enter_next(&bs->throttled_reqs[i])) { + drained = true; + } } - bs->slice_start = 0; - bs->slice_end = 0; + bs->io_limits_enabled = enabled; + + return drained; } -static void bdrv_block_timer(void *opaque) +void bdrv_io_limits_disable(BlockDriverState *bs) { - BlockDriverState *bs = opaque; + bs->io_limits_enabled = false; + + bdrv_start_throttled_reqs(bs); - qemu_co_enter_next(&bs->throttled_reqs); + throttle_destroy(&bs->throttle_state); } -void bdrv_io_limits_enable(BlockDriverState *bs) +static void bdrv_throttle_read_timer_cb(void *opaque) { - bs->block_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, bdrv_block_timer, bs); - bs->io_limits_enabled = true; + BlockDriverState *bs = opaque; + qemu_co_enter_next(&bs->throttled_reqs[0]); } -bool bdrv_io_limits_enabled(BlockDriverState *bs) +static void bdrv_throttle_write_timer_cb(void *opaque) { - BlockIOLimit *io_limits = &bs->io_limits; - return io_limits->bps[BLOCK_IO_LIMIT_READ] - || io_limits->bps[BLOCK_IO_LIMIT_WRITE] - || io_limits->bps[BLOCK_IO_LIMIT_TOTAL] - || io_limits->iops[BLOCK_IO_LIMIT_READ] - || io_limits->iops[BLOCK_IO_LIMIT_WRITE] - || io_limits->iops[BLOCK_IO_LIMIT_TOTAL]; + BlockDriverState *bs = opaque; + qemu_co_enter_next(&bs->throttled_reqs[1]); } +/* should be called before bdrv_set_io_limits if a limit is set */ +void bdrv_io_limits_enable(BlockDriverState *bs) +{ + assert(!bs->io_limits_enabled); + throttle_init(&bs->throttle_state, + QEMU_CLOCK_VIRTUAL, + bdrv_throttle_read_timer_cb, + bdrv_throttle_write_timer_cb, + bs); + bs->io_limits_enabled = true; +} + +/* This function makes an IO wait if needed + * + * @nb_sectors: the number of sectors of the IO + * @is_write: is the IO a write + */ static void bdrv_io_limits_intercept(BlockDriverState *bs, - bool is_write, int nb_sectors) + int nb_sectors, + bool is_write) { - int64_t wait_time = -1; + /* does this io must wait */ + bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write); - if (!qemu_co_queue_empty(&bs->throttled_reqs)) { - qemu_co_queue_wait(&bs->throttled_reqs); + /* if must wait or any request of this type throttled queue the IO */ + if (must_wait || + !qemu_co_queue_empty(&bs->throttled_reqs[is_write])) { + qemu_co_queue_wait(&bs->throttled_reqs[is_write]); } - /* In fact, we hope to keep each request's timing, in FIFO mode. The next - * throttled requests will not be dequeued until the current request is - * allowed to be serviced. So if the current request still exceeds the - * limits, it will be inserted to the head. All requests followed it will - * be still in throttled_reqs queue. - */ + /* the IO will be executed, do the accounting */ + throttle_account(&bs->throttle_state, + is_write, + nb_sectors * BDRV_SECTOR_SIZE); - while (bdrv_exceed_io_limits(bs, nb_sectors, is_write, &wait_time)) { - timer_mod(bs->block_timer, - wait_time + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - qemu_co_queue_wait_insert_head(&bs->throttled_reqs); + /* if the next request must wait -> do nothing */ + if (throttle_schedule_timer(&bs->throttle_state, is_write)) { + return; } - qemu_co_queue_next(&bs->throttled_reqs); + /* else queue next request for execution */ + qemu_co_queue_next(&bs->throttled_reqs[is_write]); } /* check if the path starts with "<protocol>:" */ @@ -305,7 +330,9 @@ BlockDriverState *bdrv_new(const char *device_name) bdrv_iostatus_disable(bs); notifier_list_init(&bs->close_notifiers); notifier_with_return_list_init(&bs->before_write_notifiers); - qemu_co_queue_init(&bs->throttled_reqs); + qemu_co_queue_init(&bs->throttled_reqs[0]); + qemu_co_queue_init(&bs->throttled_reqs[1]); + bs->refcnt = 1; return bs; } @@ -367,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; @@ -388,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; } @@ -407,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; } /* @@ -525,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; @@ -536,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; @@ -544,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; } @@ -560,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; @@ -679,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); @@ -711,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; } @@ -733,33 +792,40 @@ 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 (bs->filename[0]) { + error_setg_errno(errp, -ret, "Could not open '%s'", bs->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; } #ifndef _WIN32 if (bs->is_temporary) { - assert(filename != NULL); - unlink(filename); + assert(bs->filename[0] != '\0'); + unlink(bs->filename); } #endif return 0; @@ -781,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 */ @@ -805,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; } @@ -815,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; } @@ -876,7 +943,7 @@ fail: if (!bs->drv) { QDECREF(bs->options); } - bdrv_delete(bs); + bdrv_unref(bs); return ret; } @@ -888,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); @@ -910,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); @@ -925,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_delete(bs->backing_hd); + 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, ...) * @@ -963,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. */ @@ -971,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) { @@ -989,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; } @@ -1000,17 +1054,18 @@ 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_delete(bs1); + bdrv_unref(bs1); goto fail; } total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK; - bdrv_delete(bs1); + bdrv_unref(bs1); ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not get temporary filename"); goto fail; } @@ -1019,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; } @@ -1035,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; } @@ -1051,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; } @@ -1067,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) { @@ -1075,13 +1136,13 @@ 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; } if (bs->file != file) { - bdrv_delete(file); + bdrv_unref(file); file = NULL; } @@ -1089,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; } @@ -1099,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; @@ -1112,16 +1173,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, bdrv_dev_change_media_cb(bs, true); } - /* throttling disk I/O limits */ - if (bs->io_limits_enabled) { - bdrv_io_limits_enable(bs); - } - return 0; unlink_and_fail: if (file != NULL) { - bdrv_delete(file); + bdrv_unref(file); } if (bs->is_temporary) { unlink(filename); @@ -1130,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; } @@ -1382,7 +1444,7 @@ void bdrv_close(BlockDriverState *bs) if (bs->drv) { if (bs->backing_hd) { - bdrv_delete(bs->backing_hd); + bdrv_unref(bs->backing_hd); bs->backing_hd = NULL; } bs->drv->bdrv_close(bs); @@ -1407,7 +1469,7 @@ void bdrv_close(BlockDriverState *bs) bs->options = NULL; if (bs->file != NULL) { - bdrv_delete(bs->file); + bdrv_unref(bs->file); bs->file = NULL; } } @@ -1435,7 +1497,10 @@ static bool bdrv_requests_pending(BlockDriverState *bs) if (!QLIST_EMPTY(&bs->tracked_requests)) { return true; } - if (!qemu_co_queue_empty(&bs->throttled_reqs)) { + if (!qemu_co_queue_empty(&bs->throttled_reqs[0])) { + return true; + } + if (!qemu_co_queue_empty(&bs->throttled_reqs[1])) { return true; } if (bs->file && bdrv_requests_pending(bs->file)) { @@ -1481,7 +1546,7 @@ void bdrv_drain_all(void) * a busy wait. */ QTAILQ_FOREACH(bs, &bdrv_states, list) { - while (qemu_co_enter_next(&bs->throttled_reqs)) { + if (bdrv_start_throttled_reqs(bs)) { busy = true; } } @@ -1523,13 +1588,12 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, bs_dest->enable_write_cache = bs_src->enable_write_cache; - /* i/o timing parameters */ - bs_dest->slice_start = bs_src->slice_start; - bs_dest->slice_end = bs_src->slice_end; - bs_dest->slice_submitted = bs_src->slice_submitted; - bs_dest->io_limits = bs_src->io_limits; - bs_dest->throttled_reqs = bs_src->throttled_reqs; - bs_dest->block_timer = bs_src->block_timer; + /* i/o throttled req */ + memcpy(&bs_dest->throttle_state, + &bs_src->throttle_state, + sizeof(ThrottleState)); + bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0]; + bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1]; bs_dest->io_limits_enabled = bs_src->io_limits_enabled; /* r/w error */ @@ -1543,6 +1607,9 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, /* dirty bitmap */ bs_dest->dirty_bitmap = bs_src->dirty_bitmap; + /* reference count */ + bs_dest->refcnt = bs_src->refcnt; + /* job */ bs_dest->in_use = bs_src->in_use; bs_dest->job = bs_src->job; @@ -1576,7 +1643,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) assert(bs_new->dev == NULL); assert(bs_new->in_use == 0); assert(bs_new->io_limits_enabled == false); - assert(bs_new->block_timer == NULL); + assert(!throttle_have_timer(&bs_new->throttle_state)); tmp = *bs_new; *bs_new = *bs_old; @@ -1595,7 +1662,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) assert(bs_new->job == NULL); assert(bs_new->in_use == 0); assert(bs_new->io_limits_enabled == false); - assert(bs_new->block_timer == NULL); + assert(!throttle_have_timer(&bs_new->throttle_state)); bdrv_rebind(bs_new); bdrv_rebind(bs_old); @@ -1626,11 +1693,12 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) bs_new->drv ? bs_new->drv->format_name : ""); } -void bdrv_delete(BlockDriverState *bs) +static void bdrv_delete(BlockDriverState *bs) { assert(!bs->dev); assert(!bs->job); assert(!bs->in_use); + assert(!bs->refcnt); bdrv_close(bs); @@ -1829,8 +1897,11 @@ int bdrv_commit(BlockDriverState *bs) buf = g_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE); for (sector = 0; sector < total_sectors; sector += n) { - if (bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) { - + ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n); + if (ret < 0) { + goto ro_cleanup; + } + if (ret) { if (bdrv_read(bs, sector, buf, n) != 0) { ret = -EIO; goto ro_cleanup; @@ -2146,7 +2217,7 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { /* so that bdrv_close() does not recursively close the chain */ intermediate_state->bs->backing_hd = NULL; - bdrv_delete(intermediate_state->bs); + bdrv_unref(intermediate_state->bs); } ret = 0; @@ -2538,11 +2609,6 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, return -EIO; } - /* throttling disk read I/O */ - if (bs->io_limits_enabled) { - bdrv_io_limits_intercept(bs, false, nb_sectors); - } - if (bs->copy_on_read) { flags |= BDRV_REQ_COPY_ON_READ; } @@ -2554,12 +2620,17 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, wait_for_overlapping_requests(bs, sector_num, nb_sectors); } + /* throttling disk I/O */ + if (bs->io_limits_enabled) { + bdrv_io_limits_intercept(bs, nb_sectors, false); + } + tracked_request_begin(&req, bs, sector_num, nb_sectors, false); if (flags & BDRV_REQ_COPY_ON_READ) { int pnum; - ret = bdrv_co_is_allocated(bs, sector_num, nb_sectors, &pnum); + ret = bdrv_is_allocated(bs, sector_num, nb_sectors, &pnum); if (ret < 0) { goto out; } @@ -2582,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, @@ -2679,15 +2750,15 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, return -EIO; } - /* throttling disk write I/O */ - if (bs->io_limits_enabled) { - bdrv_io_limits_intercept(bs, true, nb_sectors); - } - if (bs->copy_on_read_in_flight) { wait_for_overlapping_requests(bs, sector_num, nb_sectors); } + /* throttling disk I/O */ + if (bs->io_limits_enabled) { + bdrv_io_limits_intercept(bs, nb_sectors, true); + } + tracked_request_begin(&req, bs, sector_num, nb_sectors, true); ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req); @@ -2711,6 +2782,9 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, if (bs->wr_highest_sector < sector_num + nb_sectors - 1) { bs->wr_highest_sector = sector_num + nb_sectors - 1; } + if (bs->growable && ret >= 0) { + bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors); + } tracked_request_end(&req); @@ -2785,7 +2859,7 @@ int64_t bdrv_getlength(BlockDriverState *bs) if (!drv) return -ENOMEDIUM; - if (bs->growable || bdrv_dev_has_removable_media(bs)) { + if (bdrv_dev_has_removable_media(bs)) { if (drv->bdrv_getlength) { return drv->bdrv_getlength(bs); } @@ -2805,14 +2879,6 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) *nb_sectors_ptr = length; } -/* throttling disk io limits */ -void bdrv_set_io_limits(BlockDriverState *bs, - BlockIOLimit *io_limits) -{ - bs->io_limits = *io_limits; - bs->io_limits_enabled = bdrv_io_limits_enabled(bs); -} - void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, BlockdevOnError on_write_error) { @@ -3005,6 +3071,11 @@ int bdrv_has_zero_init(BlockDriverState *bs) { assert(bs->drv); + /* If BS is a copy on write image, it is initialized to + the contents of the base image, which may not be zeroes. */ + if (bs->backing_hd) { + return 0; + } if (bs->drv->bdrv_has_zero_init) { return bs->drv->bdrv_has_zero_init(bs); } @@ -3013,15 +3084,15 @@ int bdrv_has_zero_init(BlockDriverState *bs) return 0; } -typedef struct BdrvCoIsAllocatedData { +typedef struct BdrvCoGetBlockStatusData { BlockDriverState *bs; BlockDriverState *base; int64_t sector_num; int nb_sectors; int *pnum; - int ret; + int64_t ret; bool done; -} BdrvCoIsAllocatedData; +} BdrvCoGetBlockStatusData; /* * Returns true iff the specified sector is present in the disk image. Drivers @@ -3038,12 +3109,20 @@ typedef struct BdrvCoIsAllocatedData { * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes * beyond the end of the disk image it will be clamped. */ -int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) { + int64_t length; int64_t n; + int64_t ret, ret2; + + length = bdrv_getlength(bs); + if (length < 0) { + return length; + } - if (sector_num >= bs->total_sectors) { + if (sector_num >= (length >> BDRV_SECTOR_BITS)) { *pnum = 0; return 0; } @@ -3053,35 +3132,70 @@ int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num, nb_sectors = n; } - if (!bs->drv->bdrv_co_is_allocated) { + if (!bs->drv->bdrv_co_get_block_status) { *pnum = nb_sectors; - return 1; + ret = BDRV_BLOCK_DATA; + if (bs->drv->protocol_name) { + ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE); + } + return ret; } - return bs->drv->bdrv_co_is_allocated(bs, sector_num, nb_sectors, pnum); + 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 if (bs->backing_hd) { + BlockDriverState *bs2 = bs->backing_hd; + int64_t length2 = bdrv_getlength(bs2); + if (length2 >= 0 && sector_num >= (length2 >> BDRV_SECTOR_BITS)) { + ret |= BDRV_BLOCK_ZERO; + } + } + } + + if (bs->file && + (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && + (ret & BDRV_BLOCK_OFFSET_VALID)) { + ret2 = bdrv_co_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS, + *pnum, pnum); + if (ret2 >= 0) { + /* Ignore errors. This is just providing extra information, it + * is useful but not necessary. + */ + ret |= (ret2 & BDRV_BLOCK_ZERO); + } + } + + return ret; } -/* Coroutine wrapper for bdrv_is_allocated() */ -static void coroutine_fn bdrv_is_allocated_co_entry(void *opaque) +/* Coroutine wrapper for bdrv_get_block_status() */ +static void coroutine_fn bdrv_get_block_status_co_entry(void *opaque) { - BdrvCoIsAllocatedData *data = opaque; + BdrvCoGetBlockStatusData *data = opaque; BlockDriverState *bs = data->bs; - data->ret = bdrv_co_is_allocated(bs, data->sector_num, data->nb_sectors, - data->pnum); + data->ret = bdrv_co_get_block_status(bs, data->sector_num, data->nb_sectors, + data->pnum); data->done = true; } /* - * Synchronous wrapper around bdrv_co_is_allocated(). + * Synchronous wrapper around bdrv_co_get_block_status(). * - * See bdrv_co_is_allocated() for details. + * See bdrv_co_get_block_status() for details. */ -int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - int *pnum) +int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) { Coroutine *co; - BdrvCoIsAllocatedData data = { + BdrvCoGetBlockStatusData data = { .bs = bs, .sector_num = sector_num, .nb_sectors = nb_sectors, @@ -3089,14 +3203,31 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, .done = false, }; - co = qemu_coroutine_create(bdrv_is_allocated_co_entry); - qemu_coroutine_enter(co, &data); - while (!data.done) { - qemu_aio_wait(); + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_get_block_status_co_entry(&data); + } else { + co = qemu_coroutine_create(bdrv_get_block_status_co_entry); + qemu_coroutine_enter(co, &data); + while (!data.done) { + qemu_aio_wait(); + } } return data.ret; } +int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum); + if (ret < 0) { + return ret; + } + return + (ret & BDRV_BLOCK_DATA) || + ((ret & BDRV_BLOCK_ZERO) && !bdrv_has_zero_init(bs)); +} + /* * Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP] * @@ -3109,10 +3240,10 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, * allocated/unallocated state. * */ -int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top, - BlockDriverState *base, - int64_t sector_num, - int nb_sectors, int *pnum) +int bdrv_is_allocated_above(BlockDriverState *top, + BlockDriverState *base, + int64_t sector_num, + int nb_sectors, int *pnum) { BlockDriverState *intermediate; int ret, n = nb_sectors; @@ -3120,8 +3251,8 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top, intermediate = top; while (intermediate && intermediate != base) { int pnum_inter; - ret = bdrv_co_is_allocated(intermediate, sector_num, nb_sectors, - &pnum_inter); + ret = bdrv_is_allocated(intermediate, sector_num, nb_sectors, + &pnum_inter); if (ret < 0) { return ret; } else if (ret) { @@ -3148,44 +3279,6 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top, return 0; } -/* Coroutine wrapper for bdrv_is_allocated_above() */ -static void coroutine_fn bdrv_is_allocated_above_co_entry(void *opaque) -{ - BdrvCoIsAllocatedData *data = opaque; - BlockDriverState *top = data->bs; - BlockDriverState *base = data->base; - - data->ret = bdrv_co_is_allocated_above(top, base, data->sector_num, - data->nb_sectors, data->pnum); - data->done = true; -} - -/* - * Synchronous wrapper around bdrv_co_is_allocated_above(). - * - * See bdrv_co_is_allocated_above() for details. - */ -int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, - int64_t sector_num, int nb_sectors, int *pnum) -{ - Coroutine *co; - BdrvCoIsAllocatedData data = { - .bs = top, - .base = base, - .sector_num = sector_num, - .nb_sectors = nb_sectors, - .pnum = pnum, - .done = false, - }; - - co = qemu_coroutine_create(bdrv_is_allocated_above_co_entry); - qemu_coroutine_enter(co, &data); - while (!data.done) { - qemu_aio_wait(); - } - return data.ret; -} - const char *bdrv_get_encrypted_filename(BlockDriverState *bs) { if (bs->backing_hd && bs->backing_hd->encrypted) @@ -3622,169 +3715,6 @@ void bdrv_aio_cancel(BlockDriverAIOCB *acb) acb->aiocb_info->cancel(acb); } -/* block I/O throttling */ -static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors, - bool is_write, double elapsed_time, uint64_t *wait) -{ - uint64_t bps_limit = 0; - uint64_t extension; - double bytes_limit, bytes_base, bytes_res; - double slice_time, wait_time; - - if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) { - bps_limit = bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]; - } else if (bs->io_limits.bps[is_write]) { - bps_limit = bs->io_limits.bps[is_write]; - } else { - if (wait) { - *wait = 0; - } - - return false; - } - - slice_time = bs->slice_end - bs->slice_start; - slice_time /= (NANOSECONDS_PER_SECOND); - bytes_limit = bps_limit * slice_time; - bytes_base = bs->slice_submitted.bytes[is_write]; - if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) { - bytes_base += bs->slice_submitted.bytes[!is_write]; - } - - /* bytes_base: the bytes of data which have been read/written; and - * it is obtained from the history statistic info. - * bytes_res: the remaining bytes of data which need to be read/written. - * (bytes_base + bytes_res) / bps_limit: used to calcuate - * the total time for completing reading/writting all data. - */ - bytes_res = (unsigned) nb_sectors * BDRV_SECTOR_SIZE; - - if (bytes_base + bytes_res <= bytes_limit) { - if (wait) { - *wait = 0; - } - - return false; - } - - /* Calc approx time to dispatch */ - wait_time = (bytes_base + bytes_res) / bps_limit - elapsed_time; - - /* When the I/O rate at runtime exceeds the limits, - * bs->slice_end need to be extended in order that the current statistic - * info can be kept until the timer fire, so it is increased and tuned - * based on the result of experiment. - */ - extension = wait_time * NANOSECONDS_PER_SECOND; - extension = DIV_ROUND_UP(extension, BLOCK_IO_SLICE_TIME) * - BLOCK_IO_SLICE_TIME; - bs->slice_end += extension; - if (wait) { - *wait = wait_time * NANOSECONDS_PER_SECOND; - } - - return true; -} - -static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write, - double elapsed_time, uint64_t *wait) -{ - uint64_t iops_limit = 0; - double ios_limit, ios_base; - double slice_time, wait_time; - - if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) { - iops_limit = bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]; - } else if (bs->io_limits.iops[is_write]) { - iops_limit = bs->io_limits.iops[is_write]; - } else { - if (wait) { - *wait = 0; - } - - return false; - } - - slice_time = bs->slice_end - bs->slice_start; - slice_time /= (NANOSECONDS_PER_SECOND); - ios_limit = iops_limit * slice_time; - ios_base = bs->slice_submitted.ios[is_write]; - if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) { - ios_base += bs->slice_submitted.ios[!is_write]; - } - - if (ios_base + 1 <= ios_limit) { - if (wait) { - *wait = 0; - } - - return false; - } - - /* Calc approx time to dispatch, in seconds */ - wait_time = (ios_base + 1) / iops_limit; - if (wait_time > elapsed_time) { - wait_time = wait_time - elapsed_time; - } else { - wait_time = 0; - } - - /* Exceeded current slice, extend it by another slice time */ - bs->slice_end += BLOCK_IO_SLICE_TIME; - if (wait) { - *wait = wait_time * NANOSECONDS_PER_SECOND; - } - - return true; -} - -static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors, - bool is_write, int64_t *wait) -{ - int64_t now, max_wait; - uint64_t bps_wait = 0, iops_wait = 0; - double elapsed_time; - int bps_ret, iops_ret; - - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - if (now > bs->slice_end) { - bs->slice_start = now; - bs->slice_end = now + BLOCK_IO_SLICE_TIME; - memset(&bs->slice_submitted, 0, sizeof(bs->slice_submitted)); - } - - elapsed_time = now - bs->slice_start; - elapsed_time /= (NANOSECONDS_PER_SECOND); - - bps_ret = bdrv_exceed_bps_limits(bs, nb_sectors, - is_write, elapsed_time, &bps_wait); - iops_ret = bdrv_exceed_iops_limits(bs, is_write, - elapsed_time, &iops_wait); - if (bps_ret || iops_ret) { - max_wait = bps_wait > iops_wait ? bps_wait : iops_wait; - if (wait) { - *wait = max_wait; - } - - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - if (bs->slice_end < now + max_wait) { - bs->slice_end = now + max_wait; - } - - return true; - } - - if (wait) { - *wait = 0; - } - - bs->slice_submitted.bytes[is_write] += (int64_t)nb_sectors * - BDRV_SECTOR_SIZE; - bs->slice_submitted.ios[is_write]++; - - return false; -} - /**************************************************************/ /* async block device emulation */ @@ -4445,6 +4375,23 @@ int64_t bdrv_get_dirty_count(BlockDriverState *bs) } } +/* Get a reference to bs */ +void bdrv_ref(BlockDriverState *bs) +{ + bs->refcnt++; +} + +/* Release a previously grabbed reference to bs. + * If after releasing, reference count is zero, the BlockDriverState is + * deleted. */ +void bdrv_unref(BlockDriverState *bs) +{ + assert(bs->refcnt > 0); + if (--bs->refcnt == 0) { + bdrv_delete(bs); + } +} + void bdrv_set_in_use(BlockDriverState *bs, int in_use) { assert(bs->in_use != in_use); @@ -4527,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 */ @@ -4613,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); @@ -4635,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: @@ -4658,7 +4606,10 @@ out: free_option_parameters(param); if (bs) { - bdrv_delete(bs); + bdrv_unref(bs); + } + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); } } @@ -4673,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/backup.c b/block/backup.c index 23c7264488..04c4b5c263 100644 --- a/block/backup.c +++ b/block/backup.c @@ -289,14 +289,14 @@ static void coroutine_fn backup_run(void *opaque) * backing file. */ for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) { - /* bdrv_co_is_allocated() only returns true/false based + /* bdrv_is_allocated() only returns true/false based * on the first set of sectors it comes across that * are are all in the same state. * For that reason we must verify each sector in the * backup cluster length. We end up copying more than * needed but at some point that is always the case. */ alloced = - bdrv_co_is_allocated(bs, + bdrv_is_allocated(bs, start * BACKUP_SECTORS_PER_CLUSTER + i, BACKUP_SECTORS_PER_CLUSTER - i, &n); i += n; @@ -338,7 +338,7 @@ static void coroutine_fn backup_run(void *opaque) hbitmap_free(job->bitmap); bdrv_iostatus_disable(target); - bdrv_delete(target); + bdrv_unref(target); block_job_completed(&job->common, ret); } 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 1d58cc3932..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,9 +156,11 @@ 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) { - bdrv_delete(s->test_file); + qerror_report_err(local_err); + error_free(local_err); + bdrv_unref(s->test_file); s->test_file = NULL; goto fail; } @@ -169,7 +174,7 @@ static void blkverify_close(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; - bdrv_delete(s->test_file); + bdrv_unref(s->test_file); s->test_file = NULL; } 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/commit.c b/block/commit.c index 51a1ab3678..ac4b7ccbc9 100644 --- a/block/commit.c +++ b/block/commit.c @@ -108,9 +108,9 @@ wait: break; } /* Copy if allocated above the base */ - ret = bdrv_co_is_allocated_above(top, base, sector_num, - COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE, - &n); + ret = bdrv_is_allocated_above(top, base, sector_num, + COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE, + &n); copy = (ret == 1); trace_commit_one_iteration(s, sector_num, n, ret); if (copy) { diff --git a/block/cow.c b/block/cow.c index 1cc2e89c7c..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; @@ -106,7 +107,7 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags) * XXX(hch): right now these functions are extremely inefficient. * We should just read the whole bitmap we'll need in one go instead. */ -static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) +static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum, bool *first) { uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; uint8_t bitmap; @@ -117,27 +118,52 @@ static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) return ret; } + if (bitmap & (1 << (bitnum % 8))) { + return 0; + } + + if (*first) { + ret = bdrv_flush(bs->file); + if (ret < 0) { + return ret; + } + *first = false; + } + bitmap |= (1 << (bitnum % 8)); - ret = bdrv_pwrite_sync(bs->file, offset, &bitmap, sizeof(bitmap)); + ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap)); if (ret < 0) { return ret; } return 0; } -static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum) +#define BITS_PER_BITMAP_SECTOR (512 * 8) + +/* Cannot use bitmap.c on big-endian machines. */ +static int cow_test_bit(int64_t bitnum, const uint8_t *bitmap) { - uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; - uint8_t bitmap; - int ret; + return (bitmap[bitnum / 8] & (1 << (bitnum & 7))) != 0; +} - ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap)); - if (ret < 0) { - return ret; +static int cow_find_streak(const uint8_t *bitmap, int value, int start, int nb_sectors) +{ + int streak_value = value ? 0xFF : 0; + int last = MIN(start + nb_sectors, BITS_PER_BITMAP_SECTOR); + int bitnum = start; + while (bitnum < last) { + if ((bitnum & 7) == 0 && bitmap[bitnum / 8] == streak_value) { + bitnum += 8; + continue; + } + if (cow_test_bit(bitnum, bitmap) == value) { + bitnum++; + continue; + } + break; } - - return !!(bitmap & (1 << (bitnum % 8))); + return MIN(bitnum, last) - start; } /* Return true if first block has been changed (ie. current version is @@ -146,24 +172,33 @@ static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum) static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *num_same) { + int64_t bitnum = sector_num + sizeof(struct cow_header_v2) * 8; + uint64_t offset = (bitnum / 8) & -BDRV_SECTOR_SIZE; + uint8_t bitmap[BDRV_SECTOR_SIZE]; + int ret; int changed; - if (nb_sectors == 0) { - *num_same = nb_sectors; - return 0; + ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap)); + if (ret < 0) { + return ret; } - changed = is_bit_set(bs, sector_num); - if (changed < 0) { - return 0; /* XXX: how to return I/O errors? */ - } + bitnum &= BITS_PER_BITMAP_SECTOR - 1; + changed = cow_test_bit(bitnum, bitmap); + *num_same = cow_find_streak(bitmap, changed, bitnum, nb_sectors); + return changed; +} - for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { - if (is_bit_set(bs, sector_num + *num_same) != changed) - break; +static int64_t coroutine_fn cow_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *num_same) +{ + BDRVCowState *s = bs->opaque; + int ret = cow_co_is_allocated(bs, sector_num, nb_sectors, num_same); + int64_t offset = s->cow_sectors_offset + (sector_num << BDRV_SECTOR_BITS); + if (ret < 0) { + return ret; } - - return changed; + return (ret ? BDRV_BLOCK_DATA : 0) | offset | BDRV_BLOCK_OFFSET_VALID; } static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num, @@ -171,9 +206,10 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num, { int error = 0; int i; + bool first = true; for (i = 0; i < nb_sectors; i++) { - error = cow_set_bit(bs, sector_num + i); + error = cow_set_bit(bs, sector_num + i, &first); if (error) { break; } @@ -189,7 +225,11 @@ static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num, int ret, n; while (nb_sectors > 0) { - if (bdrv_co_is_allocated(bs, sector_num, nb_sectors, &n)) { + ret = cow_co_is_allocated(bs, sector_num, nb_sectors, &n); + if (ret < 0) { + return ret; + } + if (ret) { ret = bdrv_pread(bs->file, s->cow_sectors_offset + sector_num * 512, buf, n * 512); @@ -255,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; @@ -274,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; } @@ -314,7 +360,7 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) } exit: - bdrv_delete(cow_bs); + bdrv_unref(cow_bs); return ret; } @@ -344,7 +390,7 @@ static BlockDriver bdrv_cow = { .bdrv_read = cow_co_read, .bdrv_write = cow_co_write, - .bdrv_co_is_allocated = cow_co_is_allocated, + .bdrv_co_get_block_status = cow_co_get_block_status, .create_options = cow_create_options, }; 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 2bbee1f6e5..a2a961e163 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(LIBISCSI_FEATURE_IOVECTOR) + +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 /* LIBISCSI_FEATURE_IOVECTOR */ + +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,15 +1452,16 @@ 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; - BlockDriverState bs; + BlockDriverState *bs; IscsiLun *iscsilun = NULL; QDict *bs_options; - memset(&bs, 0, sizeof(BlockDriverState)); + bs = bdrv_new(""); /* Read out options */ while (options && options->name) { @@ -1255,12 +1471,12 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) options++; } - bs.opaque = g_malloc0(sizeof(struct IscsiLun)); - iscsilun = bs.opaque; + bs->opaque = g_malloc0(sizeof(struct IscsiLun)); + iscsilun = bs->opaque; 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) { @@ -1274,7 +1490,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) ret = -ENODEV; goto out; } - if (bs.total_sectors < total_size) { + if (bs->total_sectors < total_size) { ret = -ENOSPC; goto out; } @@ -1284,7 +1500,9 @@ out: if (iscsilun->iscsi != NULL) { iscsi_destroy_context(iscsilun->iscsi); } - g_free(bs.opaque); + g_free(bs->opaque); + bs->opaque = NULL; + bdrv_unref(bs); return ret; } @@ -1302,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, @@ -1310,11 +1529,15 @@ static BlockDriver bdrv_iscsi = { .bdrv_getlength = iscsi_getlength, .bdrv_truncate = iscsi_truncate, +#if defined(LIBISCSI_FEATURE_IOVECTOR) + .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 86de4582b4..6e7a274e43 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -338,8 +338,8 @@ static void coroutine_fn mirror_run(void *opaque) base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd; for (sector_num = 0; sector_num < end; ) { int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1; - ret = bdrv_co_is_allocated_above(bs, base, - sector_num, next - sector_num, &n); + ret = bdrv_is_allocated_above(bs, base, + sector_num, next - sector_num, &n); if (ret < 0) { goto immediate_exit; @@ -480,7 +480,7 @@ immediate_exit: bdrv_swap(s->target, s->common.bs); } bdrv_close(s->target); - bdrv_delete(s->target); + bdrv_unref(s->target); block_job_completed(&s->common, ret); } @@ -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/qapi.c b/block/qapi.c index a4bc4113b7..782051c65d 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -223,18 +223,44 @@ void bdrv_query_info(BlockDriverState *bs, info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs); if (bs->io_limits_enabled) { - info->inserted->bps = - bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]; - info->inserted->bps_rd = - bs->io_limits.bps[BLOCK_IO_LIMIT_READ]; - info->inserted->bps_wr = - bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE]; - info->inserted->iops = - bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]; - info->inserted->iops_rd = - bs->io_limits.iops[BLOCK_IO_LIMIT_READ]; - info->inserted->iops_wr = - bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE]; + ThrottleConfig cfg; + throttle_get_config(&bs->throttle_state, &cfg); + info->inserted->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; + info->inserted->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; + info->inserted->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; + + info->inserted->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg; + info->inserted->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg; + info->inserted->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg; + + info->inserted->has_bps_max = + cfg.buckets[THROTTLE_BPS_TOTAL].max; + info->inserted->bps_max = + cfg.buckets[THROTTLE_BPS_TOTAL].max; + info->inserted->has_bps_rd_max = + cfg.buckets[THROTTLE_BPS_READ].max; + info->inserted->bps_rd_max = + cfg.buckets[THROTTLE_BPS_READ].max; + info->inserted->has_bps_wr_max = + cfg.buckets[THROTTLE_BPS_WRITE].max; + info->inserted->bps_wr_max = + cfg.buckets[THROTTLE_BPS_WRITE].max; + + info->inserted->has_iops_max = + cfg.buckets[THROTTLE_OPS_TOTAL].max; + info->inserted->iops_max = + cfg.buckets[THROTTLE_OPS_TOTAL].max; + info->inserted->has_iops_rd_max = + cfg.buckets[THROTTLE_OPS_READ].max; + info->inserted->iops_rd_max = + cfg.buckets[THROTTLE_OPS_READ].max; + info->inserted->has_iops_wr_max = + cfg.buckets[THROTTLE_OPS_WRITE].max; + info->inserted->iops_wr_max = + cfg.buckets[THROTTLE_OPS_WRITE].max; + + info->inserted->has_iops_size = cfg.op_size; + info->inserted->iops_size = cfg.op_size; } bs0 = bs; diff --git a/block/qcow.c b/block/qcow.c index 5239bd68f1..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; @@ -395,7 +396,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, return cluster_offset; } -static int coroutine_fn qcow_co_is_allocated(BlockDriverState *bs, +static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { BDRVQcowState *s = bs->opaque; @@ -410,7 +411,14 @@ static int coroutine_fn qcow_co_is_allocated(BlockDriverState *bs, if (n > nb_sectors) n = nb_sectors; *pnum = n; - return (cluster_offset != 0); + if (!cluster_offset) { + return 0; + } + if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) { + return BDRV_BLOCK_DATA; + } + cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset; } static int decompress_buffer(uint8_t *out_buf, int out_buf_size, @@ -651,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; @@ -659,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; @@ -674,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; } @@ -751,7 +765,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) g_free(tmp); ret = 0; exit: - bdrv_delete(qcow_bs); + bdrv_unref(qcow_bs); return ret; } @@ -896,7 +910,7 @@ static BlockDriver bdrv_qcow = { .bdrv_co_readv = qcow_co_readv, .bdrv_co_writev = qcow_co_writev, - .bdrv_co_is_allocated = qcow_co_is_allocated, + .bdrv_co_get_block_status = qcow_co_get_block_status, .bdrv_set_key = qcow_set_key, .bdrv_make_empty = qcow_make_empty, 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..0fd26bb4cc 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -35,6 +35,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, BDRVQcowState *s = bs->opaque; int new_l1_size2, ret, i; uint64_t *new_l1_table; + int64_t old_l1_table_offset, old_l1_size; int64_t new_l1_table_offset, new_l1_size; uint8_t data[12]; @@ -106,11 +107,13 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, goto fail; } g_free(s->l1_table); - qcow2_free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t), - QCOW2_DISCARD_OTHER); + old_l1_table_offset = s->l1_table_offset; s->l1_table_offset = new_l1_table_offset; s->l1_table = new_l1_table; + old_l1_size = s->l1_size; s->l1_size = new_l1_size; + qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * sizeof(uint64_t), + QCOW2_DISCARD_OTHER); return 0; fail: g_free(new_l1_table); @@ -188,7 +191,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 +203,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 +217,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,8 +269,14 @@ 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; + if (l2_offset > 0) { + qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t), + QCOW2_DISCARD_ALWAYS); + } return ret; } @@ -278,23 +288,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 +500,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 +511,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 +727,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 +941,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 +1350,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 +1379,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 +1391,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 +1414,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 +1509,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..2d67885850 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); } @@ -1034,7 +1034,6 @@ static void inc_refcounts(BlockDriverState *bs, /* Flags for check_refcounts_l1() and check_refcounts_l2() */ enum { - CHECK_OFLAG_COPIED = 0x1, /* check QCOW_OFLAG_COPIED matches refcount */ CHECK_FRAG_INFO = 0x2, /* update BlockFragInfo counters */ }; @@ -1481,8 +1480,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* current L1 table */ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, - s->l1_table_offset, s->l1_size, - CHECK_OFLAG_COPIED | CHECK_FRAG_INFO); + s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO); if (ret < 0) { goto fail; } @@ -1733,8 +1731,8 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset, } for (j = 0; j < l1_sz; j++) { - if ((l1[j] & L1E_OFFSET_MASK) && - overlaps_with(l1[j] & L1E_OFFSET_MASK, s->cluster_size)) { + uint64_t l2_ofs = be64_to_cpu(l1[j]) & L1E_OFFSET_MASK; + if (l2_ofs && overlaps_with(l2_ofs, s->cluster_size)) { g_free(l1); return QCOW2_OL_INACTIVE_L2; } 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 4bc679a155..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; } @@ -688,24 +714,34 @@ static int qcow2_reopen_prepare(BDRVReopenState *state, return 0; } -static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs, +static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { BDRVQcowState *s = bs->opaque; uint64_t cluster_offset; - int ret; + int index_in_cluster, ret; + int64_t status = 0; *pnum = nb_sectors; - /* FIXME We can get errors here, but the bdrv_co_is_allocated interface - * can't pass them on today */ qemu_co_mutex_lock(&s->lock); ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { - *pnum = 0; + return ret; } - return (cluster_offset != 0) || (ret == QCOW2_CLUSTER_ZERO); + if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED && + !s->crypt_method) { + index_in_cluster = sector_num & (s->cluster_sectors - 1); + cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); + status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; + } + if (ret == QCOW2_CLUSTER_ZERO) { + status |= BDRV_BLOCK_ZERO; + } else if (ret != QCOW2_CLUSTER_UNALLOCATED) { + status |= BDRV_BLOCK_DATA; + } + return status; } /* handle reading after the end of the backing file */ @@ -1049,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); @@ -1133,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), }; @@ -1322,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; @@ -1330,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; } @@ -1351,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; } @@ -1389,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; } @@ -1398,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; } @@ -1411,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) { @@ -1428,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; } @@ -1435,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; } } @@ -1446,17 +1493,19 @@ 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; } } ret = 0; out: - bdrv_delete(bs); + bdrv_unref(bs); 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; @@ -1465,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) { @@ -1486,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)) { @@ -1498,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)) { @@ -1509,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) @@ -1572,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; } @@ -1747,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; @@ -1814,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, @@ -1868,7 +2111,7 @@ static BlockDriver bdrv_qcow2 = { .bdrv_reopen_prepare = qcow2_reopen_prepare, .bdrv_create = qcow2_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_co_is_allocated = qcow2_co_is_allocated, + .bdrv_co_get_block_status = qcow2_co_get_block_status, .bdrv_set_key = qcow2_set_key, .bdrv_make_empty = qcow2_make_empty, @@ -1897,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 cc904c4834..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; } @@ -599,11 +606,12 @@ static int qed_create(const char *filename, uint32_t cluster_size, ret = 0; /* success */ out: g_free(l1_table); - bdrv_delete(bs); + bdrv_unref(bs); 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; @@ -652,45 +660,66 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options) } typedef struct { + BlockDriverState *bs; Coroutine *co; - int is_allocated; + uint64_t pos; + int64_t status; int *pnum; } QEDIsAllocatedCB; static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len) { QEDIsAllocatedCB *cb = opaque; + BDRVQEDState *s = cb->bs->opaque; *cb->pnum = len / BDRV_SECTOR_SIZE; - cb->is_allocated = (ret == QED_CLUSTER_FOUND || ret == QED_CLUSTER_ZERO); + switch (ret) { + case QED_CLUSTER_FOUND: + offset |= qed_offset_into_cluster(s, cb->pos); + cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; + break; + case QED_CLUSTER_ZERO: + cb->status = BDRV_BLOCK_ZERO; + break; + case QED_CLUSTER_L2: + case QED_CLUSTER_L1: + cb->status = 0; + break; + default: + assert(ret < 0); + cb->status = ret; + break; + } + if (cb->co) { qemu_coroutine_enter(cb->co, NULL); } } -static int coroutine_fn bdrv_qed_co_is_allocated(BlockDriverState *bs, +static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { BDRVQEDState *s = bs->opaque; - uint64_t pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE; size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE; QEDIsAllocatedCB cb = { - .is_allocated = -1, + .bs = bs, + .pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE, + .status = BDRV_BLOCK_OFFSET_MASK, .pnum = pnum, }; QEDRequest request = { .l2_table = NULL }; - qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb); + qed_find_cluster(s, &request, cb.pos, len, qed_is_allocated_cb, &cb); /* Now sleep if the callback wasn't invoked immediately */ - while (cb.is_allocated == -1) { + while (cb.status == BDRV_BLOCK_OFFSET_MASK) { cb.co = qemu_coroutine_self(); qemu_coroutine_yield(); } qed_unref_l2_cache_entry(request.l2_table); - return cb.is_allocated; + return cb.status; } static int bdrv_qed_make_empty(BlockDriverState *bs) @@ -1526,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, @@ -1575,7 +1604,7 @@ static BlockDriver bdrv_qed = { .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, .bdrv_create = bdrv_qed_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_co_is_allocated = bdrv_qed_co_is_allocated, + .bdrv_co_get_block_status = bdrv_qed_co_get_block_status, .bdrv_make_empty = bdrv_qed_make_empty, .bdrv_aio_readv = bdrv_qed_aio_readv, .bdrv_aio_writev = bdrv_qed_aio_writev, 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 ba721d3f5b..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; @@ -1084,12 +1086,12 @@ static int raw_create(const char *filename, QEMUOptionParameter *options) * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes * beyond the end of the disk image it will be clamped. */ -static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, +static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { off_t start, data, hole; - int ret; + int64_t ret; ret = fd_open(bs); if (ret < 0) { @@ -1097,6 +1099,7 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, } start = sector_num * BDRV_SECTOR_SIZE; + ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; #ifdef CONFIG_FIEMAP @@ -1114,7 +1117,7 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) { /* Assume everything is allocated. */ *pnum = nb_sectors; - return 1; + return ret; } if (f.fm.fm_mapped_extents == 0) { @@ -1127,6 +1130,9 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, } else { data = f.fe.fe_logical; hole = f.fe.fe_logical + f.fe.fe_length; + if (f.fe.fe_flags & FIEMAP_EXTENT_UNWRITTEN) { + ret |= BDRV_BLOCK_ZERO; + } } #elif defined SEEK_HOLE && defined SEEK_DATA @@ -1141,7 +1147,7 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, /* Most likely EINVAL. Assume everything is allocated. */ *pnum = nb_sectors; - return 1; + return ret; } if (hole > start) { @@ -1154,19 +1160,21 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, } } #else - *pnum = nb_sectors; - return 1; + data = 0; + hole = start + nb_sectors * BDRV_SECTOR_SIZE; #endif if (data <= start) { /* On a data extent, compute sectors to the end of the extent. */ *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE); - return 1; } else { /* On a hole, compute sectors to the beginning of the next extent. */ *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE); - return 0; + ret &= ~BDRV_BLOCK_DATA; + ret |= BDRV_BLOCK_ZERO; } + + return ret; } static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs, @@ -1192,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, @@ -1200,7 +1209,7 @@ static BlockDriver bdrv_file = { .bdrv_close = raw_close, .bdrv_create = raw_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_co_is_allocated = raw_co_is_allocated, + .bdrv_co_get_block_status = raw_co_get_block_status, .bdrv_aio_readv = raw_aio_readv, .bdrv_aio_writev = raw_aio_writev, @@ -1325,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; @@ -1498,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; @@ -1532,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, @@ -1559,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; @@ -1656,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, @@ -1680,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; @@ -1757,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, @@ -1878,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 9b5b2af4e8..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,17 +535,34 @@ 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; + int ret = 0; DWORD overlapped; char device_name[64]; - const char *filename = qdict_get_str(options, "filename"); + + Error *local_err = NULL; + const char *filename; + + QemuOpts *opts = qemu_opts_create_nofail(&raw_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); + ret = -EINVAL; + goto done; + } + + filename = qemu_opt_get(opts, "filename"); if (strstart(filename, "/dev/cdrom", NULL)) { - if (find_cdrom(device_name, sizeof(device_name)) < 0) - return -ENOENT; + if (find_cdrom(device_name, sizeof(device_name)) < 0) { + ret = -ENOENT; + goto done; + } filename = device_name; } else { /* transform drive letters into device name */ @@ -564,17 +585,24 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags) if (s->hfile == INVALID_HANDLE_VALUE) { int err = GetLastError(); - if (err == ERROR_ACCESS_DENIED) - return -EACCES; - return -1; + if (err == ERROR_ACCESS_DENIED) { + ret = -EACCES; + } else { + ret = -1; + } + goto done; } - return 0; + +done: + qemu_opts_del(opts); + return ret; } 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 ab2b0fd7d2..d4ace6020b 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -58,11 +58,11 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); } -static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - int *pnum) +static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) { - return bdrv_co_is_allocated(bs->file, sector_num, nb_sectors, pnum); + return bdrv_get_block_status(bs->file, sector_num, nb_sectors, pnum); } static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs, @@ -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; @@ -164,7 +174,7 @@ static BlockDriver bdrv_raw = { .bdrv_co_writev = &raw_co_writev, .bdrv_co_write_zeroes = &raw_co_write_zeroes, .bdrv_co_discard = &raw_co_discard, - .bdrv_co_is_allocated = &raw_co_is_allocated, + .bdrv_co_get_block_status = &raw_co_get_block_status, .bdrv_truncate = &raw_truncate, .bdrv_getlength = &raw_getlength, .bdrv_get_info = &raw_get_info, 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 1ad4d070e7..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; } @@ -1430,14 +1434,15 @@ static int sd_prealloc(const char *filename) } out: if (bs) { - bdrv_delete(bs); + bdrv_unref(bs); } g_free(buf); 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; } @@ -1509,13 +1517,13 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) if (!is_snapshot(&s->inode)) { error_report("cannot clone from a non snapshot vdi"); - bdrv_delete(bs); + bdrv_unref(bs); ret = -EINVAL; goto out; } base_vid = s->inode.vdi_id; - bdrv_delete(bs); + bdrv_unref(bs); } ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0); @@ -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; @@ -2270,9 +2281,9 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num, return acb->ret; } -static coroutine_fn int -sd_co_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - int *pnum) +static coroutine_fn int64_t +sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + int *pnum) { BDRVSheepdogState *s = bs->opaque; SheepdogInode *inode = &s->inode; @@ -2280,7 +2291,7 @@ sd_co_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, end = DIV_ROUND_UP((sector_num + nb_sectors) * BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE); unsigned long idx; - int ret = 1; + int64_t ret = BDRV_BLOCK_DATA; for (idx = start; idx < end; idx++) { if (inode->data_vdi_id[idx] == 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, @@ -2338,7 +2350,7 @@ static BlockDriver bdrv_sheepdog = { .bdrv_co_writev = sd_co_writev, .bdrv_co_flush_to_disk = sd_co_flush_to_disk, .bdrv_co_discard = sd_co_discard, - .bdrv_co_is_allocated = sd_co_is_allocated, + .bdrv_co_get_block_status = sd_co_get_block_status, .bdrv_snapshot_create = sd_snapshot_create, .bdrv_snapshot_goto = sd_snapshot_goto, @@ -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, @@ -2366,7 +2379,7 @@ static BlockDriver bdrv_sheepdog_tcp = { .bdrv_co_writev = sd_co_writev, .bdrv_co_flush_to_disk = sd_co_flush_to_disk, .bdrv_co_discard = sd_co_discard, - .bdrv_co_is_allocated = sd_co_is_allocated, + .bdrv_co_get_block_status = sd_co_get_block_status, .bdrv_snapshot_create = sd_snapshot_create, .bdrv_snapshot_goto = sd_snapshot_goto, @@ -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, @@ -2394,7 +2408,7 @@ static BlockDriver bdrv_sheepdog_unix = { .bdrv_co_writev = sd_co_writev, .bdrv_co_flush_to_disk = sd_co_flush_to_disk, .bdrv_co_discard = sd_co_discard, - .bdrv_co_is_allocated = sd_co_is_allocated, + .bdrv_co_get_block_status = sd_co_get_block_status, .bdrv_snapshot_create = sd_snapshot_create, .bdrv_snapshot_goto = sd_snapshot_goto, diff --git a/block/snapshot.c b/block/snapshot.c index 6c6d9deea1..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,9 +170,9 @@ 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_delete(bs->file); + bdrv_unref(bs->file); bs->drv = NULL; return open_ret; } @@ -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 99821252b1..45837f4476 100644 --- a/block/stream.c +++ b/block/stream.c @@ -73,7 +73,7 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base, unused = intermediate; intermediate = intermediate->backing_hd; unused->backing_hd = NULL; - bdrv_delete(unused); + bdrv_unref(unused); } } @@ -119,16 +119,17 @@ wait: break; } - ret = bdrv_co_is_allocated(bs, sector_num, - STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n); + 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 { + } else if (ret >= 0) { /* Copy if allocated in the intermediate images. Limit to the * known-unallocated area [sector_num, sector_num+n). */ - ret = bdrv_co_is_allocated_above(bs->backing_hd, base, - sector_num, n, &n); + ret = bdrv_is_allocated_above(bs->backing_hd, base, + sector_num, n, &n); /* Finish early if end of backing file has been reached */ if (ret == 0 && n == 0) { @@ -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 8a915257e8..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; @@ -470,7 +471,7 @@ static int vdi_reopen_prepare(BDRVReopenState *state, return 0; } -static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs, +static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */ @@ -479,12 +480,23 @@ static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs, size_t sector_in_block = sector_num % s->block_sectors; int n_sectors = s->block_sectors - sector_in_block; uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]); + uint64_t offset; + int result; + logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum); if (n_sectors > nb_sectors) { n_sectors = nb_sectors; } *pnum = n_sectors; - return VDI_IS_ALLOCATED(bmap_entry); + result = VDI_IS_ALLOCATED(bmap_entry); + if (!result) { + return 0; + } + + offset = s->header.offset_data + + (uint64_t)bmap_entry * s->block_size + + sector_in_block * SECTOR_SIZE; + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; } static int vdi_co_read(BlockDriverState *bs, @@ -633,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; @@ -780,7 +793,7 @@ static BlockDriver bdrv_vdi = { .bdrv_reopen_prepare = vdi_reopen_prepare, .bdrv_create = vdi_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_co_is_allocated = vdi_co_is_allocated, + .bdrv_co_get_block_status = vdi_co_get_block_status, .bdrv_make_empty = vdi_make_empty, .bdrv_read = vdi_co_read, diff --git a/block/vhdx.c b/block/vhdx.c index e9704b1fdc..6cb04122bb 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -20,6 +20,7 @@ #include "qemu/module.h" #include "qemu/crc32c.h" #include "block/vhdx.h" +#include "migration/migration.h" /* Several metadata and region table data entries are identified by @@ -159,6 +160,7 @@ typedef struct BDRVVHDXState { VHDXParentLocatorHeader parent_header; VHDXParentLocatorEntry *parent_entries; + Error *migration_blocker; } BDRVVHDXState; uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, @@ -715,7 +717,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; @@ -805,6 +808,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags) /* TODO: differencing files, write */ + /* Disable migration when VHDX images are used */ + error_set(&s->migration_blocker, + QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + "vhdx", bs->device_name, "live migration"); + migrate_add_blocker(s->migration_blocker); + return 0; fail: qemu_vfree(s->headers[0]); @@ -951,6 +960,8 @@ static void vhdx_close(BlockDriverState *bs) qemu_vfree(s->headers[1]); qemu_vfree(s->bat); qemu_vfree(s->parent_entries); + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); } static BlockDriver bdrv_vhdx = { diff --git a/block/vmdk.c b/block/vmdk.c index 63b489d29e..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 { @@ -216,7 +216,7 @@ static void vmdk_free_extents(BlockDriverState *bs) g_free(e->l2_cache); g_free(e->l1_backup_table); if (e->file != bs->file) { - bdrv_delete(e->file); + bdrv_unref(e->file); } } g_free(s->extents); @@ -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; } @@ -746,7 +750,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, /* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/ ret = vmdk_open_sparse(bs, extent_file, bs->open_flags); if (ret) { - bdrv_delete(extent_file); + bdrv_unref(extent_file); return ret; } } else { @@ -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; @@ -1042,7 +1047,7 @@ static VmdkExtent *find_extent(BDRVVmdkState *s, return NULL; } -static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs, +static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { BDRVVmdkState *s = bs->opaque; @@ -1059,7 +1064,24 @@ static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs, sector_num * 512, 0, &offset); qemu_co_mutex_unlock(&s->lock); - ret = (ret == VMDK_OK || ret == VMDK_ZEROED); + switch (ret) { + case VMDK_ERROR: + ret = -EIO; + break; + case VMDK_UNALLOC: + ret = 0; + break; + case VMDK_ZEROED: + ret = BDRV_BLOCK_ZERO; + break; + case VMDK_OK: + ret = BDRV_BLOCK_DATA; + if (extent->file == bs->file) { + ret |= BDRV_BLOCK_OFFSET_VALID | offset; + } + + break; + } index_in_cluster = sector_num % extent->cluster_sectors; n = extent->cluster_sectors - index_in_cluster; @@ -1534,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]; @@ -1572,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; @@ -1634,17 +1658,19 @@ 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) { - bdrv_delete(bs); + qerror_report_err(local_err); + error_free(local_err); + bdrv_unref(bs); return ret; } if (strcmp(bs->drv->format_name, "vmdk")) { - bdrv_delete(bs); + bdrv_unref(bs); return -EINVAL; } parent_cid = vmdk_read_cid(bs, 0); - bdrv_delete(bs); + bdrv_unref(bs); snprintf(parent_desc_line, sizeof(parent_desc_line), "parentFileNameHint=\"%s\"", backing_file); } @@ -1837,7 +1863,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_close = vmdk_close, .bdrv_create = vmdk_create, .bdrv_co_flush_to_disk = vmdk_co_flush, - .bdrv_co_is_allocated = vmdk_co_is_allocated, + .bdrv_co_get_block_status = vmdk_co_get_block_status, .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, .bdrv_has_zero_init = vmdk_has_zero_init, 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 cd3b8edd9f..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; @@ -2874,16 +2875,17 @@ static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num, return ret; } -static int coroutine_fn vvfat_co_is_allocated(BlockDriverState *bs, +static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int* n) { BDRVVVFATState* s = bs->opaque; *n = s->sector_count - sector_num; - if (*n > nb_sectors) - *n = nb_sectors; - else if (*n < 0) - return 0; - return 1; + if (*n > nb_sectors) { + *n = nb_sectors; + } else if (*n < 0) { + return 0; + } + return BDRV_BLOCK_DATA; } static int write_target_commit(BlockDriverState *bs, int64_t sector_num, @@ -2894,7 +2896,7 @@ static int write_target_commit(BlockDriverState *bs, int64_t sector_num, static void write_target_close(BlockDriverState *bs) { BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); - bdrv_delete(s->qcow); + bdrv_unref(s->qcow); g_free(s->qcow_filename); } @@ -2908,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); @@ -2925,17 +2928,22 @@ 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) { - bdrv_delete(s->qcow); + qerror_report_err(local_err); + error_free(local_err); + bdrv_unref(s->qcow); goto err; } @@ -2943,7 +2951,7 @@ static int enable_write_target(BDRVVVFATState *s) unlink(s->qcow_filename); #endif - s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1); + s->bs->backing_hd = bdrv_new(""); s->bs->backing_hd->drv = &vvfat_write_target; s->bs->backing_hd->opaque = g_malloc(sizeof(void*)); *(void**)s->bs->backing_hd->opaque = s; @@ -2984,7 +2992,7 @@ static BlockDriver bdrv_vvfat = { .bdrv_read = vvfat_co_read, .bdrv_write = vvfat_co_write, - .bdrv_co_is_allocated = vvfat_co_is_allocated, + .bdrv_co_get_block_status = vvfat_co_get_block_status, }; static void bdrv_vvfat_init(void) diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 95f10c81e3..922cf5657b 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -69,12 +69,6 @@ static void nbd_close_notifier(Notifier *n, void *data) g_free(cn); } -static void nbd_server_put_ref(NBDExport *exp) -{ - BlockDriverState *bs = nbd_export_get_blockdev(exp); - drive_put_ref(drive_get_by_blockdev(bs)); -} - void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, Error **errp) { @@ -105,11 +99,9 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, writable = false; } - exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, - nbd_server_put_ref); + exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, NULL); nbd_export_set_name(exp, device); - drive_get_ref(drive_get_by_blockdev(bs)); n = g_malloc0(sizeof(NBDCloseNotifier)); n->n.notify = nbd_close_notifier; diff --git a/blockdev.c b/blockdev.c index e70e16e4de..8c83f6f1ca 100644 --- a/blockdev.c +++ b/blockdev.c @@ -212,7 +212,7 @@ static void bdrv_format_print(void *opaque, const char *name) static void drive_uninit(DriveInfo *dinfo) { qemu_opts_del(dinfo->opts); - bdrv_delete(dinfo->bdrv); + bdrv_unref(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); g_free(dinfo->serial); @@ -234,32 +234,32 @@ void drive_get_ref(DriveInfo *dinfo) typedef struct { QEMUBH *bh; - DriveInfo *dinfo; -} DrivePutRefBH; + BlockDriverState *bs; +} BDRVPutRefBH; -static void drive_put_ref_bh(void *opaque) +static void bdrv_put_ref_bh(void *opaque) { - DrivePutRefBH *s = opaque; + BDRVPutRefBH *s = opaque; - drive_put_ref(s->dinfo); + bdrv_unref(s->bs); qemu_bh_delete(s->bh); g_free(s); } /* - * Release a drive reference in a BH + * Release a BDS reference in a BH * - * It is not possible to use drive_put_ref() from a callback function when the - * callers still need the drive. In such cases we schedule a BH to release the - * reference. + * It is not safe to use bdrv_unref() from a callback function when the callers + * still need the BlockDriverState. In such cases we schedule a BH to release + * the reference. */ -static void drive_put_ref_bh_schedule(DriveInfo *dinfo) +static void bdrv_put_ref_bh_schedule(BlockDriverState *bs) { - DrivePutRefBH *s; + BDRVPutRefBH *s; - s = g_new(DrivePutRefBH, 1); - s->bh = qemu_bh_new(drive_put_ref_bh, s); - s->dinfo = dinfo; + s = g_new(BDRVPutRefBH, 1); + s->bh = qemu_bh_new(bdrv_put_ref_bh, s); + s->bs = bs; qemu_bh_schedule(s->bh); } @@ -280,32 +280,16 @@ static int parse_block_error_action(const char *buf, bool is_read) } } -static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp) +static bool check_throttle_config(ThrottleConfig *cfg, Error **errp) { - bool bps_flag; - bool iops_flag; - - assert(io_limits); - - bps_flag = (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] != 0) - && ((io_limits->bps[BLOCK_IO_LIMIT_READ] != 0) - || (io_limits->bps[BLOCK_IO_LIMIT_WRITE] != 0)); - iops_flag = (io_limits->iops[BLOCK_IO_LIMIT_TOTAL] != 0) - && ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0) - || (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0)); - if (bps_flag || iops_flag) { - error_setg(errp, "bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) " - "cannot be used at the same time"); + if (throttle_conflicting(cfg)) { + error_setg(errp, "bps/iops/max total values and read/write values" + " cannot be used at the same time"); return false; } - if (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] < 0 || - io_limits->bps[BLOCK_IO_LIMIT_WRITE] < 0 || - io_limits->bps[BLOCK_IO_LIMIT_READ] < 0 || - io_limits->iops[BLOCK_IO_LIMIT_TOTAL] < 0 || - io_limits->iops[BLOCK_IO_LIMIT_WRITE] < 0 || - io_limits->iops[BLOCK_IO_LIMIT_READ] < 0) { - error_setg(errp, "bps and iops values must be 0 or greater"); + if (!throttle_is_valid(cfg)) { + error_setg(errp, "bps/iops/maxs values must be 0 or greater"); return false; } @@ -330,7 +314,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, int on_read_error, on_write_error; const char *devaddr; DriveInfo *dinfo; - BlockIOLimit io_limits; + ThrottleConfig cfg; int snapshot = 0; bool copy_on_read; int ret; @@ -459,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; } @@ -496,20 +480,36 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, } /* disk I/O throttling */ - io_limits.bps[BLOCK_IO_LIMIT_TOTAL] = + memset(&cfg, 0, sizeof(cfg)); + cfg.buckets[THROTTLE_BPS_TOTAL].avg = qemu_opt_get_number(opts, "throttling.bps-total", 0); - io_limits.bps[BLOCK_IO_LIMIT_READ] = + cfg.buckets[THROTTLE_BPS_READ].avg = qemu_opt_get_number(opts, "throttling.bps-read", 0); - io_limits.bps[BLOCK_IO_LIMIT_WRITE] = + cfg.buckets[THROTTLE_BPS_WRITE].avg = qemu_opt_get_number(opts, "throttling.bps-write", 0); - io_limits.iops[BLOCK_IO_LIMIT_TOTAL] = + cfg.buckets[THROTTLE_OPS_TOTAL].avg = qemu_opt_get_number(opts, "throttling.iops-total", 0); - io_limits.iops[BLOCK_IO_LIMIT_READ] = + cfg.buckets[THROTTLE_OPS_READ].avg = qemu_opt_get_number(opts, "throttling.iops-read", 0); - io_limits.iops[BLOCK_IO_LIMIT_WRITE] = + cfg.buckets[THROTTLE_OPS_WRITE].avg = qemu_opt_get_number(opts, "throttling.iops-write", 0); - if (!do_check_io_limits(&io_limits, &error)) { + cfg.buckets[THROTTLE_BPS_TOTAL].max = + qemu_opt_get_number(opts, "throttling.bps-total-max", 0); + cfg.buckets[THROTTLE_BPS_READ].max = + qemu_opt_get_number(opts, "throttling.bps-read-max", 0); + cfg.buckets[THROTTLE_BPS_WRITE].max = + qemu_opt_get_number(opts, "throttling.bps-write-max", 0); + cfg.buckets[THROTTLE_OPS_TOTAL].max = + qemu_opt_get_number(opts, "throttling.iops-total-max", 0); + cfg.buckets[THROTTLE_OPS_READ].max = + qemu_opt_get_number(opts, "throttling.iops-read-max", 0); + cfg.buckets[THROTTLE_OPS_WRITE].max = + qemu_opt_get_number(opts, "throttling.iops-write-max", 0); + + cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); + + if (!check_throttle_config(&cfg, &error)) { error_report("%s", error_get_pretty(error)); error_free(error); return NULL; @@ -636,7 +636,10 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); /* disk I/O throttling */ - bdrv_set_io_limits(dinfo->bdrv, &io_limits); + if (throttle_enabled(&cfg)) { + bdrv_io_limits_enable(dinfo->bdrv); + bdrv_set_io_limits(dinfo->bdrv, &cfg); + } switch(type) { case IF_IDE: @@ -707,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; } @@ -732,7 +729,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, err: qemu_opts_del(opts); QDECREF(bs_opts); - bdrv_delete(dinfo->bdrv); + bdrv_unref(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); g_free(dinfo); @@ -763,6 +760,17 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) qemu_opt_rename(all_opts, "bps_rd", "throttling.bps-read"); qemu_opt_rename(all_opts, "bps_wr", "throttling.bps-write"); + qemu_opt_rename(all_opts, "iops_max", "throttling.iops-total-max"); + qemu_opt_rename(all_opts, "iops_rd_max", "throttling.iops-read-max"); + qemu_opt_rename(all_opts, "iops_wr_max", "throttling.iops-write-max"); + + qemu_opt_rename(all_opts, "bps_max", "throttling.bps-total-max"); + qemu_opt_rename(all_opts, "bps_rd_max", "throttling.bps-read-max"); + qemu_opt_rename(all_opts, "bps_wr_max", "throttling.bps-write-max"); + + qemu_opt_rename(all_opts, + "iops_size", "throttling.iops-size"); + qemu_opt_rename(all_opts, "readonly", "read-only"); value = qemu_opt_get(all_opts, "cache"); @@ -844,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 */ @@ -875,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; @@ -957,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); } } @@ -982,7 +1175,7 @@ static void external_snapshot_abort(BlkTransactionState *common) ExternalSnapshotState *state = DO_UPCAST(ExternalSnapshotState, common, common); if (state->new_bs) { - bdrv_delete(state->new_bs); + bdrv_unref(state->new_bs); } } @@ -1058,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, + }, }; /* @@ -1088,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; @@ -1189,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; } @@ -1247,10 +1448,26 @@ void qmp_change_blockdev(const char *device, const char *filename, /* throttling disk I/O limits */ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, - int64_t bps_wr, int64_t iops, int64_t iops_rd, - int64_t iops_wr, Error **errp) + int64_t bps_wr, + int64_t iops, + int64_t iops_rd, + int64_t iops_wr, + bool has_bps_max, + int64_t bps_max, + bool has_bps_rd_max, + int64_t bps_rd_max, + bool has_bps_wr_max, + int64_t bps_wr_max, + bool has_iops_max, + int64_t iops_max, + bool has_iops_rd_max, + int64_t iops_rd_max, + bool has_iops_wr_max, + int64_t iops_wr_max, + bool has_iops_size, + int64_t iops_size, Error **errp) { - BlockIOLimit io_limits; + ThrottleConfig cfg; BlockDriverState *bs; bs = bdrv_find(device); @@ -1259,27 +1476,50 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, return; } - io_limits.bps[BLOCK_IO_LIMIT_TOTAL] = bps; - io_limits.bps[BLOCK_IO_LIMIT_READ] = bps_rd; - io_limits.bps[BLOCK_IO_LIMIT_WRITE] = bps_wr; - io_limits.iops[BLOCK_IO_LIMIT_TOTAL]= iops; - io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd; - io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr; + memset(&cfg, 0, sizeof(cfg)); + cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps; + cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd; + cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr; - if (!do_check_io_limits(&io_limits, errp)) { - return; + cfg.buckets[THROTTLE_OPS_TOTAL].avg = iops; + cfg.buckets[THROTTLE_OPS_READ].avg = iops_rd; + cfg.buckets[THROTTLE_OPS_WRITE].avg = iops_wr; + + if (has_bps_max) { + cfg.buckets[THROTTLE_BPS_TOTAL].max = bps_max; + } + if (has_bps_rd_max) { + cfg.buckets[THROTTLE_BPS_READ].max = bps_rd_max; + } + if (has_bps_wr_max) { + cfg.buckets[THROTTLE_BPS_WRITE].max = bps_wr_max; + } + if (has_iops_max) { + cfg.buckets[THROTTLE_OPS_TOTAL].max = iops_max; + } + if (has_iops_rd_max) { + cfg.buckets[THROTTLE_OPS_READ].max = iops_rd_max; + } + if (has_iops_wr_max) { + cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max; } - bs->io_limits = io_limits; + if (has_iops_size) { + cfg.op_size = iops_size; + } - if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) { + if (!check_throttle_config(&cfg, errp)) { + return; + } + + if (!bs->io_limits_enabled && throttle_enabled(&cfg)) { bdrv_io_limits_enable(bs); - } else if (bs->io_limits_enabled && !bdrv_io_limits_enabled(bs)) { + } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) { bdrv_io_limits_disable(bs); - } else { - if (bs->block_timer) { - timer_mod(bs->block_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - } + } + + if (bs->io_limits_enabled) { + bdrv_set_io_limits(bs, &cfg); } } @@ -1383,7 +1623,7 @@ static void block_job_cb(void *opaque, int ret) } qobject_decref(obj); - drive_put_ref_bh_schedule(drive_get_by_blockdev(bs)); + bdrv_put_ref_bh_schedule(bs); } void qmp_block_stream(const char *device, bool has_base, @@ -1420,11 +1660,6 @@ void qmp_block_stream(const char *device, bool has_base, return; } - /* Grab a reference so hotplug does not delete the BlockDriverState from - * underneath us. - */ - drive_get_ref(drive_get_by_blockdev(bs)); - trace_qmp_block_stream(bs, bs->job); } @@ -1481,10 +1716,6 @@ void qmp_block_commit(const char *device, error_propagate(errp, local_err); return; } - /* Grab a reference so hotplug does not delete the BlockDriverState from - * underneath us. - */ - drive_get_ref(drive_get_by_blockdev(bs)); } void qmp_drive_backup(const char *device, const char *target, @@ -1583,25 +1814,20 @@ 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_delete(target_bs); - error_setg_file_open(errp, -ret, target); + bdrv_unref(target_bs); + error_propagate(errp, local_err); return; } backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { - bdrv_delete(target_bs); + bdrv_unref(target_bs); error_propagate(errp, local_err); return; } - - /* Grab a reference so hotplug does not delete the BlockDriverState from - * underneath us. - */ - drive_get_ref(drive_get_by_blockdev(bs)); } #define DEFAULT_MIRROR_BUF_SIZE (10 << 20) @@ -1700,7 +1926,6 @@ void qmp_drive_mirror(const char *device, const char *target, } else { switch (mode) { case NEW_IMAGE_MODE_EXISTING: - ret = 0; break; case NEW_IMAGE_MODE_ABSOLUTE_PATHS: /* create new image with backing file */ @@ -1723,10 +1948,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_delete(target_bs); - error_setg_file_open(errp, -ret, target); + bdrv_unref(target_bs); + error_propagate(errp, local_err); return; } @@ -1734,15 +1960,10 @@ void qmp_drive_mirror(const char *device, const char *target, on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { - bdrv_delete(target_bs); + bdrv_unref(target_bs); error_propagate(errp, local_err); return; } - - /* Grab a reference so hotplug does not delete the BlockDriverState from - * underneath us. - */ - drive_get_ref(drive_get_by_blockdev(bs)); } static BlockJob *find_block_job(const char *device) @@ -1968,6 +2189,34 @@ QemuOptsList qemu_common_drive_opts = { .type = QEMU_OPT_NUMBER, .help = "limit write bytes per second", },{ + .name = "throttling.iops-total-max", + .type = QEMU_OPT_NUMBER, + .help = "I/O operations burst", + },{ + .name = "throttling.iops-read-max", + .type = QEMU_OPT_NUMBER, + .help = "I/O operations read burst", + },{ + .name = "throttling.iops-write-max", + .type = QEMU_OPT_NUMBER, + .help = "I/O operations write burst", + },{ + .name = "throttling.bps-total-max", + .type = QEMU_OPT_NUMBER, + .help = "total bytes burst", + },{ + .name = "throttling.bps-read-max", + .type = QEMU_OPT_NUMBER, + .help = "total bytes read burst", + },{ + .name = "throttling.bps-write-max", + .type = QEMU_OPT_NUMBER, + .help = "total bytes write burst", + },{ + .name = "throttling.iops-size", + .type = QEMU_OPT_NUMBER, + .help = "when limiting by iops max size of an I/O in bytes", + },{ .name = "copy-on-read", .type = QEMU_OPT_BOOL, .help = "copy read data from backing file into image file", diff --git a/blockjob.c b/blockjob.c index 7edc945119..e7d49b7169 100644 --- a/blockjob.c +++ b/blockjob.c @@ -45,6 +45,7 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs, error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs)); return NULL; } + bdrv_ref(bs); bdrv_set_in_use(bs, 1); job = g_malloc0(job_type->instance_size); 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="" @@ -232,9 +231,13 @@ usb_redir="" glx="" zlib="yes" guest_agent="" +guest_agent_with_vss="no" +vss_win32_sdk="" +win_sdk="no" want_tools="yes" libiscsi="" coroutine="" +coroutine_pool="" seccomp="" glusterfs="" glusterfs_discard="no" @@ -252,6 +255,8 @@ for opt do ;; --cc=*) CC="$optarg" ;; + --cxx=*) CXX="$optarg" + ;; --source-path=*) source_path="$optarg" ;; --cpu=*) cpu="$optarg" @@ -282,6 +287,12 @@ else cc="${CC-${cross_prefix}gcc}" fi +if test -z "${CXX}${cross_prefix}"; then + cxx="c++" +else + cxx="${CXX-${cross_prefix}g++}" +fi + ar="${AR-${cross_prefix}ar}" as="${AS-${cross_prefix}as}" cpp="${CPP-$cc -E}" @@ -550,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" @@ -563,9 +573,6 @@ esac if [ "$bsd" = "yes" ] ; then if [ "$darwin" != "yes" ] ; then - if [ "$targetos" != "FreeBSD" ]; then - usb="bsd" - fi bsd_user="yes" fi fi @@ -626,6 +633,8 @@ for opt do ;; --host-cc=*) host_cc="$optarg" ;; + --cxx=*) + ;; --objcc=*) objcc="$optarg" ;; --make=*) make="$optarg" @@ -859,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" @@ -875,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" @@ -917,6 +928,18 @@ for opt do ;; --disable-guest-agent) guest_agent="no" ;; + --with-vss-sdk) vss_win32_sdk="" + ;; + --with-vss-sdk=*) vss_win32_sdk="$optarg" + ;; + --without-vss-sdk) vss_win32_sdk="no" + ;; + --with-win-sdk) win_sdk="" + ;; + --with-win-sdk=*) win_sdk="$optarg" + ;; + --without-win-sdk) win_sdk="no" + ;; --enable-tools) want_tools="yes" ;; --disable-tools) want_tools="no" @@ -955,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" @@ -1032,6 +1063,7 @@ echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]" echo " --cc=CC use C compiler CC [$cc]" echo " --host-cc=CC use C compiler CC [$host_cc] for code run at" echo " build time" +echo " --cxx=CXX use C++ compiler CXX [$cxx]" echo " --objcc=OBJCC use Objective-C compiler OBJCC [$objcc]" echo " --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS" echo " --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS" @@ -1076,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" @@ -1157,10 +1188,14 @@ echo " --disable-usb-redir disable usb network redirection support" echo " --enable-usb-redir enable usb network redirection support" echo " --disable-guest-agent disable building of the QEMU Guest Agent" echo " --enable-guest-agent enable building of the QEMU Guest Agent" +echo " --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent" +echo " --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb)" 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" @@ -2522,7 +2557,7 @@ fi fdt_required=no for target in $target_list; do case $target in - arm*-softmmu|ppc*-softmmu|microblaze*-softmmu) + aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu) fdt_required=yes ;; esac @@ -3091,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" @@ -3121,6 +3155,61 @@ if test "$usb_redir" != "no" ; then fi ########################################## +# check if we have VSS SDK headers for win + +if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$vss_win32_sdk" != "no" ; then + case "$vss_win32_sdk" in + "") vss_win32_include="-I$source_path" ;; + *\ *) # The SDK is installed in "Program Files" by default, but we cannot + # handle path with spaces. So we symlink the headers into ".sdk/vss". + vss_win32_include="-I$source_path/.sdk/vss" + symlink "$vss_win32_sdk/inc" "$source_path/.sdk/vss/inc" + ;; + *) vss_win32_include="-I$vss_win32_sdk" + esac + cat > $TMPC << EOF +#define __MIDL_user_allocate_free_DEFINED__ +#include <inc/win2003/vss.h> +int main(void) { return VSS_CTX_BACKUP; } +EOF + if compile_prog "$vss_win32_include" "" ; then + guest_agent_with_vss="yes" + QEMU_CFLAGS="$QEMU_CFLAGS $vss_win32_include" + libs_qga="-lole32 -loleaut32 -lshlwapi -luuid -lstdc++ -Wl,--enable-stdcall-fixup $libs_qga" + else + if test "$vss_win32_sdk" != "" ; then + echo "ERROR: Please download and install Microsoft VSS SDK:" + echo "ERROR: http://www.microsoft.com/en-us/download/details.aspx?id=23490" + echo "ERROR: On POSIX-systems, you can extract the SDK headers by:" + echo "ERROR: scripts/extract-vsssdk-headers setup.exe" + echo "ERROR: The headers are extracted in the directory \`inc'." + feature_not_found "VSS support" + fi + guest_agent_with_vss="no" + fi +fi + +########################################## +# lookup Windows platform SDK (if not specified) +# The SDK is needed only to build .tlb (type library) file of guest agent +# VSS provider from the source. It is usually unnecessary because the +# pre-compiled .tlb file is included. + +if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$guest_agent_with_vss" = "yes" ; then + if test -z "$win_sdk"; then + programfiles="$PROGRAMFILES" + test -n "$PROGRAMW6432" && programfiles="$PROGRAMW6432" + if test -n "$programfiles"; then + win_sdk=$(ls -d "$programfiles/Microsoft SDKs/Windows/v"* | tail -1) 2>/dev/null + else + feature_not_found "Windows SDK" + fi + elif test "$win_sdk" = "no"; then + win_sdk="" + fi +fi + +########################################## ########################################## # check if we have fdatasync @@ -3279,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 @@ -3419,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 @@ -3485,8 +3585,11 @@ if test "$softmmu" = yes ; then fi fi if [ "$guest_agent" != "no" ]; then - if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then + if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then tools="qemu-ga\$(EXESUF) $tools" + if [ "$mingw32" = "yes" -a "$guest_agent_with_vss" = "yes" ]; then + tools="qga/vss-win32/qga-vss.dll qga/vss-win32/qga-vss.tlb $tools" + fi guest_agent=yes elif [ "$guest_agent" != yes ]; then guest_agent=no @@ -3557,10 +3660,12 @@ echo "Manual directory `eval echo $mandir`" echo "ELF interp prefix $interp_prefix" else echo "local state directory queried at runtime" +echo "Windows SDK $win_sdk" fi echo "Source path $source_path" echo "C compiler $cc" echo "Host C compiler $host_cc" +echo "C++ compiler $cxx" echo "Objective-C compiler $objcc" echo "CFLAGS $CFLAGS" echo "QEMU_CFLAGS $QEMU_CFLAGS" @@ -3593,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 @@ -3642,8 +3746,10 @@ echo "usb net redir $usb_redir" echo "GLX support $glx" echo "libiscsi support $libiscsi" 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" @@ -3689,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 @@ -3716,6 +3822,10 @@ if test "$mingw32" = "yes" ; then version_micro=0 echo "CONFIG_FILEVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak + if test "$guest_agent_with_vss" = "yes" ; then + echo "CONFIG_QGA_VSS=y" >> $config_host_mak + echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak + fi else echo "CONFIG_POSIX=y" >> $config_host_mak fi @@ -3774,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 @@ -3999,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 @@ -4049,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 @@ -4148,6 +4247,7 @@ echo "PYTHON=$python" >> $config_host_mak echo "CC=$cc" >> $config_host_mak echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak +echo "CXX=$cxx" >> $config_host_mak echo "OBJCC=$objcc" >> $config_host_mak echo "AR=$ar" >> $config_host_mak echo "AS=$as" >> $config_host_mak @@ -4272,6 +4372,11 @@ case "$target_name" in bflt="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" ;; + aarch64) + TARGET_BASE_ARCH=arm + bflt="yes" + gdb_xml_files="aarch64-core.xml" + ;; cris) ;; lm32) @@ -4552,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 39b6016460..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 @@ -18,6 +18,28 @@ Example: "data": { "actual": 944766976 }, "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } +BLOCK_IMAGE_CORRUPTED +--------------------- + +Emitted when a disk image is being marked corrupt. + +Data: + +- "device": Device name (json-string) +- "msg": Informative message (e.g., reason for the corruption) (json-string) +- "offset": If the corruption resulted from an image access, this is the access + offset into the image (json-int) +- "size": If the corruption resulted from an image access, this is the access + size (json-int) + +Example: + +{ "event": "BLOCK_IMAGE_CORRUPTED", + "data": { "device": "ide0-hd0", + "msg": "Prevented active L1 table overwrite", "offset": 196608, + "size": 65536 }, + "timestamp": { "seconds": 1378126126, "microseconds": 966463 } } + BLOCK_IO_ERROR -------------- @@ -137,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. @@ -172,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. @@ -464,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 @@ -854,7 +866,7 @@ static void mem_add(MemoryListener *listener, MemoryRegionSection *section) now = remain; if (int128_lt(remain.size, page_size)) { register_subpage(d, &now); - } else if (remain.offset_within_region & ~TARGET_PAGE_MASK) { + } else if (remain.offset_within_address_space & ~TARGET_PAGE_MASK) { now.size = page_size; register_subpage(d, &now); } else { @@ -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: " @@ -1560,7 +1573,7 @@ static uint64_t subpage_read(void *opaque, hwaddr addr, uint8_t buf[4]; #if defined(DEBUG_SUBPAGE) - printf("%s: subpage %p len %d addr " TARGET_FMT_plx "\n", __func__, + printf("%s: subpage %p len %u addr " TARGET_FMT_plx "\n", __func__, subpage, len, addr); #endif address_space_read(subpage->as, addr + subpage->base, buf, len); @@ -1583,7 +1596,7 @@ static void subpage_write(void *opaque, hwaddr addr, uint8_t buf[4]; #if defined(DEBUG_SUBPAGE) - printf("%s: subpage %p len %d addr " TARGET_FMT_plx + printf("%s: subpage %p len %u addr " TARGET_FMT_plx " value %"PRIx64"\n", __func__, subpage, len, addr, value); #endif @@ -1604,16 +1617,16 @@ static void subpage_write(void *opaque, hwaddr addr, } static bool subpage_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned len, bool is_write) { subpage_t *subpage = opaque; #if defined(DEBUG_SUBPAGE) - printf("%s: subpage %p %c len %d addr " TARGET_FMT_plx "\n", + printf("%s: subpage %p %c len %u addr " TARGET_FMT_plx "\n", __func__, subpage, is_write ? 'w' : 'r', len, addr); #endif return address_space_access_valid(subpage->as, addr + subpage->base, - size, is_write); + len, is_write); } static const MemoryRegionOps subpage_ops = { @@ -1633,8 +1646,8 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, idx = SUBPAGE_IDX(start); eidx = SUBPAGE_IDX(end); #if defined(DEBUG_SUBPAGE) - printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__, - mmio, start, end, idx, eidx, memory); + printf("%s: %p start %08x end %08x idx %08x eidx %08x section %d\n", + __func__, mmio, start, end, idx, eidx, section); #endif for (; idx <= eidx; idx++) { mmio->sub_section[idx] = section; @@ -1655,8 +1668,8 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base) "subpage", TARGET_PAGE_SIZE); mmio->iomem.subpage = true; #if defined(DEBUG_SUBPAGE) - printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__, - mmio, base, TARGET_PAGE_SIZE, subpage_memory); + printf("%s: %p base " TARGET_FMT_plx " len %08x\n", __func__, + mmio, base, TARGET_PAGE_SIZE); #endif subpage_register(mmio, 0, TARGET_PAGE_SIZE-1, PHYS_SECTION_UNASSIGNED); @@ -1805,11 +1818,14 @@ static void memory_map_init(void) address_space_init(&address_space_memory, system_memory, "memory"); system_io = g_malloc(sizeof(*system_io)); - memory_region_init(system_io, NULL, "io", 65536); + memory_region_init_io(system_io, NULL, &unassigned_io_ops, NULL, "io", + 65536); address_space_init(&address_space_io, system_io, "I/O"); memory_listener_register(&core_memory_listener, &address_space_memory); - memory_listener_register(&tcg_memory_listener, &address_space_memory); + if (tcg_enabled()) { + memory_listener_register(&tcg_memory_listener, &address_space_memory); + } } MemoryRegion *get_system_memory(void) @@ -1913,6 +1929,9 @@ static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) if (l > access_size_max) { l = access_size_max; } + if (l & (l - 1)) { + l = 1 << (qemu_fls(l) - 1); + } return l; } diff --git a/gdb-xml/aarch64-core.xml b/gdb-xml/aarch64-core.xml new file mode 100644 index 0000000000..e1e9dc3f91 --- /dev/null +++ b/gdb-xml/aarch64-core.xml @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2009-2012 Free Software Foundation, Inc. + Contributed by ARM Ltd. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.aarch64.core"> + <reg name="x0" bitsize="64"/> + <reg name="x1" bitsize="64"/> + <reg name="x2" bitsize="64"/> + <reg name="x3" bitsize="64"/> + <reg name="x4" bitsize="64"/> + <reg name="x5" bitsize="64"/> + <reg name="x6" bitsize="64"/> + <reg name="x7" bitsize="64"/> + <reg name="x8" bitsize="64"/> + <reg name="x9" bitsize="64"/> + <reg name="x10" bitsize="64"/> + <reg name="x11" bitsize="64"/> + <reg name="x12" bitsize="64"/> + <reg name="x13" bitsize="64"/> + <reg name="x14" bitsize="64"/> + <reg name="x15" bitsize="64"/> + <reg name="x16" bitsize="64"/> + <reg name="x17" bitsize="64"/> + <reg name="x18" bitsize="64"/> + <reg name="x19" bitsize="64"/> + <reg name="x20" bitsize="64"/> + <reg name="x21" bitsize="64"/> + <reg name="x22" bitsize="64"/> + <reg name="x23" bitsize="64"/> + <reg name="x24" bitsize="64"/> + <reg name="x25" bitsize="64"/> + <reg name="x26" bitsize="64"/> + <reg name="x27" bitsize="64"/> + <reg name="x28" bitsize="64"/> + <reg name="x29" bitsize="64"/> + <reg name="x30" bitsize="64"/> + <reg name="sp" bitsize="64" type="data_ptr"/> + + <reg name="pc" bitsize="64" type="code_ptr"/> + <reg name="cpsr" bitsize="32"/> +</feature> @@ -1553,7 +1553,7 @@ static void gdb_accept(void) static int gdbserver_open(int port) { struct sockaddr_in sockaddr; - int fd, val, ret; + int fd, ret; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { @@ -1564,9 +1564,7 @@ static int gdbserver_open(int port) fcntl(fd, F_SETFD, FD_CLOEXEC); #endif - /* allow fast reuse */ - val = 1; - qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + socket_set_fast_reuse(fd); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(port); 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]", @@ -344,14 +344,30 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) { monitor_printf(mon, " I/O throttling: bps=%" PRId64 " bps_rd=%" PRId64 " bps_wr=%" PRId64 + " bps_max=%" PRId64 + " bps_rd_max=%" PRId64 + " bps_wr_max=%" PRId64 " iops=%" PRId64 " iops_rd=%" PRId64 - " iops_wr=%" PRId64 "\n", + " iops_wr=%" PRId64 + " iops_max=%" PRId64 + " iops_rd_max=%" PRId64 + " iops_wr_max=%" PRId64 + " iops_size=%" PRId64 "\n", info->value->inserted->bps, info->value->inserted->bps_rd, info->value->inserted->bps_wr, + info->value->inserted->bps_max, + info->value->inserted->bps_rd_max, + info->value->inserted->bps_wr_max, info->value->inserted->iops, info->value->inserted->iops_rd, - info->value->inserted->iops_wr); + info->value->inserted->iops_wr, + info->value->inserted->iops_max, + info->value->inserted->iops_rd_max, + info->value->inserted->iops_wr_max, + info->value->inserted->iops_size); + } else { + monitor_printf(mon, " [not inserted]"); } if (verbose) { @@ -528,7 +544,7 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev) if (dev->class_info.has_desc) { monitor_printf(mon, "%s", dev->class_info.desc); } else { - monitor_printf(mon, "Class %04" PRId64, dev->class_info.class); + monitor_printf(mon, "Class %04" PRId64, dev->class_info.q_class); } monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n", @@ -962,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); @@ -1098,7 +1136,21 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) qdict_get_int(qdict, "bps_wr"), qdict_get_int(qdict, "iops"), qdict_get_int(qdict, "iops_rd"), - qdict_get_int(qdict, "iops_wr"), &err); + qdict_get_int(qdict, "iops_wr"), + false, /* no burst max via HMP */ + 0, + false, + 0, + false, + 0, + false, + 0, + false, + 0, + false, + 0, + false, /* No default I/O size */ + 0, &err); hmp_handle_error(mon, &err); } @@ -1111,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/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c index 90ae565c19..3fae557a84 100644 --- a/hw/9pfs/virtio-9p-xattr.c +++ b/hw/9pfs/virtio-9p-xattr.c @@ -36,7 +36,7 @@ ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, if (xops) { return xops->getxattr(ctx, path, name, value, size); } - errno = -EOPNOTSUPP; + errno = EOPNOTSUPP; return -1; } @@ -123,7 +123,7 @@ int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, if (xops) { return xops->setxattr(ctx, path, name, value, size, flags); } - errno = -EOPNOTSUPP; + errno = EOPNOTSUPP; return -1; } @@ -135,7 +135,7 @@ int v9fs_remove_xattr(FsContext *ctx, if (xops) { return xops->removexattr(ctx, path, name); } - errno = -EOPNOTSUPP; + errno = EOPNOTSUPP; return -1; } 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/acpi/piix4.c b/hw/acpi/piix4.c index 0b8d1d9da9..b46bd5ea6a 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -380,6 +380,7 @@ static void piix4_reset(void *opaque) /* Mark SMM as already inited (until KVM supports SMM). */ pci_conf[0x5B] = 0x02; } + pm_io_space_update(s); piix4_update_hotplug(s); } diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index aac9a32e0c..59e1bb8388 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -700,7 +700,7 @@ static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr) } } - if (addr >= 0x80000000000 && addr <= 0xfffffffffff) { + if (addr >= 0x80000000000ull && addr <= 0xfffffffffffull) { /* Check the fourth window for DAC enable and window enable. */ if ((pchip->win[3].wba & 0x80000000001ull) == 0x80000000001ull) { uint64_t pte_addr; 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/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 5a96ccd416..f2d7350a50 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -42,6 +42,7 @@ typedef struct { struct VirtIOBlockDataPlane { bool started; + bool starting; bool stopping; QEMUBH *start_bh; QemuThread thread; @@ -451,8 +452,15 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) return; } + if (s->starting) { + return; + } + + s->starting = true; + vq = virtio_get_queue(s->vdev, 0); if (!vring_setup(&s->vring, s->vdev, 0)) { + s->starting = false; return; } @@ -482,6 +490,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) s->io_notifier = *ioq_get_notifier(&s->ioqueue); aio_set_event_notifier(s->ctx, &s->io_notifier, handle_io); + s->starting = false; s->started = true; trace_virtio_blk_data_plane_start(s); 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 727f4331c0..8742294dfb 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -405,6 +405,7 @@ static int ioreq_map(struct ioreq *ioreq) xen_be_printf(&ioreq->blkdev->xendev, 0, "can't map grant ref %d (%s, %d maps)\n", refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq->mapped = 1; ioreq_unmap(ioreq); return -1; } @@ -809,11 +810,16 @@ 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) { - bdrv_delete(blkdev->bs); + 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; } } @@ -824,6 +830,9 @@ static int blk_connect(struct XenDevice *xendev) /* setup via qemu cmdline -> already setup for us */ xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); blkdev->bs = blkdev->dinfo->bdrv; + /* blkdev->bs is not create by us, we get a reference + * so we can bdrv_unref() unconditionally */ + bdrv_ref(blkdev->bs); } bdrv_attach_dev_nofail(blkdev->bs, blkdev); blkdev->file_size = bdrv_getlength(blkdev->bs); @@ -922,12 +931,8 @@ static void blk_disconnect(struct XenDevice *xendev) struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); if (blkdev->bs) { - if (!blkdev->dinfo) { - /* close/delete only if we created it ourself */ - bdrv_close(blkdev->bs); - bdrv_detach_dev(blkdev->bs, blkdev); - bdrv_delete(blkdev->bs); - } + bdrv_detach_dev(blkdev->bs, blkdev); + bdrv_unref(blkdev->bs); blkdev->bs = NULL; } xen_be_unbind_evtchn(&blkdev->xendev); diff --git a/hw/bt/core.c b/hw/bt/core.c index 49012e028c..0ffc948898 100644 --- a/hw/bt/core.c +++ b/hw/bt/core.c @@ -119,3 +119,26 @@ void bt_device_done(struct bt_device_s *dev) *p = dev->next; } + +static struct bt_vlan_s { + struct bt_scatternet_s net; + int id; + struct bt_vlan_s *next; +} *first_bt_vlan; + +/* find or alloc a new bluetooth "VLAN" */ +struct bt_scatternet_s *qemu_find_bt_vlan(int id) +{ + struct bt_vlan_s **pvlan, *vlan; + for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) { + if (vlan->id == id) + return &vlan->net; + } + vlan = g_malloc0(sizeof(struct bt_vlan_s)); + vlan->id = id; + pvlan = &first_bt_vlan; + while (*pvlan != NULL) + pvlan = &(*pvlan)->next; + *pvlan = vlan; + return &vlan->net; +} diff --git a/hw/bt/hci.c b/hw/bt/hci.c index d1c0604a9b..7ea3dc6b70 100644 --- a/hw/bt/hci.c +++ b/hw/bt/hci.c @@ -429,6 +429,24 @@ static const uint8_t bt_event_reserved_mask[8] = { 0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00, }; + +static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len) +{ +} + +static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr) +{ + return -ENOTSUP; +} + +struct HCIInfo null_hci = { + .cmd_send = null_hci_send, + .sco_send = null_hci_send, + .acl_send = null_hci_send, + .bdaddr_set = null_hci_addr_set, +}; + + static inline uint8_t *bt_hci_event_start(struct bt_hci_s *hci, int evt, int len) { @@ -2176,6 +2194,36 @@ struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net) return &s->info; } +struct HCIInfo *hci_init(const char *str) +{ + char *endp; + struct bt_scatternet_s *vlan = 0; + + if (!strcmp(str, "null")) + /* null */ + return &null_hci; + else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':')) + /* host[:hciN] */ + return bt_host_hci(str[4] ? str + 5 : "hci0"); + else if (!strncmp(str, "hci", 3)) { + /* hci[,vlan=n] */ + if (str[3]) { + if (!strncmp(str + 3, ",vlan=", 6)) { + vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0)); + if (*endp) + vlan = 0; + } + } else + vlan = qemu_find_bt_vlan(0); + if (vlan) + return bt_new_hci(vlan); + } + + fprintf(stderr, "qemu: Unknown bluetooth HCI `%s'.\n", str); + + return 0; +} + static void bt_hci_done(struct HCIInfo *info) { struct bt_hci_s *hci = hci_from_info(info); 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/char/sh_serial.c b/hw/char/sh_serial.c index 6223a557b2..9328dd1b57 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -248,11 +248,9 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, s->flags &= ~SH_SERIAL_FLAG_RDF; } break; -#if 0 case 0x18: ret = s->fcr; break; -#endif case 0x1c: ret = s->rx_cnt; break; 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/pl110.c b/hw/display/pl110.c index e79ab4bbdd..790e5108ed 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -94,23 +94,21 @@ static const VMStateDescription vmstate_pl110 = { static const unsigned char pl110_id[] = { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; -/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board - has a different ID. However Linux only looks for the normal ID. */ -#if 0 -static const unsigned char pl110_versatile_id[] = -{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; -#else -#define pl110_versatile_id pl110_id -#endif - static const unsigned char pl111_id[] = { 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; + /* Indexed by pl110_version */ static const unsigned char *idregs[] = { pl110_id, - pl110_versatile_id, + /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board + * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware + * itself has the same ID values as a stock PL110, and guests (in + * particular Linux) rely on this. We emulate what the hardware does, + * rather than what the docs claim it ought to do. + */ + pl110_id, pl111_id }; diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 269b1a7568..d34b0c4170 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -31,10 +31,6 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) if (is_buffer_shared(surface)) { return; } - if (!qxl->guest_primary.data) { - trace_qxl_render_blit_guest_primary_initialized(); - qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); - } trace_qxl_render_blit(qxl->guest_primary.qxl_stride, rect->left, rect->right, rect->top, rect->bottom); src = qxl->guest_primary.data; @@ -104,7 +100,12 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; - qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); + qxl->guest_primary.data = qxl_phys2virt(qxl, + qxl->guest_primary.surface.mem, + MEMSLOT_GROUP_GUEST); + if (!qxl->guest_primary.data) { + return; + } qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); qxl->num_dirty_rects = 1; trace_qxl_render_guest_primary_resized( @@ -128,6 +129,10 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) } dpy_gfx_replace_surface(vga->con, surface); } + + if (!qxl->guest_primary.data) { + return; + } for (i = 0; i < qxl->num_dirty_rects; i++) { if (qemu_spice_rect_is_empty(qxl->dirty+i)) { break; diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 7649f2b1f4..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; } @@ -1541,8 +1539,9 @@ async_common: default: break; } - trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), addr, val, size, - async); + trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), + addr, io_port_to_string(addr), + val, size, async); switch (io_port) { case QXL_IO_UPDATE_AREA: 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 275e39595d..c6042c7e23 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -89,7 +89,7 @@ static void pc_init1(QEMUMachineInitArgs *args, FWCfgState *fw_cfg = NULL; PcGuestInfo *guest_info; - if (xen_enabled() && xen_hvm_init() != 0) { + if (xen_enabled() && xen_hvm_init(&ram_memory) != 0) { fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); exit(1); } @@ -339,14 +339,25 @@ static void pc_xen_hvm_init(QEMUMachineInitArgs *args) .desc = "Standard PC (i440FX + PIIX, 1996)", \ .hot_add_cpu = pc_hot_add_cpu +#define PC_I440FX_1_7_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS +static QEMUMachine pc_i440fx_machine_v1_7 = { + PC_I440FX_1_7_MACHINE_OPTIONS, + .name = "pc-i440fx-1.7", + .alias = "pc", + .init = pc_init_pci, + .is_default = 1, +}; + #define PC_I440FX_1_6_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS static QEMUMachine pc_i440fx_machine_v1_6 = { PC_I440FX_1_6_MACHINE_OPTIONS, .name = "pc-i440fx-1.6", - .alias = "pc", .init = pc_init_pci_1_6, - .is_default = 1, + .compat_props = (GlobalProperty[]) { + PC_COMPAT_1_6, + { /* end of list */ } + }, }; static QEMUMachine pc_i440fx_machine_v1_5 = { @@ -730,11 +741,13 @@ 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 static void pc_machine_init(void) { + qemu_register_machine(&pc_i440fx_machine_v1_7); qemu_register_machine(&pc_i440fx_machine_v1_6); qemu_register_machine(&pc_i440fx_machine_v1_5); qemu_register_machine(&pc_i440fx_machine_v1_4); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d7b7c3bf9a..ca84e1c04c 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -76,6 +76,11 @@ static void pc_q35_init(QEMUMachineInitArgs *args) DeviceState *icc_bridge; PcGuestInfo *guest_info; + if (xen_enabled() && xen_hvm_init(&ram_memory) != 0) { + fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); + exit(1); + } + icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE); object_property_add_child(qdev_get_machine(), "icc-bridge", OBJECT(icc_bridge), NULL); @@ -258,13 +263,25 @@ static void pc_q35_init_1_4(QEMUMachineInitArgs *args) .desc = "Standard PC (Q35 + ICH9, 2009)", \ .hot_add_cpu = pc_hot_add_cpu +#define PC_Q35_1_7_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS + +static QEMUMachine pc_q35_machine_v1_7 = { + PC_Q35_1_7_MACHINE_OPTIONS, + .name = "pc-q35-1.7", + .alias = "q35", + .init = pc_q35_init, +}; + #define PC_Q35_1_6_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS static QEMUMachine pc_q35_machine_v1_6 = { PC_Q35_1_6_MACHINE_OPTIONS, .name = "pc-q35-1.6", - .alias = "q35", .init = pc_q35_init_1_6, + .compat_props = (GlobalProperty[]) { + PC_COMPAT_1_6, + { /* end of list */ } + }, }; static QEMUMachine pc_q35_machine_v1_5 = { @@ -293,6 +310,7 @@ static QEMUMachine pc_q35_machine_v1_4 = { static void pc_q35_machine_init(void) { + qemu_register_machine(&pc_q35_machine_v1_7); qemu_register_machine(&pc_q35_machine_v1_6); qemu_register_machine(&pc_q35_machine_v1_5); qemu_register_machine(&pc_q35_machine_v1_4); 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/i386/smbios.c b/hw/i386/smbios.c index e708cb8919..d3f1ee65c6 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -2,9 +2,11 @@ * SMBIOS Support * * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2013 Red Hat, Inc. * * Authors: * Alex Williamson <alex.williamson@hp.com> + * Markus Armbruster <armbru@redhat.com> * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. @@ -13,6 +15,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/config-file.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "hw/i386/smbios.h" @@ -41,10 +44,117 @@ struct smbios_table { #define SMBIOS_FIELD_ENTRY 0 #define SMBIOS_TABLE_ENTRY 1 - static uint8_t *smbios_entries; static size_t smbios_entries_len; static int smbios_type4_count = 0; +static bool smbios_immutable; + +static struct { + bool seen; + int headertype; + Location loc; +} first_opt[2]; + +static struct { + const char *vendor, *version, *date; + bool have_major_minor; + uint8_t major, minor; +} type0; + +static struct { + const char *manufacturer, *product, *version, *serial, *sku, *family; + /* uuid is in qemu_uuid[] */ +} type1; + +static QemuOptsList qemu_smbios_opts = { + .name = "smbios", + .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), + .desc = { + /* + * no elements => accept any params + * validation will happen later + */ + { /* end of list */ } + } +}; + +static const QemuOptDesc qemu_smbios_file_opts[] = { + { + .name = "file", + .type = QEMU_OPT_STRING, + .help = "binary file containing an SMBIOS element", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type0_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "vendor", + .type = QEMU_OPT_STRING, + .help = "vendor name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "date", + .type = QEMU_OPT_STRING, + .help = "release date", + },{ + .name = "release", + .type = QEMU_OPT_STRING, + .help = "revision number", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type1_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "product", + .type = QEMU_OPT_STRING, + .help = "product name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "uuid", + .type = QEMU_OPT_STRING, + .help = "UUID", + },{ + .name = "sku", + .type = QEMU_OPT_STRING, + .help = "SKU number", + },{ + .name = "family", + .type = QEMU_OPT_STRING, + .help = "family name", + }, + { /* end of list */ } +}; + +static void smbios_register_config(void) +{ + qemu_add_opts(&qemu_smbios_opts); +} + +machine_init(smbios_register_config); static void smbios_validate_table(void) { @@ -54,57 +164,33 @@ static void smbios_validate_table(void) } } -uint8_t *smbios_get_table(size_t *length) -{ - smbios_validate_table(); - *length = smbios_entries_len; - return smbios_entries; -} - /* * To avoid unresolvable overlaps in data, don't allow both * tables and fields for the same smbios type. */ static void smbios_check_collision(int type, int entry) { - uint16_t *num_entries = (uint16_t *)smbios_entries; - struct smbios_header *header; - char *p; - int i; - - if (!num_entries) - return; - - p = (char *)(num_entries + 1); - - for (i = 0; i < *num_entries; i++) { - header = (struct smbios_header *)p; - if (entry == SMBIOS_TABLE_ENTRY && header->type == SMBIOS_FIELD_ENTRY) { - struct smbios_field *field = (void *)header; - if (type == field->type) { - error_report("SMBIOS type %d field already defined, " - "cannot add table", type); - exit(1); - } - } else if (entry == SMBIOS_FIELD_ENTRY && - header->type == SMBIOS_TABLE_ENTRY) { - struct smbios_structure_header *table = (void *)(header + 1); - if (type == table->type) { - error_report("SMBIOS type %d table already defined, " - "cannot add field", type); + if (type < ARRAY_SIZE(first_opt)) { + if (first_opt[type].seen) { + if (first_opt[type].headertype != entry) { + error_report("Can't mix file= and type= for same type"); + loc_push_restore(&first_opt[type].loc); + error_report("This is the conflicting setting"); + loc_pop(&first_opt[type].loc); exit(1); } + } else { + first_opt[type].seen = true; + first_opt[type].headertype = entry; + loc_save(&first_opt[type].loc); } - p += le16_to_cpu(header->length); } } -void smbios_add_field(int type, int offset, const void *data, size_t len) +static void smbios_add_field(int type, int offset, const void *data, size_t len) { struct smbios_field *field; - smbios_check_collision(type, SMBIOS_FIELD_ENTRY); - if (!smbios_entries) { smbios_entries_len = sizeof(uint16_t); smbios_entries = g_malloc0(smbios_entries_len); @@ -124,76 +210,94 @@ void smbios_add_field(int type, int offset, const void *data, size_t len) cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); } -static void smbios_build_type_0_fields(const char *t) +static void smbios_maybe_add_str(int type, int offset, const char *data) { - char buf[1024]; - unsigned char major, minor; - - if (get_param_value(buf, sizeof(buf), "vendor", t)) - smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "version", t)) - smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "date", t)) - smbios_add_field(0, offsetof(struct smbios_type_0, + if (data) { + smbios_add_field(type, offset, data, strlen(data) + 1); + } +} + +static void smbios_build_type_0_fields(void) +{ + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), + type0.vendor); + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), + type0.version); + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_release_date_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "release", t)) { - if (sscanf(buf, "%hhu.%hhu", &major, &minor) != 2) { - error_report("Invalid release"); - exit(1); - } + type0.date); + if (type0.have_major_minor) { smbios_add_field(0, offsetof(struct smbios_type_0, system_bios_major_release), - &major, 1); + &type0.major, 1); smbios_add_field(0, offsetof(struct smbios_type_0, system_bios_minor_release), - &minor, 1); + &type0.minor, 1); } } -static void smbios_build_type_1_fields(const char *t) +static void smbios_build_type_1_fields(void) { - char buf[1024]; - - if (get_param_value(buf, sizeof(buf), "manufacturer", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "product", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "version", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, version_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "serial", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "uuid", t)) { - if (qemu_uuid_parse(buf, qemu_uuid) != 0) { - error_report("Invalid UUID"); - exit(1); - } + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str), + type1.manufacturer); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), + type1.product); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), + type1.version); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), + type1.serial); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), + type1.sku); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), + type1.family); + if (qemu_uuid_set) { + smbios_add_field(1, offsetof(struct smbios_type_1, uuid), + qemu_uuid, 16); + } +} + +uint8_t *smbios_get_table(size_t *length) +{ + if (!smbios_immutable) { + smbios_build_type_0_fields(); + smbios_build_type_1_fields(); + smbios_validate_table(); + smbios_immutable = true; + } + *length = smbios_entries_len; + return smbios_entries; +} + +static void save_opt(const char **dest, QemuOpts *opts, const char *name) +{ + const char *val = qemu_opt_get(opts, name); + + if (val) { + *dest = val; } - if (get_param_value(buf, sizeof(buf), "sku", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "family", t)) - smbios_add_field(1, offsetof(struct smbios_type_1, family_str), - buf, strlen(buf) + 1); } -int smbios_entry_add(const char *t) +void smbios_entry_add(QemuOpts *opts) { - char buf[1024]; + Error *local_err = NULL; + const char *val; - if (get_param_value(buf, sizeof(buf), "file", t)) { + assert(!smbios_immutable); + val = qemu_opt_get(opts, "file"); + if (val) { struct smbios_structure_header *header; struct smbios_table *table; - int size = get_image_size(buf); + int size; + qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + + size = get_image_size(val); if (size == -1 || size < sizeof(struct smbios_structure_header)) { - error_report("Cannot read SMBIOS file %s", buf); + error_report("Cannot read SMBIOS file %s", val); exit(1); } @@ -208,8 +312,8 @@ int smbios_entry_add(const char *t) table->header.type = SMBIOS_TABLE_ENTRY; table->header.length = cpu_to_le16(sizeof(*table) + size); - if (load_image(buf, table->data) != size) { - error_report("Failed to load SMBIOS file %s", buf); + if (load_image(val, table->data) != size) { + error_report("Failed to load SMBIOS file %s", val); exit(1); } @@ -222,18 +326,57 @@ int smbios_entry_add(const char *t) smbios_entries_len += sizeof(*table) + size; (*(uint16_t *)smbios_entries) = cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); - return 0; + return; } - if (get_param_value(buf, sizeof(buf), "type", t)) { - unsigned long type = strtoul(buf, NULL, 0); + val = qemu_opt_get(opts, "type"); + if (val) { + unsigned long type = strtoul(val, NULL, 0); + + smbios_check_collision(type, SMBIOS_FIELD_ENTRY); + switch (type) { case 0: - smbios_build_type_0_fields(t); - return 0; + qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type0.vendor, opts, "vendor"); + save_opt(&type0.version, opts, "version"); + save_opt(&type0.date, opts, "date"); + + val = qemu_opt_get(opts, "release"); + if (val) { + if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { + error_report("Invalid release"); + exit(1); + } + type0.have_major_minor = true; + } + return; case 1: - smbios_build_type_1_fields(t); - return 0; + qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type1.manufacturer, opts, "manufacturer"); + save_opt(&type1.product, opts, "product"); + save_opt(&type1.version, opts, "version"); + save_opt(&type1.serial, opts, "serial"); + save_opt(&type1.sku, opts, "sku"); + save_opt(&type1.family, opts, "family"); + + val = qemu_opt_get(opts, "uuid"); + if (val) { + if (qemu_uuid_parse(val, qemu_uuid) != 0) { + error_report("Invalid UUID"); + exit(1); + } + qemu_uuid_set = true; + } + return; default: error_report("Don't know how to build fields for SMBIOS type %ld", type); @@ -242,5 +385,5 @@ int smbios_entry_add(const char *t) } error_report("Must specify type= or file="); - return -1; + exit(1); } diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index ae0921c6aa..05c8771220 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -827,7 +827,8 @@ static int64_t load_kernel (void) } prom_set(prom_buf, prom_index++, "memsize"); - prom_set(prom_buf, prom_index++, "%i", loaderparams.ram_size); + prom_set(prom_buf, prom_index++, "%i", + MIN(loaderparams.ram_size, 256 << 20)); prom_set(prom_buf, prom_index++, "modetty0"); prom_set(prom_buf, prom_index++, "38400n8r"); prom_set(prom_buf, prom_index++, NULL); @@ -884,7 +885,9 @@ void mips_malta_init(QEMUMachineInitArgs *args) char *filename; pflash_t *fl; MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *ram_high = g_new(MemoryRegion, 1); + MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1); + MemoryRegion *ram_low_postio; MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1); target_long bios_size = FLASH_SIZE; const size_t smbus_eeprom_size = 8 * 256; @@ -951,15 +954,32 @@ void mips_malta_init(QEMUMachineInitArgs *args) env = &cpu->env; /* allocate RAM */ - if (ram_size > (256 << 20)) { + if (ram_size > (2048u << 20)) { fprintf(stderr, - "qemu: Too much memory for this machine: %d MB, maximum 256 MB\n", + "qemu: Too much memory for this machine: %d MB, maximum 2048 MB\n", ((unsigned int)ram_size / (1 << 20))); exit(1); } - memory_region_init_ram(ram, NULL, "mips_malta.ram", ram_size); - vmstate_register_ram_global(ram); - memory_region_add_subregion(system_memory, 0, ram); + + /* register RAM at high address where it is undisturbed by IO */ + memory_region_init_ram(ram_high, NULL, "mips_malta.ram", ram_size); + vmstate_register_ram_global(ram_high); + memory_region_add_subregion(system_memory, 0x80000000, ram_high); + + /* alias for pre IO hole access */ + memory_region_init_alias(ram_low_preio, NULL, "mips_malta_low_preio.ram", + ram_high, 0, MIN(ram_size, (256 << 20))); + memory_region_add_subregion(system_memory, 0, ram_low_preio); + + /* alias for post IO hole access, if there is enough RAM */ + if (ram_size > (512 << 20)) { + ram_low_postio = g_new(MemoryRegion, 1); + memory_region_init_alias(ram_low_postio, NULL, + "mips_malta_low_postio.ram", + ram_high, 512 << 20, + ram_size - (512 << 20)); + memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio); + } /* generate SPD EEPROM data */ generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size); @@ -992,7 +1012,7 @@ void mips_malta_init(QEMUMachineInitArgs *args) fl_idx++; if (kernel_filename) { /* Write a small bootloader to the flash location. */ - loaderparams.ram_size = ram_size; + loaderparams.ram_size = MIN(ram_size, 256 << 20); loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; diff --git a/hw/net/e1000.c b/hw/net/e1000.c index f5ebed46ab..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 @@ -135,9 +138,16 @@ typedef struct E1000State_st { QEMUTimer *autoneg_timer; + QEMUTimer *mit_timer; /* Mitigation timer. */ + bool mit_timer_on; /* Mitigation timer is running. */ + bool mit_irq_level; /* Tracks interrupt pin level. */ + uint32_t mit_ide; /* Tracks E1000_TXD_CMD_IDE bit. */ + /* Compatibility flags for migration to/from qemu 1.3.0 and older */ #define E1000_FLAG_AUTONEG_BIT 0 +#define E1000_FLAG_MIT_BIT 1 #define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT) +#define E1000_FLAG_MIT (1 << E1000_FLAG_MIT_BIT) uint32_t compat_flags; } E1000State; @@ -158,7 +168,8 @@ enum { defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL), defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC), defreg(RA), defreg(MTA), defreg(CRCERRS),defreg(VFTA), - defreg(VET), + defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV), + defreg(ITR), }; static void @@ -245,10 +256,21 @@ static const uint32_t mac_reg_init[] = { E1000_MANC_RMCP_EN, }; +/* Helper function, *curr == 0 means the value is not set */ +static inline void +mit_update_delay(uint32_t *curr, uint32_t value) +{ + if (value && (*curr == 0 || value < *curr)) { + *curr = value; + } +} + static void set_interrupt_cause(E1000State *s, int index, uint32_t val) { PCIDevice *d = PCI_DEVICE(s); + uint32_t pending_ints; + uint32_t mit_delay; if (val && (E1000_DEVID >= E1000_DEV_ID_82547EI_MOBILE)) { /* Only for 8257x */ @@ -266,7 +288,57 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) */ s->mac_reg[ICS] = val; - qemu_set_irq(d->irq[0], (s->mac_reg[IMS] & s->mac_reg[ICR]) != 0); + pending_ints = (s->mac_reg[IMS] & s->mac_reg[ICR]); + if (!s->mit_irq_level && pending_ints) { + /* + * Here we detect a potential raising edge. We postpone raising the + * interrupt line if we are inside the mitigation delay window + * (s->mit_timer_on == 1). + * We provide a partial implementation of interrupt mitigation, + * emulating only RADV, TADV and ITR (lower 16 bits, 1024ns units for + * RADV and TADV, 256ns units for ITR). RDTR is only used to enable + * RADV; relative timers based on TIDV and RDTR are not implemented. + */ + if (s->mit_timer_on) { + return; + } + if (s->compat_flags & E1000_FLAG_MIT) { + /* Compute the next mitigation delay according to pending + * interrupts and the current values of RADV (provided + * RDTR!=0), TADV and ITR. + * Then rearm the timer. + */ + mit_delay = 0; + if (s->mit_ide && + (pending_ints & (E1000_ICR_TXQE | E1000_ICR_TXDW))) { + mit_update_delay(&mit_delay, s->mac_reg[TADV] * 4); + } + if (s->mac_reg[RDTR] && (pending_ints & E1000_ICS_RXT0)) { + mit_update_delay(&mit_delay, s->mac_reg[RADV] * 4); + } + mit_update_delay(&mit_delay, s->mac_reg[ITR]); + + if (mit_delay) { + s->mit_timer_on = 1; + timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + mit_delay * 256); + } + s->mit_ide = 0; + } + } + + s->mit_irq_level = (pending_ints != 0); + qemu_set_irq(d->irq[0], s->mit_irq_level); +} + +static void +e1000_mit_timer(void *opaque) +{ + E1000State *s = opaque; + + s->mit_timer_on = 0; + /* Call set_interrupt_cause to update the irq level (if necessary). */ + set_interrupt_cause(s, 0, s->mac_reg[ICR]); } static void @@ -307,6 +379,10 @@ static void e1000_reset(void *opaque) int i; timer_del(d->autoneg_timer); + timer_del(d->mit_timer); + d->mit_timer_on = 0; + d->mit_irq_level = 0; + d->mit_ide = 0; memset(d->phy_reg, 0, sizeof d->phy_reg); memmove(d->phy_reg, phy_reg_init, sizeof phy_reg_init); memset(d->mac_reg, 0, sizeof d->mac_reg); @@ -572,6 +648,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) struct e1000_context_desc *xp = (struct e1000_context_desc *)dp; struct e1000_tx *tp = &s->tx; + s->mit_ide |= (txd_lower & E1000_TXD_CMD_IDE); if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor op = le32_to_cpu(xp->cmd_and_length); tp->ipcss = xp->lower_setup.ip_fields.ipcss; @@ -825,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); @@ -834,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; @@ -850,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. */ @@ -864,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; } @@ -893,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); @@ -948,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) { @@ -1047,7 +1166,8 @@ static uint32_t (*macreg_readops[])(E1000State *, int) = { getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL), getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS), getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL), - getreg(TDLEN), getreg(RDLEN), + getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV), + getreg(TADV), getreg(ITR), [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4, [GPTC] = mac_read_clr4, [TPR] = mac_read_clr4, [TPT] = mac_read_clr4, @@ -1069,6 +1189,8 @@ static void (*macreg_writeops[])(E1000State *, int, uint32_t) = { [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt, [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr, [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl, + [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit, + [ITR] = set_16bit, [RA ... RA+31] = &mac_writereg, [MTA ... MTA+127] = &mac_writereg, [VFTA ... VFTA+127] = &mac_writereg, @@ -1150,6 +1272,11 @@ static void e1000_pre_save(void *opaque) E1000State *s = opaque; NetClientState *nc = qemu_get_queue(s->nic); + /* If the mitigation timer is active, emulate a timeout now. */ + if (s->mit_timer_on) { + e1000_mit_timer(s); + } + if (!(s->compat_flags & E1000_FLAG_AUTONEG)) { return; } @@ -1171,6 +1298,14 @@ static int e1000_post_load(void *opaque, int version_id) E1000State *s = opaque; NetClientState *nc = qemu_get_queue(s->nic); + if (!(s->compat_flags & E1000_FLAG_MIT)) { + s->mac_reg[ITR] = s->mac_reg[RDTR] = s->mac_reg[RADV] = + s->mac_reg[TADV] = 0; + s->mit_irq_level = false; + } + s->mit_ide = 0; + s->mit_timer_on = false; + /* nc.link_down can't be migrated, so infer link_down according * to link status bit in mac_reg[STATUS]. * Alternatively, restart link negotiation if it was in progress. */ @@ -1190,6 +1325,28 @@ static int e1000_post_load(void *opaque, int version_id) return 0; } +static bool e1000_mit_state_needed(void *opaque) +{ + E1000State *s = opaque; + + return s->compat_flags & E1000_FLAG_MIT; +} + +static const VMStateDescription vmstate_e1000_mit_state = { + .name = "e1000/mit_state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(mac_reg[RDTR], E1000State), + VMSTATE_UINT32(mac_reg[RADV], E1000State), + VMSTATE_UINT32(mac_reg[TADV], E1000State), + VMSTATE_UINT32(mac_reg[ITR], E1000State), + VMSTATE_BOOL(mit_irq_level, E1000State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_e1000 = { .name = "e1000", .version_id = 2, @@ -1267,6 +1424,14 @@ static const VMStateDescription vmstate_e1000 = { VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, 128), VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_e1000_mit_state, + .needed = e1000_mit_state_needed, + }, { + /* empty */ + } } }; @@ -1316,6 +1481,8 @@ pci_e1000_uninit(PCIDevice *dev) timer_del(d->autoneg_timer); timer_free(d->autoneg_timer); + timer_del(d->mit_timer); + timer_free(d->mit_timer); memory_region_destroy(&d->mmio); memory_region_destroy(&d->io); qemu_del_nic(d->nic); @@ -1326,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, }; @@ -1371,6 +1539,7 @@ static int pci_e1000_init(PCIDevice *pci_dev) add_boot_device_path(d->conf.bootindex, dev, "/ethernet-phy@0"); d->autoneg_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, e1000_autoneg_timer, d); + d->mit_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000_mit_timer, d); return 0; } @@ -1385,6 +1554,8 @@ static Property e1000_properties[] = { DEFINE_NIC_PROPERTIES(E1000State, conf), DEFINE_PROP_BIT("autonegotiation", E1000State, compat_flags, E1000_FLAG_AUTONEG_BIT, true), + DEFINE_PROP_BIT("mitigation", E1000State, + compat_flags, E1000_FLAG_MIT_BIT, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 31afd28c7c..c96125895e 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -693,7 +693,7 @@ static void ne2000_write(void *opaque, hwaddr addr, static const MemoryRegionOps ne2000_ops = { .read = ne2000_read, .write = ne2000_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; /***********************************************************/ 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/net/virtio-net.c b/hw/net/virtio-net.c index dd41008fb0..22dbd053d4 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -314,6 +314,7 @@ static void virtio_net_reset(VirtIODevice *vdev) n->mac_table.uni_overflow = 0; memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); + qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); memset(n->vlans, 0, MAX_VLAN >> 3); } diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h index 4eae7c76be..f987d71269 100644 --- a/hw/net/vmxnet3.h +++ b/hw/net/vmxnet3.h @@ -34,7 +34,6 @@ #define __le16 uint16_t #define __le32 uint32_t #define __le64 uint64_t -#define __packed QEMU_PACKED #if defined(HOST_WORDS_BIGENDIAN) #define __BIG_ENDIAN_BITFIELD @@ -749,7 +748,6 @@ struct Vmxnet3_DriverShared { #undef __le16 #undef __le32 #undef __le64 -#undef __packed #if defined(HOST_WORDS_BIGENDIAN) #undef __BIG_ENDIAN_BITFIELD #endif diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index 221d82b637..c041149320 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -235,18 +235,24 @@ static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { - I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; + + pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &s->pci_info.w64.begin, name, errp); + visit_type_uint64(v, &w64.begin, name, errp); } static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { - I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; + + pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &s->pci_info.w64.end, name, errp); + visit_type_uint64(v, &w64.end, name, errp); } static void i440fx_pcihost_initfn(Object *obj) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 54735043b7..ad703a4bf7 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -89,18 +89,24 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { - Q35PCIHost *s = Q35_HOST_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; + + pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &s->mch.pci_info.w64.begin, name, errp); + visit_type_uint64(v, &w64.begin, name, errp); } static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { - Q35PCIHost *s = Q35_HOST_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; - visit_type_uint64(v, &s->mch.pci_info.w64.end, name, errp); + pci_bus_get_w64_range(h->bus, &w64); + + visit_type_uint64(v, &w64.end, name, errp); } static Property mch_props[] = { @@ -214,6 +220,16 @@ static void mch_update_pciexbar(MCHPCIState *mch) } addr = pciexbar & addr_mask; pcie_host_mmcfg_update(pehb, enable, addr, length); + /* Leave enough space for the MCFG BAR */ + /* + * TODO: this matches current bios behaviour, but it's not a power of two, + * which means an MTRR can't cover it exactly. + */ + if (enable) { + mch->pci_info.w32.begin = addr + length; + } else { + mch->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; + } } /* PAM */ @@ -372,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/pci/pci.c b/hw/pci/pci.c index d00682e134..00554a05ac 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1028,8 +1028,10 @@ static pcibus_t pci_bar_address(PCIDevice *d, } new_addr = pci_get_long(d->config + bar) & ~(size - 1); last_addr = new_addr + size - 1; - /* NOTE: we have only 64K ioports on PC */ - if (last_addr <= new_addr || new_addr == 0 || last_addr > UINT16_MAX) { + /* Check if 32 bit BAR wraps around explicitly. + * TODO: make priorities correct and remove this work around. + */ + if (last_addr <= new_addr || new_addr == 0 || last_addr >= UINT32_MAX) { return PCI_BAR_UNMAPPED; } return new_addr; @@ -1461,7 +1463,7 @@ static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, info->function = PCI_FUNC(dev->devfn); class = pci_get_word(dev->config + PCI_CLASS_DEVICE); - info->class_info.class = class; + info->class_info.q_class = class; desc = get_class_desc(class); if (desc->desc) { info->class_info.has_desc = true; @@ -2257,6 +2259,56 @@ void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque) bus->iommu_opaque = opaque; } +static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) +{ + Range *range = opaque; + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND); + int r; + + if (!(cmd & PCI_COMMAND_MEMORY)) { + return; + } + + if (pc->is_bridge) { + pcibus_t base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + pcibus_t limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + base = MAX(base, 0x1ULL << 32); + + if (limit >= base) { + Range pref_range; + pref_range.begin = base; + pref_range.end = limit + 1; + range_extend(range, &pref_range); + } + } + for (r = 0; r < PCI_NUM_REGIONS; ++r) { + PCIIORegion *region = &dev->io_regions[r]; + Range region_range; + + if (!region->size || + (region->type & PCI_BASE_ADDRESS_SPACE_IO) || + !(region->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { + continue; + } + region_range.begin = pci_get_quad(dev->config + pci_bar(dev, r)); + region_range.end = region_range.begin + region->size; + + region_range.begin = MAX(region_range.begin, 0x1ULL << 32); + + if (region_range.end - 1 >= region_range.begin) { + range_extend(range, ®ion_range); + } + } +} + +void pci_bus_get_w64_range(PCIBus *bus, Range *range) +{ + range->begin = range->end = 0; + pci_for_each_device_under_bus(bus, pci_dev_get_w64, range); +} + static const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, 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..24ec52f8f9 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -11,6 +11,8 @@ static char *scsibus_get_dev_path(DeviceState *dev); static char *scsibus_get_fw_dev_path(DeviceState *dev); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); static void scsi_req_dequeue(SCSIRequest *req); +static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len); +static void scsi_target_free_buf(SCSIRequest *req); static Property scsi_props[] = { DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), @@ -224,7 +226,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) { @@ -317,7 +319,8 @@ typedef struct SCSITargetReq SCSITargetReq; struct SCSITargetReq { SCSIRequest req; int len; - uint8_t buf[2056]; + uint8_t *buf; + int buf_len; }; static void store_lun(uint8_t *outbuf, int lun) @@ -361,14 +364,12 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r) if (!found_lun0) { n += 8; } - len = MIN(n + 8, r->req.cmd.xfer & ~7); - if (len > sizeof(r->buf)) { - /* TODO: > 256 LUNs? */ - return false; - } + scsi_target_alloc_buf(&r->req, n + 8); + + len = MIN(n + 8, r->req.cmd.xfer & ~7); memset(r->buf, 0, len); - stl_be_p(&r->buf, n); + stl_be_p(&r->buf[0], n); i = found_lun0 ? 8 : 16; QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) { DeviceState *qdev = kid->child; @@ -387,6 +388,9 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r) static bool scsi_target_emulate_inquiry(SCSITargetReq *r) { assert(r->req.dev->lun != r->req.lun); + + scsi_target_alloc_buf(&r->req, SCSI_INQUIRY_LEN); + if (r->req.cmd.buf[1] & 0x2) { /* Command support data - optional, not implemented */ return false; @@ -411,7 +415,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) return false; } /* done with EVPD */ - assert(r->len < sizeof(r->buf)); + assert(r->len < r->buf_len); r->len = MIN(r->req.cmd.xfer, r->len); return true; } @@ -422,7 +426,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) } /* PAGE CODE == 0 */ - r->len = MIN(r->req.cmd.xfer, 36); + r->len = MIN(r->req.cmd.xfer, SCSI_INQUIRY_LEN); memset(r->buf, 0, r->len); if (r->req.lun != 0) { r->buf[0] = TYPE_NO_LUN; @@ -455,8 +459,9 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) } break; case REQUEST_SENSE: + scsi_target_alloc_buf(&r->req, SCSI_SENSE_LEN); r->len = scsi_device_get_sense(r->req.dev, r->buf, - MIN(req->cmd.xfer, sizeof r->buf), + MIN(req->cmd.xfer, r->buf_len), (req->cmd.buf[1] & 1) == 0); if (r->req.dev->sense_is_ua) { scsi_device_unit_attention_reported(req->dev); @@ -501,11 +506,29 @@ static uint8_t *scsi_target_get_buf(SCSIRequest *req) return r->buf; } +static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len) +{ + SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); + + r->buf = g_malloc(len); + r->buf_len = len; + + return r->buf; +} + +static void scsi_target_free_buf(SCSIRequest *req) +{ + SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); + + g_free(r->buf); +} + static const struct SCSIReqOps reqops_target_command = { .size = sizeof(SCSITargetReq), .send_command = scsi_target_send_command, .read_data = scsi_target_read_data, .get_buf = scsi_target_get_buf, + .free_req = scsi_target_free_buf, }; @@ -1365,7 +1388,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len, buf[7] = 10; buf[12] = sense.asc; buf[13] = sense.ascq; - return MIN(len, 18); + return MIN(len, SCSI_SENSE_LEN); } else { /* Return descriptor format sense buffer */ buf[0] = 0x72; 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/Makefile.objs b/hw/usb/Makefile.objs index f9695e7d8a..a3eac3e5c1 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -18,9 +18,6 @@ common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o common-obj-$(CONFIG_USB_NETWORK) += dev-network.o - -# FIXME: make configurable too -CONFIG_USB_BLUETOOTH := y common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o ifeq ($(CONFIG_USB_SMARTCARD),y) diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 82ca6a13e8..72d5b92225 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -47,6 +47,10 @@ static int usb_device_post_load(void *opaque, int version_id) } else { dev->attached = 1; } + if (dev->setup_index >= sizeof(dev->data_buf) || + dev->setup_len >= sizeof(dev->data_buf)) { + return -EINVAL; + } return 0; } 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/dev-bluetooth.c b/hw/usb/dev-bluetooth.c index f2fc2a8034..7f292b1ae6 100644 --- a/hw/usb/dev-bluetooth.c +++ b/hw/usb/dev-bluetooth.c @@ -511,10 +511,17 @@ static int usb_bt_initfn(USBDevice *dev) return 0; } -USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci) +static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline) { USBDevice *dev; struct USBBtState *s; + HCIInfo *hci; + + if (*cmdline) { + hci = hci_init(cmdline); + } else { + hci = bt_new_hci(qemu_find_bt_vlan(0)); + } if (!hci) return NULL; @@ -566,6 +573,7 @@ static const TypeInfo bt_info = { static void usb_bt_register_types(void) { type_register_static(&bt_info); + usb_legacy_register("usb-bt-dongle", "bt", usb_bt_init); } type_init(usb_bt_register_types) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 137e200202..22bdbf4a7d 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1241,13 +1241,11 @@ static int ehci_init_transfer(EHCIPacket *p) { uint32_t cpage, offset, bytes, plen; dma_addr_t page; - USBBus *bus = &p->queue->ehci->bus; - BusState *qbus = BUS(bus); cpage = get_field(p->qtd.token, QTD_TOKEN_CPAGE); bytes = get_field(p->qtd.token, QTD_TOKEN_TBYTES); offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK; - qemu_sglist_init(&p->sgl, qbus->parent, 5, p->queue->ehci->as); + qemu_sglist_init(&p->sgl, p->queue->ehci->device, 5, p->queue->ehci->as); while (bytes > 0) { if (cpage > 4) { @@ -1486,7 +1484,7 @@ static int ehci_process_itd(EHCIState *ehci, return -1; } - qemu_sglist_init(&ehci->isgl, DEVICE(ehci), 2, ehci->as); + qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as); if (off + len > 4096) { /* transfer crosses page border */ uint32_t len2 = off + len - 4096; @@ -2529,6 +2527,7 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ehci_frame_timer, s); s->async_bh = qemu_bh_new(ehci_frame_timer, s); + s->device = dev; qemu_register_reset(ehci_reset, s); qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 15a28e8b31..065c9fa741 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -255,6 +255,7 @@ typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; struct EHCIState { USBBus bus; + DeviceState *device; qemu_irq irq; MemoryRegion mem; AddressSpace *as; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 35f0878409..0396e334ed 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1143,7 +1143,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) switch (ret) { case USB_RET_IOERROR: case USB_RET_NODEV: + DPRINTF("usb-ohci: got DEV ERROR\n"); OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); + break; case USB_RET_NAK: DPRINTF("usb-ohci: got NAK\n"); return 1; 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/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index d82ce5d8a6..197795ffe1 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -205,7 +205,6 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, struct XenDevOps *ops) { struct XenDevice *xendev; - char *dom0; xendev = xen_be_find_xendev(type, dom, dev); if (xendev) { @@ -219,12 +218,10 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, xendev->dev = dev; xendev->ops = ops; - dom0 = xs_get_domain_path(xenstore, 0); - snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d", - dom0, xendev->type, xendev->dom, xendev->dev); + snprintf(xendev->be, sizeof(xendev->be), "backend/%s/%d/%d", + xendev->type, xendev->dom, xendev->dev); snprintf(xendev->name, sizeof(xendev->name), "%s-%d", xendev->type, xendev->dev); - free(dom0); xendev->debug = debug; xendev->local_port = -1; @@ -570,14 +567,12 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) { struct XenDevice *xendev; char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; - char **dev = NULL, *dom0; + char **dev = NULL; unsigned int cdev, j; /* setup watch */ - dom0 = xs_get_domain_path(xenstore, 0); snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); - snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); - free(dom0); + snprintf(path, sizeof(path), "backend/%s/%d", type, dom); if (!xs_watch(xenstore, path, token)) { xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); return -1; @@ -603,12 +598,10 @@ static void xenstore_update_be(char *watch, char *type, int dom, struct XenDevOps *ops) { struct XenDevice *xendev; - char path[XEN_BUFSIZE], *dom0, *bepath; + char path[XEN_BUFSIZE], *bepath; unsigned int len, dev; - dom0 = xs_get_domain_path(xenstore, 0); - len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); - free(dom0); + len = snprintf(path, sizeof(path), "backend/%s/%d", type, dom); if (strncmp(path, watch, len) != 0) { return; } diff --git a/include/block/block.h b/include/block/block.h index e6b391ce88..f808550959 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -81,6 +81,32 @@ typedef struct BlockDevOps { #define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) #define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1) +/* BDRV_BLOCK_DATA: data is read from bs->file or another file + * BDRV_BLOCK_ZERO: sectors read as zero + * BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data + * + * If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 represent the offset in + * bs->file where sector data can be read from as raw data. + * + * DATA == 0 && ZERO == 0 means that data is read from backing_hd if present. + * + * DATA ZERO OFFSET_VALID + * t t t sectors read as zero, bs->file is zero at offset + * t f t sectors read as valid from bs->file at offset + * f t t sectors preallocated, read as zero, bs->file not + * necessarily zero at offset + * f f t sectors preallocated but read from backing_hd, + * bs->file contains garbage at offset + * t t f sectors preallocated, read as zero, unknown offset + * t f f sectors read from unknown file or offset + * f t f not allocated or unknown offset, read as zero + * f f f not allocated or unknown offset, read from backing_hd + */ +#define BDRV_BLOCK_DATA 1 +#define BDRV_BLOCK_ZERO 2 +#define BDRV_BLOCK_OFFSET_VALID 4 +#define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK + typedef enum { BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP } BlockErrorAction; @@ -107,7 +133,6 @@ void bdrv_info_stats(Monitor *mon, QObject **ret_data); /* disk I/O throttling */ void bdrv_io_limits_enable(BlockDriverState *bs); void bdrv_io_limits_disable(BlockDriverState *bs); -bool bdrv_io_limits_enabled(BlockDriverState *bs); void bdrv_init(void); void bdrv_init_with_whitelist(void); @@ -117,20 +142,20 @@ 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); void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); -void bdrv_delete(BlockDriverState *bs); int bdrv_parse_cache_flags(const char *mode, int *flags); int bdrv_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); @@ -181,12 +206,6 @@ int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, */ int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors); -int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum); -int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top, - BlockDriverState *base, - int64_t sector_num, - int nb_sectors, int *pnum); BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, const char *backing_file); int bdrv_get_backing_file_depth(BlockDriverState *bs); @@ -223,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); @@ -277,6 +298,8 @@ int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); int bdrv_has_zero_init_1(BlockDriverState *bs); int bdrv_has_zero_init(BlockDriverState *bs); +int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum); int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, @@ -356,6 +379,8 @@ int64_t bdrv_get_dirty_count(BlockDriverState *bs); void bdrv_enable_copy_on_read(BlockDriverState *bs); void bdrv_disable_copy_on_read(BlockDriverState *bs); +void bdrv_ref(BlockDriverState *bs); +void bdrv_unref(BlockDriverState *bs); void bdrv_set_in_use(BlockDriverState *bs, int in_use); int bdrv_in_use(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index 8012e253c9..211087aa91 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -35,18 +35,12 @@ #include "qemu/hbitmap.h" #include "block/snapshot.h" #include "qemu/main-loop.h" +#include "qemu/throttle.h" #define BLOCK_FLAG_ENCRYPT 1 #define BLOCK_FLAG_COMPAT6 4 #define BLOCK_FLAG_LAZY_REFCOUNTS 8 -#define BLOCK_IO_LIMIT_READ 0 -#define BLOCK_IO_LIMIT_WRITE 1 -#define BLOCK_IO_LIMIT_TOTAL 2 - -#define BLOCK_IO_SLICE_TIME 100000000 -#define NANOSECONDS_PER_SECOND 1000000000.0 - #define BLOCK_OPT_SIZE "size" #define BLOCK_OPT_ENCRYPT "encryption" #define BLOCK_OPT_COMPAT6 "compat6" @@ -70,17 +64,6 @@ typedef struct BdrvTrackedRequest { CoQueue wait_queue; /* coroutines blocked on this request */ } BdrvTrackedRequest; - -typedef struct BlockIOLimit { - int64_t bps[3]; - int64_t iops[3]; -} BlockIOLimit; - -typedef struct BlockIOBaseValue { - uint64_t bytes[2]; - uint64_t ios[2]; -} BlockIOBaseValue; - struct BlockDriver { const char *format_name; int instance_size; @@ -90,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, @@ -97,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 */ @@ -135,7 +127,7 @@ struct BlockDriver { int64_t sector_num, int nb_sectors); int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs, int64_t sector_num, int nb_sectors); - int coroutine_fn (*bdrv_co_is_allocated)(BlockDriverState *bs, + int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); /* @@ -167,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, @@ -205,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? */ @@ -264,13 +262,9 @@ struct BlockDriverState { /* number of in-flight copy-on-read requests */ unsigned int copy_on_read_in_flight; - /* the time for latest disk I/O */ - int64_t slice_start; - int64_t slice_end; - BlockIOLimit io_limits; - BlockIOBaseValue slice_submitted; - CoQueue throttled_reqs; - QEMUTimer *block_timer; + /* I/O throttling */ + ThrottleState throttle_state; + CoQueue throttled_reqs[2]; bool io_limits_enabled; /* I/O stats (display with "info blockstats"). */ @@ -298,6 +292,7 @@ struct BlockDriverState { BlockDeviceIoStatus iostatus; char device_name[32]; HBitmap *dirty_bitmap; + int refcnt; int in_use; /* users other than guest access, eg. block migration */ QTAILQ_ENTRY(BlockDriverState) list; @@ -312,7 +307,8 @@ struct BlockDriverState { int get_tmp_filename(char *filename, int size); void bdrv_set_io_limits(BlockDriverState *bs, - BlockIOLimit *io_limits); + ThrottleConfig *cfg); + /** * bdrv_add_before_write_notifier: 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..8dd15948d8 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -324,23 +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__) -/* 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) -static inline uintptr_t tcg_getra_ldst(uintptr_t ra) -{ - int32_t b; - ra += 8; /* skip the two insns */ - b = *(int32_t *)ra; /* load the branch insn */ - b = (b << 8) >> (8 - 2); /* extract the displacement */ - ra += 8; /* branches are relative to pc+8 */ - ra += b; /* apply the displacement */ - return ra; -} -# elif defined(__aarch64__) +# if defined(__aarch64__) # define GETRA_LDST(RA) tcg_getra_ldst(RA) static inline uintptr_t tcg_getra_ldst(uintptr_t ra) { @@ -368,6 +352,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/exec/ioport.h b/include/exec/ioport.h index bdd4e964eb..b3848be684 100644 --- a/include/exec/ioport.h +++ b/include/exec/ioport.h @@ -45,6 +45,10 @@ typedef struct MemoryRegionPortio { #define PORTIO_END_OF_LIST() { } +#ifndef CONFIG_USER_ONLY +extern const MemoryRegionOps unassigned_io_ops; +#endif + void cpu_outb(pio_addr_t addr, uint8_t val); void cpu_outw(pio_addr_t addr, uint16_t val); void cpu_outl(pio_addr_t addr, uint32_t val); diff --git a/include/exec/user/abitypes.h b/include/exec/user/abitypes.h index 008501b8d8..80eedaccff 100644 --- a/include/exec/user/abitypes.h +++ b/include/exec/user/abitypes.h @@ -14,10 +14,6 @@ #define ABI_LLONG_ALIGNMENT 2 #endif -#ifdef TARGET_ARM -#define ABI_LLONG_ALIGNMENT 4 -#endif - #ifndef ABI_SHORT_ALIGNMENT #define ABI_SHORT_ALIGNMENT 2 #endif diff --git a/include/hw/bt.h b/include/hw/bt.h index 3f365bcbcb..cb2a7e6579 100644 --- a/include/hw/bt.h +++ b/include/hw/bt.h @@ -108,12 +108,15 @@ struct bt_device_s { uint16_t clkoff; /* Note: Always little-endian */ }; +extern struct HCIInfo null_hci; /* bt.c */ void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net); void bt_device_done(struct bt_device_s *dev); +struct bt_scatternet_s *qemu_find_bt_vlan(int id); /* bt-hci.c */ struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net); +struct HCIInfo *hci_init(const char *str); /* bt-vhci.c */ void bt_vhci_init(struct HCIInfo *info); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 7fb04d8cd8..9b2ddc4acc 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -225,7 +225,15 @@ void pvpanic_init(ISABus *bus); int e820_add_entry(uint64_t, uint64_t, uint32_t); +#define PC_COMPAT_1_6 \ + {\ + .driver = "e1000",\ + .property = "mitigation",\ + .value = "off",\ + } + #define PC_COMPAT_1_5 \ + PC_COMPAT_1_6, \ {\ .driver = "Conroe-" TYPE_X86_CPU,\ .property = "model",\ diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 9babeaf270..b08ec713f2 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -13,8 +13,9 @@ * */ -int smbios_entry_add(const char *t); -void smbios_add_field(int type, int offset, const void *data, size_t len); +#include "qemu/option.h" + +void smbios_entry_add(QemuOpts *opts); uint8_t *smbios_get_table(size_t *length); /* 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.h b/include/hw/pci/pci.h index 37979aa723..4b90e5d00b 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -397,6 +397,7 @@ const char *pci_root_bus_path(PCIDevice *dev); PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); int pci_qdev_find_device(const char *id, PCIDevice **pdev); PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr); +void pci_bus_get_w64_range(PCIBus *bus, Range *range); int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned int *slotp, unsigned int *funcp); 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/scsi/scsi.h b/include/hw/scsi/scsi.h index 1b6651054a..76f6ac24a7 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -9,6 +9,8 @@ #define MAX_SCSI_DEVS 255 #define SCSI_CMD_BUF_SIZE 16 +#define SCSI_SENSE_LEN 18 +#define SCSI_INQUIRY_LEN 36 typedef struct SCSIBus SCSIBus; typedef struct SCSIBusInfo SCSIBusInfo; diff --git a/include/hw/usb.h b/include/hw/usb.h index 1b8acba6f6..a7680d4e8a 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -442,9 +442,6 @@ int set_usb_string(uint8_t *buf, const char *str); USBDevice *usb_host_device_open(USBBus *bus, const char *devname); void usb_host_info(Monitor *mon, const QDict *qdict); -/* usb-bt.c */ -USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci); - /* usb ports of the VM */ #define VM_USB_HUB_SIZE 8 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/hw/xen/xen.h b/include/hw/xen/xen.h index 6d42dd1bd1..e1f88bf9cf 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -37,17 +37,15 @@ void xen_cmos_set_s3_resume(void *opaque, int irq, int level); qemu_irq *xen_interrupt_controller_init(void); int xen_init(void); -int xen_hvm_init(void); +int xen_hvm_init(MemoryRegion **ram_memory); void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); #if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) -struct MemoryRegion; void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, struct MemoryRegion *mr); void xen_modified_memory(ram_addr_t start, ram_addr_t length); #endif -struct MemoryRegion; void xen_register_framebuffer(struct MemoryRegion *mr); #if defined(CONFIG_XEN) && CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 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/net/net.h b/include/net/net.h index 30e4b04066..11e146888b 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -69,7 +69,7 @@ struct NetClientState { int link_down; QTAILQ_ENTRY(NetClientState) next; NetClientState *peer; - NetQueue *send_queue; + NetQueue *incoming_queue; char *model; char *name; char info_str[256]; diff --git a/include/qapi/error.h b/include/qapi/error.h index ffd1cea477..7d4c6963d3 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -36,6 +36,15 @@ void error_set(Error **err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ */ void error_set_errno(Error **err, int os_error, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(4, 5); +#ifdef _WIN32 +/** + * Set an indirect pointer to an error given a ErrorClass value and a + * printf-style human message, followed by a g_win32_error_message() string if + * @win32_err is not zero. + */ +void error_set_win32(Error **err, int win32_err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(4, 5); +#endif + /** * Same as error_set(), but sets a generic error */ @@ -43,6 +52,10 @@ void error_set_errno(Error **err, int os_error, ErrorClass err_class, const char error_set(err, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__) #define error_setg_errno(err, os_error, fmt, ...) \ error_set_errno(err, os_error, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__) +#ifdef _WIN32 +#define error_setg_win32(err, win32_err, fmt, ...) \ + error_set_win32(err, win32_err, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__) +#endif /** * Helper for open() errors 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/bitops.h b/include/qemu/bitops.h index 06e2e6f0ee..304c90c2b4 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -184,6 +184,86 @@ static inline unsigned long hweight_long(unsigned long w) } /** + * rol8 - rotate an 8-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint8_t rol8(uint8_t word, unsigned int shift) +{ + return (word << shift) | (word >> (8 - shift)); +} + +/** + * ror8 - rotate an 8-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint8_t ror8(uint8_t word, unsigned int shift) +{ + return (word >> shift) | (word << (8 - shift)); +} + +/** + * rol16 - rotate a 16-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint16_t rol16(uint16_t word, unsigned int shift) +{ + return (word << shift) | (word >> (16 - shift)); +} + +/** + * ror16 - rotate a 16-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint16_t ror16(uint16_t word, unsigned int shift) +{ + return (word >> shift) | (word << (16 - shift)); +} + +/** + * rol32 - rotate a 32-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint32_t rol32(uint32_t word, unsigned int shift) +{ + return (word << shift) | (word >> (32 - shift)); +} + +/** + * ror32 - rotate a 32-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint32_t ror32(uint32_t word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + +/** + * rol64 - rotate a 64-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint64_t rol64(uint64_t word, unsigned int shift) +{ + return (word << shift) | (word >> (64 - shift)); +} + +/** + * ror64 - rotate a 64-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint64_t ror64(uint64_t word, unsigned int shift) +{ + return (word >> shift) | (word << (64 - shift)); +} + +/** * extract32: * @value: the value to extract the bit field from * @start: the lowest bit in the bit field (numbered from 0) diff --git a/include/qemu/range.h b/include/qemu/range.h index b76cc0df09..aae9720161 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -2,6 +2,7 @@ #define QEMU_RANGE_H #include <inttypes.h> +#include <qemu/typedefs.h> /* * Operations on 64 bit address ranges. @@ -15,7 +16,24 @@ struct Range { uint64_t begin; /* First byte of the range, or 0 if empty. */ uint64_t end; /* 1 + the last byte. 0 if range empty or ends at ~0x0LL. */ }; -typedef struct Range Range; + +static inline void range_extend(Range *range, Range *extend_by) +{ + if (!extend_by->begin && !extend_by->end) { + return; + } + if (!range->begin && !range->end) { + *range = *extend_by; + return; + } + if (range->begin > extend_by->begin) { + range->begin = extend_by->begin; + } + /* Compare last byte in case region ends at ~0x0LL */ + if (range->end - 1 < extend_by->end - 1) { + range->end = extend_by->end; + } +} /* Get last byte of a range from offset + length. * Undefined for ranges that wrap around 0. */ diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index c5174d76a7..45588d7d58 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -39,6 +39,7 @@ int socket_set_cork(int fd, int v); int socket_set_nodelay(int fd); void qemu_set_block(int fd); void qemu_set_nonblock(int fd); +int socket_set_fast_reuse(int fd); int send_all(int fd, const void *buf, int len1); int recv_all(int fd, void *buf, int len1, bool single_read); diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h new file mode 100644 index 0000000000..ab29b0b918 --- /dev/null +++ b/include/qemu/throttle.h @@ -0,0 +1,110 @@ +/* + * QEMU throttling infrastructure + * + * Copyright (C) Nodalink, SARL. 2013 + * + * Author: + * BenoĆ®t Canet <benoit.canet@irqsave.net> + * + * 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/>. + */ + +#ifndef THROTTLE_H +#define THROTTLE_H + +#include <stdint.h> +#include "qemu-common.h" +#include "qemu/timer.h" + +#define NANOSECONDS_PER_SECOND 1000000000.0 + +typedef enum { + THROTTLE_BPS_TOTAL, + THROTTLE_BPS_READ, + THROTTLE_BPS_WRITE, + THROTTLE_OPS_TOTAL, + THROTTLE_OPS_READ, + THROTTLE_OPS_WRITE, + BUCKETS_COUNT, +} BucketType; + +/* + * The max parameter of the leaky bucket throttling algorithm can be used to + * allow the guest to do bursts. + * The max value is a pool of I/O that the guest can use without being throttled + * at all. Throttling is triggered once this pool is empty. + */ + +typedef struct LeakyBucket { + double avg; /* average goal in units per second */ + double max; /* leaky bucket max burst in units */ + double level; /* bucket level in units */ +} LeakyBucket; + +/* The following structure is used to configure a ThrottleState + * It contains a bit of state: the bucket field of the LeakyBucket structure. + * However it allows to keep the code clean and the bucket field is reset to + * zero at the right time. + */ +typedef struct ThrottleConfig { + LeakyBucket buckets[BUCKETS_COUNT]; /* leaky buckets */ + uint64_t op_size; /* size of an operation in bytes */ +} ThrottleConfig; + +typedef struct ThrottleState { + ThrottleConfig cfg; /* configuration */ + int64_t previous_leak; /* timestamp of the last leak done */ + QEMUTimer * timers[2]; /* timers used to do the throttling */ + QEMUClockType clock_type; /* the clock used */ +} ThrottleState; + +/* operations on single leaky buckets */ +void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta); + +int64_t throttle_compute_wait(LeakyBucket *bkt); + +/* expose timer computation function for unit tests */ +bool throttle_compute_timer(ThrottleState *ts, + bool is_write, + int64_t now, + int64_t *next_timestamp); + +/* init/destroy cycle */ +void throttle_init(ThrottleState *ts, + QEMUClockType clock_type, + void (read_timer)(void *), + void (write_timer)(void *), + void *timer_opaque); + +void throttle_destroy(ThrottleState *ts); + +bool throttle_have_timer(ThrottleState *ts); + +/* configuration */ +bool throttle_enabled(ThrottleConfig *cfg); + +bool throttle_conflicting(ThrottleConfig *cfg); + +bool throttle_is_valid(ThrottleConfig *cfg); + +void throttle_config(ThrottleState *ts, ThrottleConfig *cfg); + +void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg); + +/* usage */ +bool throttle_schedule_timer(ThrottleState *ts, bool is_write); + +void throttle_account(ThrottleState *ts, bool is_write, uint64_t size); + +#endif 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/qemu/typedefs.h b/include/qemu/typedefs.h index 3205540059..a4c1b84d69 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -68,5 +68,6 @@ typedef struct QEMUSGList QEMUSGList; typedef struct SHPCDevice SHPCDevice; typedef struct FWCfgState FWCfgState; typedef struct PcGuestInfo PcGuestInfo; +typedef struct Range Range; #endif /* QEMU_TYPEDEFS_H */ diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index dece913e7b..be71bcac2d 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -28,7 +28,7 @@ extern const uint32_t arch_type; void select_soundhw(const char *optarg); void do_acpitable_option(const QemuOpts *opts); -void do_smbios_option(const char *optarg); +void do_smbios_option(QemuOpts *opts); void cpudef_init(void); void audio_init(void); int tcg_available(void); 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 b1aa059102..cd5791eb74 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -16,8 +16,11 @@ extern const char *bios_name; 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); @@ -39,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); @@ -44,6 +44,22 @@ typedef struct MemoryRegionPortioList { MemoryRegionPortio ports[]; } MemoryRegionPortioList; +static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size) +{ + return -1ULL; +} + +static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ +} + +const MemoryRegionOps unassigned_io_ops = { + .read = unassigned_io_read, + .write = unassigned_io_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + void cpu_outb(pio_addr_t addr, uint8_t val) { LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); @@ -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/aarch64/syscall.h b/linux-user/aarch64/syscall.h new file mode 100644 index 0000000000..aef419efeb --- /dev/null +++ b/linux-user/aarch64/syscall.h @@ -0,0 +1,9 @@ +struct target_pt_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +}; + +#define UNAME_MACHINE "aarch64" +#define UNAME_MINIMUM_RELEASE "3.8.0" diff --git a/linux-user/aarch64/syscall_nr.h b/linux-user/aarch64/syscall_nr.h new file mode 100644 index 0000000000..743255db06 --- /dev/null +++ b/linux-user/aarch64/syscall_nr.h @@ -0,0 +1,323 @@ +/* + * This file contains the system call numbers. + */ + +#define TARGET_NR_io_setup 0 +#define TARGET_NR_io_destroy 1 +#define TARGET_NR_io_submit 2 +#define TARGET_NR_io_cancel 3 +#define TARGET_NR_io_getevents 4 +#define TARGET_NR_setxattr 5 +#define TARGET_NR_lsetxattr 6 +#define TARGET_NR_fsetxattr 7 +#define TARGET_NR_getxattr 8 +#define TARGET_NR_lgetxattr 9 +#define TARGET_NR_fgetxattr 10 +#define TARGET_NR_listxattr 11 +#define TARGET_NR_llistxattr 12 +#define TARGET_NR_flistxattr 13 +#define TARGET_NR_removexattr 14 +#define TARGET_NR_lremovexattr 15 +#define TARGET_NR_fremovexattr 16 +#define TARGET_NR_getcwd 17 +#define TARGET_NR_lookup_dcookie 18 +#define TARGET_NR_eventfd2 19 +#define TARGET_NR_epoll_create1 20 +#define TARGET_NR_epoll_ctl 21 +#define TARGET_NR_epoll_pwait 22 +#define TARGET_NR_dup 23 +#define TARGET_NR_dup3 24 +#define TARGET_NR_fcntl 25 +#define TARGET_NR_inotify_init1 26 +#define TARGET_NR_inotify_add_watch 27 +#define TARGET_NR_inotify_rm_watch 28 +#define TARGET_NR_ioctl 29 +#define TARGET_NR_ioprio_set 30 +#define TARGET_NR_ioprio_get 31 +#define TARGET_NR_flock 32 +#define TARGET_NR_mknodat 33 +#define TARGET_NR_mkdirat 34 +#define TARGET_NR_unlinkat 35 +#define TARGET_NR_symlinkat 36 +#define TARGET_NR_linkat 37 +#define TARGET_NR_renameat 38 +#define TARGET_NR_umount2 39 +#define TARGET_NR_mount 40 +#define TARGET_NR_pivot_root 41 +#define TARGET_NR_nfsservctl 42 +#define TARGET_NR_statfs 43 +#define TARGET_NR_fstatfs 44 +#define TARGET_NR_truncate 45 +#define TARGET_NR_ftruncate 46 +#define TARGET_NR_fallocate 47 +#define TARGET_NR_faccessat 48 +#define TARGET_NR_chdir 49 +#define TARGET_NR_fchdir 50 +#define TARGET_NR_chroot 51 +#define TARGET_NR_fchmod 52 +#define TARGET_NR_fchmodat 53 +#define TARGET_NR_fchownat 54 +#define TARGET_NR_fchown 55 +#define TARGET_NR_openat 56 +#define TARGET_NR_close 57 +#define TARGET_NR_vhangup 58 +#define TARGET_NR_pipe2 59 +#define TARGET_NR_quotactl 60 +#define TARGET_NR_getdents64 61 +#define TARGET_NR_lseek 62 +#define TARGET_NR_read 63 +#define TARGET_NR_write 64 +#define TARGET_NR_readv 65 +#define TARGET_NR_writev 66 +#define TARGET_NR_pread64 67 +#define TARGET_NR_pwrite64 68 +#define TARGET_NR_preadv 69 +#define TARGET_NR_pwritev 70 +#define TARGET_NR_sendfile 71 +#define TARGET_NR_pselect6 72 +#define TARGET_NR_ppoll 73 +#define TARGET_NR_signalfd4 74 +#define TARGET_NR_vmsplice 75 +#define TARGET_NR_splice 76 +#define TARGET_NR_tee 77 +#define TARGET_NR_readlinkat 78 +#define TARGET_NR_fstatat64 79 +#define TARGET_NR_fstat 80 +#define TARGET_NR_sync 81 +#define TARGET_NR_fsync 82 +#define TARGET_NR_fdatasync 83 +#define TARGET_NR_sync_file_range2 84 +/* #define TARGET_NR_sync_file_range 84 */ +#define TARGET_NR_timerfd_create 85 +#define TARGET_NR_timerfd_settime 86 +#define TARGET_NR_timerfd_gettime 87 +#define TARGET_NR_utimensat 88 +#define TARGET_NR_acct 89 +#define TARGET_NR_capget 90 +#define TARGET_NR_capset 91 +#define TARGET_NR_personality 92 +#define TARGET_NR_exit 93 +#define TARGET_NR_exit_group 94 +#define TARGET_NR_waitid 95 +#define TARGET_NR_set_tid_address 96 +#define TARGET_NR_unshare 97 +#define TARGET_NR_futex 98 +#define TARGET_NR_set_robust_list 99 +#define TARGET_NR_get_robust_list 100 +#define TARGET_NR_nanosleep 101 +#define TARGET_NR_getitimer 102 +#define TARGET_NR_setitimer 103 +#define TARGET_NR_kexec_load 104 +#define TARGET_NR_init_module 105 +#define TARGET_NR_delete_module 106 +#define TARGET_NR_timer_create 107 +#define TARGET_NR_timer_gettime 108 +#define TARGET_NR_timer_getoverrun 109 +#define TARGET_NR_timer_settime 110 +#define TARGET_NR_timer_delete 111 +#define TARGET_NR_clock_settime 112 +#define TARGET_NR_clock_gettime 113 +#define TARGET_NR_clock_getres 114 +#define TARGET_NR_clock_nanosleep 115 +#define TARGET_NR_syslog 116 +#define TARGET_NR_ptrace 117 +#define TARGET_NR_sched_setparam 118 +#define TARGET_NR_sched_setscheduler 119 +#define TARGET_NR_sched_getscheduler 120 +#define TARGET_NR_sched_getparam 121 +#define TARGET_NR_sched_setaffinity 122 +#define TARGET_NR_sched_getaffinity 123 +#define TARGET_NR_sched_yield 124 +#define TARGET_NR_sched_get_priority_max 125 +#define TARGET_NR_sched_get_priority_min 126 +#define TARGET_NR_sched_rr_get_interval 127 +#define TARGET_NR_restart_syscall 128 +#define TARGET_NR_kill 129 +#define TARGET_NR_tkill 130 +#define TARGET_NR_tgkill 131 +#define TARGET_NR_sigaltstack 132 +#define TARGET_NR_rt_sigsuspend 133 +#define TARGET_NR_rt_sigaction 134 +#define TARGET_NR_rt_sigprocmask 135 +#define TARGET_NR_rt_sigpending 136 +#define TARGET_NR_rt_sigtimedwait 137 +#define TARGET_NR_rt_sigqueueinfo 138 +#define TARGET_NR_rt_sigreturn 139 +#define TARGET_NR_setpriority 140 +#define TARGET_NR_getpriority 141 +#define TARGET_NR_reboot 142 +#define TARGET_NR_setregid 143 +#define TARGET_NR_setgid 144 +#define TARGET_NR_setreuid 145 +#define TARGET_NR_setuid 146 +#define TARGET_NR_setresuid 147 +#define TARGET_NR_getresuid 148 +#define TARGET_NR_setresgid 149 +#define TARGET_NR_getresgid 150 +#define TARGET_NR_setfsuid 151 +#define TARGET_NR_setfsgid 152 +#define TARGET_NR_times 153 +#define TARGET_NR_setpgid 154 +#define TARGET_NR_getpgid 155 +#define TARGET_NR_getsid 156 +#define TARGET_NR_setsid 157 +#define TARGET_NR_getgroups 158 +#define TARGET_NR_setgroups 159 +#define TARGET_NR_uname 160 +#define TARGET_NR_sethostname 161 +#define TARGET_NR_setdomainname 162 +#define TARGET_NR_getrlimit 163 +#define TARGET_NR_setrlimit 164 +#define TARGET_NR_getrusage 165 +#define TARGET_NR_umask 166 +#define TARGET_NR_prctl 167 +#define TARGET_NR_getcpu 168 +#define TARGET_NR_gettimeofday 169 +#define TARGET_NR_settimeofday 170 +#define TARGET_NR_adjtimex 171 +#define TARGET_NR_getpid 172 +#define TARGET_NR_getppid 173 +#define TARGET_NR_getuid 174 +#define TARGET_NR_geteuid 175 +#define TARGET_NR_getgid 176 +#define TARGET_NR_getegid 177 +#define TARGET_NR_gettid 178 +#define TARGET_NR_sysinfo 179 +#define TARGET_NR_mq_open 180 +#define TARGET_NR_mq_unlink 181 +#define TARGET_NR_mq_timedsend 182 +#define TARGET_NR_mq_timedreceive 183 +#define TARGET_NR_mq_notify 184 +#define TARGET_NR_mq_getsetattr 185 +#define TARGET_NR_msgget 186 +#define TARGET_NR_msgctl 187 +#define TARGET_NR_msgrcv 188 +#define TARGET_NR_msgsnd 189 +#define TARGET_NR_semget 190 +#define TARGET_NR_semctl 191 +#define TARGET_NR_semtimedop 192 +#define TARGET_NR_semop 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_shmat 196 +#define TARGET_NR_shmdt 197 +#define TARGET_NR_socket 198 +#define TARGET_NR_socketpair 199 +#define TARGET_NR_bind 200 +#define TARGET_NR_listen 201 +#define TARGET_NR_accept 202 +#define TARGET_NR_connect 203 +#define TARGET_NR_getsockname 204 +#define TARGET_NR_getpeername 205 +#define TARGET_NR_sendto 206 +#define TARGET_NR_recvfrom 207 +#define TARGET_NR_setsockopt 208 +#define TARGET_NR_getsockopt 209 +#define TARGET_NR_shutdown 210 +#define TARGET_NR_sendmsg 211 +#define TARGET_NR_recvmsg 212 +#define TARGET_NR_readahead 213 +#define TARGET_NR_brk 214 +#define TARGET_NR_munmap 215 +#define TARGET_NR_mremap 216 +#define TARGET_NR_add_key 217 +#define TARGET_NR_request_key 218 +#define TARGET_NR_keyctl 219 +#define TARGET_NR_clone 220 +#define TARGET_NR_execve 221 +#define TARGET_NR_mmap 222 +#define TARGET_NR_fadvise64 223 +#define TARGET_NR_swapon 224 +#define TARGET_NR_swapoff 225 +#define TARGET_NR_mprotect 226 +#define TARGET_NR_msync 227 +#define TARGET_NR_mlock 228 +#define TARGET_NR_munlock 229 +#define TARGET_NR_mlockall 230 +#define TARGET_NR_munlockall 231 +#define TARGET_NR_mincore 232 +#define TARGET_NR_madvise 233 +#define TARGET_NR_remap_file_pages 234 +#define TARGET_NR_mbind 235 +#define TARGET_NR_get_mempolicy 236 +#define TARGET_NR_set_mempolicy 237 +#define TARGET_NR_migrate_pages 238 +#define TARGET_NR_move_pages 239 +#define TARGET_NR_rt_tgsigqueueinfo 240 +#define TARGET_NR_perf_event_open 241 +#define TARGET_NR_accept4 242 +#define TARGET_NR_recvmmsg 243 +#define TARGET_NR_arch_specific_syscall 244 +#define TARGET_NR_wait4 260 +#define TARGET_NR_prlimit64 261 +#define TARGET_NR_fanotify_init 262 +#define TARGET_NR_fanotify_mark 263 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_clock_adjtime 266 +#define TARGET_NR_syncfs 267 +#define TARGET_NR_setns 268 +#define TARGET_NR_sendmmsg 269 +#define TARGET_NR_process_vm_readv 270 +#define TARGET_NR_process_vm_writev 271 +#define TARGET_NR_kcmp 272 +#define TARGET_NR_finit_module 273 +#define TARGET_NR_open 1024 +#define TARGET_NR_link 1025 +#define TARGET_NR_unlink 1026 +#define TARGET_NR_mknod 1027 +#define TARGET_NR_chmod 1028 +#define TARGET_NR_chown 1029 +#define TARGET_NR_mkdir 1030 +#define TARGET_NR_rmdir 1031 +#define TARGET_NR_lchown 1032 +#define TARGET_NR_access 1033 +#define TARGET_NR_rename 1034 +#define TARGET_NR_readlink 1035 +#define TARGET_NR_symlink 1036 +#define TARGET_NR_utimes 1037 +#define TARGET_NR_stat 1038 +#define TARGET_NR_lstat 1039 +#define TARGET_NR_pipe 1040 +#define TARGET_NR_dup2 1041 +#define TARGET_NR_epoll_create 1042 +#define TARGET_NR_inotify_init 1043 +#define TARGET_NR_eventfd 1044 +#define TARGET_NR_signalfd 1045 +#define TARGET_NR_sendfile64 1046 +#define TARGET_NR_ftruncate64 1047 +#define TARGET_NR_truncate64 1048 +#define TARGET_NR_stat64 1049 +#define TARGET_NR_lstat64 1050 +#define TARGET_NR_fstat64 1051 +#define TARGET_NR_fcntl64 1052 +/* #define TARGET_NR_fadvise64 1053 */ +#define TARGET_NR_newfstatat 1054 +#define TARGET_NR_fstatfs64 1055 +#define TARGET_NR_statfs64 1056 +#define TARGET_NR_lseek64 1057 +#define TARGET_NR_mmap64 1058 +#define TARGET_NR_alarm 1059 +#define TARGET_NR_getpgrp 1060 +#define TARGET_NR_pause 1061 +#define TARGET_NR_time 1062 +#define TARGET_NR_utime 1063 +#define TARGET_NR_creat 1064 +#define TARGET_NR_getdents 1065 +#define TARGET_NR_futimesat 1066 +#define TARGET_NR_select 1067 +#define TARGET_NR_poll 1068 +#define TARGET_NR_epoll_wait 1069 +#define TARGET_NR_ustat 1070 +#define TARGET_NR_vfork 1071 +#define TARGET_NR_oldwait4 1072 +#define TARGET_NR_recv 1073 +#define TARGET_NR_send 1074 +#define TARGET_NR_bdflush 1075 +#define TARGET_NR_umount 1076 +#define TARGET_NR_uselib 1077 +#define TARGET_NR__sysctl 1078 +#define TARGET_NR_fork 1079 +#define TARGET_NR_syscalls (__NR_fork+1) + +#define TARGET_NR_sigreturn 1999 diff --git a/linux-user/aarch64/target_cpu.h b/linux-user/aarch64/target_cpu.h new file mode 100644 index 0000000000..6f5539b50f --- /dev/null +++ b/linux-user/aarch64/target_cpu.h @@ -0,0 +1,35 @@ +/* + * ARM AArch64 specific CPU ABI and functions for linux-user + * + * Copyright (c) 2013 Alexander Graf <agraf@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_CPU_H +#define TARGET_CPU_H + +static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp) +{ + if (newsp) { + env->xregs[31] = newsp; + } + env->xregs[0] = 0; +} + +static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) +{ + env->sr.tpidr_el0 = newtls; +} + +#endif diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h new file mode 100644 index 0000000000..e8c677de11 --- /dev/null +++ b/linux-user/aarch64/target_signal.h @@ -0,0 +1,29 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_int ss_flags; + abi_ulong ss_size; +} target_stack_t; + + +/* + * sigaltstack controls + */ +#define TARGET_SS_ONSTACK 1 +#define TARGET_SS_DISABLE 2 + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_SIGSTKSZ 8192 + +static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) +{ + return state->xregs[31]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/aarch64/termbits.h b/linux-user/aarch64/termbits.h new file mode 100644 index 0000000000..b64ba974cf --- /dev/null +++ b/linux-user/aarch64/termbits.h @@ -0,0 +1,220 @@ +/* from asm/termbits.h */ +/* NOTE: exactly the same as i386 */ + +#define TARGET_NCCS 19 + +struct target_termios { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ +}; + +/* c_iflag bits */ +#define TARGET_IGNBRK 0000001 +#define TARGET_BRKINT 0000002 +#define TARGET_IGNPAR 0000004 +#define TARGET_PARMRK 0000010 +#define TARGET_INPCK 0000020 +#define TARGET_ISTRIP 0000040 +#define TARGET_INLCR 0000100 +#define TARGET_IGNCR 0000200 +#define TARGET_ICRNL 0000400 +#define TARGET_IUCLC 0001000 +#define TARGET_IXON 0002000 +#define TARGET_IXANY 0004000 +#define TARGET_IXOFF 0010000 +#define TARGET_IMAXBEL 0020000 +#define TARGET_IUTF8 0040000 + +/* c_oflag bits */ +#define TARGET_OPOST 0000001 +#define TARGET_OLCUC 0000002 +#define TARGET_ONLCR 0000004 +#define TARGET_OCRNL 0000010 +#define TARGET_ONOCR 0000020 +#define TARGET_ONLRET 0000040 +#define TARGET_OFILL 0000100 +#define TARGET_OFDEL 0000200 +#define TARGET_NLDLY 0000400 +#define TARGET_NL0 0000000 +#define TARGET_NL1 0000400 +#define TARGET_CRDLY 0003000 +#define TARGET_CR0 0000000 +#define TARGET_CR1 0001000 +#define TARGET_CR2 0002000 +#define TARGET_CR3 0003000 +#define TARGET_TABDLY 0014000 +#define TARGET_TAB0 0000000 +#define TARGET_TAB1 0004000 +#define TARGET_TAB2 0010000 +#define TARGET_TAB3 0014000 +#define TARGET_XTABS 0014000 +#define TARGET_BSDLY 0020000 +#define TARGET_BS0 0000000 +#define TARGET_BS1 0020000 +#define TARGET_VTDLY 0040000 +#define TARGET_VT0 0000000 +#define TARGET_VT1 0040000 +#define TARGET_FFDLY 0100000 +#define TARGET_FF0 0000000 +#define TARGET_FF1 0100000 + +/* c_cflag bit meaning */ +#define TARGET_CBAUD 0010017 +#define TARGET_B0 0000000 /* hang up */ +#define TARGET_B50 0000001 +#define TARGET_B75 0000002 +#define TARGET_B110 0000003 +#define TARGET_B134 0000004 +#define TARGET_B150 0000005 +#define TARGET_B200 0000006 +#define TARGET_B300 0000007 +#define TARGET_B600 0000010 +#define TARGET_B1200 0000011 +#define TARGET_B1800 0000012 +#define TARGET_B2400 0000013 +#define TARGET_B4800 0000014 +#define TARGET_B9600 0000015 +#define TARGET_B19200 0000016 +#define TARGET_B38400 0000017 +#define TARGET_EXTA B19200 +#define TARGET_EXTB B38400 +#define TARGET_CSIZE 0000060 +#define TARGET_CS5 0000000 +#define TARGET_CS6 0000020 +#define TARGET_CS7 0000040 +#define TARGET_CS8 0000060 +#define TARGET_CSTOPB 0000100 +#define TARGET_CREAD 0000200 +#define TARGET_PARENB 0000400 +#define TARGET_PARODD 0001000 +#define TARGET_HUPCL 0002000 +#define TARGET_CLOCAL 0004000 +#define TARGET_CBAUDEX 0010000 +#define TARGET_B57600 0010001 +#define TARGET_B115200 0010002 +#define TARGET_B230400 0010003 +#define TARGET_B460800 0010004 +#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ +#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ +#define TARGET_CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define TARGET_ISIG 0000001 +#define TARGET_ICANON 0000002 +#define TARGET_XCASE 0000004 +#define TARGET_ECHO 0000010 +#define TARGET_ECHOE 0000020 +#define TARGET_ECHOK 0000040 +#define TARGET_ECHONL 0000100 +#define TARGET_NOFLSH 0000200 +#define TARGET_TOSTOP 0000400 +#define TARGET_ECHOCTL 0001000 +#define TARGET_ECHOPRT 0002000 +#define TARGET_ECHOKE 0004000 +#define TARGET_FLUSHO 0010000 +#define TARGET_PENDIN 0040000 +#define TARGET_IEXTEN 0100000 + +/* c_cc character offsets */ +#define TARGET_VINTR 0 +#define TARGET_VQUIT 1 +#define TARGET_VERASE 2 +#define TARGET_VKILL 3 +#define TARGET_VEOF 4 +#define TARGET_VTIME 5 +#define TARGET_VMIN 6 +#define TARGET_VSWTC 7 +#define TARGET_VSTART 8 +#define TARGET_VSTOP 9 +#define TARGET_VSUSP 10 +#define TARGET_VEOL 11 +#define TARGET_VREPRINT 12 +#define TARGET_VDISCARD 13 +#define TARGET_VWERASE 14 +#define TARGET_VLNEXT 15 +#define TARGET_VEOL2 16 + +/* ioctls */ + +#define TARGET_TCGETS 0x5401 +#define TARGET_TCSETS 0x5402 +#define TARGET_TCSETSW 0x5403 +#define TARGET_TCSETSF 0x5404 +#define TARGET_TCGETA 0x5405 +#define TARGET_TCSETA 0x5406 +#define TARGET_TCSETAW 0x5407 +#define TARGET_TCSETAF 0x5408 +#define TARGET_TCSBRK 0x5409 +#define TARGET_TCXONC 0x540A +#define TARGET_TCFLSH 0x540B + +#define TARGET_TIOCEXCL 0x540C +#define TARGET_TIOCNXCL 0x540D +#define TARGET_TIOCSCTTY 0x540E +#define TARGET_TIOCGPGRP 0x540F +#define TARGET_TIOCSPGRP 0x5410 +#define TARGET_TIOCOUTQ 0x5411 +#define TARGET_TIOCSTI 0x5412 +#define TARGET_TIOCGWINSZ 0x5413 +#define TARGET_TIOCSWINSZ 0x5414 +#define TARGET_TIOCMGET 0x5415 +#define TARGET_TIOCMBIS 0x5416 +#define TARGET_TIOCMBIC 0x5417 +#define TARGET_TIOCMSET 0x5418 +#define TARGET_TIOCGSOFTCAR 0x5419 +#define TARGET_TIOCSSOFTCAR 0x541A +#define TARGET_FIONREAD 0x541B +#define TARGET_TIOCINQ TARGET_FIONREAD +#define TARGET_TIOCLINUX 0x541C +#define TARGET_TIOCCONS 0x541D +#define TARGET_TIOCGSERIAL 0x541E +#define TARGET_TIOCSSERIAL 0x541F +#define TARGET_TIOCPKT 0x5420 +#define TARGET_FIONBIO 0x5421 +#define TARGET_TIOCNOTTY 0x5422 +#define TARGET_TIOCSETD 0x5423 +#define TARGET_TIOCGETD 0x5424 +#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ +#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ +#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TARGET_TIOCGPTN TARGET_IOR('T', 0x30, unsigned int) + /* Get Pty Number (of pty-mux device) */ +#define TARGET_TIOCSPTLCK TARGET_IOW('T', 0x31, int) + /* Lock/unlock Pty */ + +#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */ +#define TARGET_FIOCLEX 0x5451 +#define TARGET_FIOASYNC 0x5452 +#define TARGET_TIOCSERCONFIG 0x5453 +#define TARGET_TIOCSERGWILD 0x5454 +#define TARGET_TIOCSERSWILD 0x5455 +#define TARGET_TIOCGLCKTRMIOS 0x5456 +#define TARGET_TIOCSLCKTRMIOS 0x5457 +#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TARGET_TIOCMIWAIT 0x545C + /* wait for a change on serial input line(s) */ +#define TARGET_TIOCGICOUNT 0x545D + /* read serial port inline interrupt counts */ +#define TARGET_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ +#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ + +/* Used for packet mode */ +#define TARGET_TIOCPKT_DATA 0 +#define TARGET_TIOCPKT_FLUSHREAD 1 +#define TARGET_TIOCPKT_FLUSHWRITE 2 +#define TARGET_TIOCPKT_STOP 4 +#define TARGET_TIOCPKT_START 8 +#define TARGET_TIOCPKT_NOSTOP 16 +#define TARGET_TIOCPKT_DOSTOP 32 + +#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */ 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/cpu-uname.c b/linux-user/cpu-uname.c index cc713e6553..5db6e89567 100644 --- a/linux-user/cpu-uname.c +++ b/linux-user/cpu-uname.c @@ -30,7 +30,8 @@ * return here */ const char *cpu_to_uname_machine(void *cpu_env) { -#ifdef TARGET_ARM +#if defined(TARGET_ARM) && !defined(TARGET_AARCH64) + /* utsname machine name on linux arm is CPU arch name + endianness, e.g. * armv7l; to get a list of CPU arch names from the linux source, use: * grep arch_name: -A1 linux/arch/arm/mm/proc-*.S diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 72d92707c6..8dd424dadd 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -269,16 +269,26 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ( (x) == EM_ARM ) +#define elf_check_arch(x) ((x) == ELF_MACHINE) +#define ELF_ARCH ELF_MACHINE + +#ifdef TARGET_AARCH64 +#define ELF_CLASS ELFCLASS64 +#else #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_ARM +#endif static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { abi_long stack = infop->start_stack; memset(regs, 0, sizeof(*regs)); + +#ifdef TARGET_AARCH64 + regs->pc = infop->entry & ~0x3ULL; + regs->sp = stack; +#else regs->ARM_cpsr = 0x10; if (infop->entry & 1) regs->ARM_cpsr |= CPSR_T; @@ -292,6 +302,7 @@ static inline void init_thread(struct target_pt_regs *regs, /* For uClinux PIC binaries. */ /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ regs->ARM_r10 = infop->start_data; +#endif } #define ELF_NREG 18 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 5c2f7b26b4..1561950bf5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -449,6 +449,9 @@ void cpu_loop(CPUX86State *env) __r; \ }) +#ifdef TARGET_ABI32 +/* Commpage handling -- there is no commpage for AArch64 */ + /* * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt * Input: @@ -582,6 +585,7 @@ do_kernel_trap(CPUARMState *env) return 0; } +#endif static int do_strex(CPUARMState *env) { @@ -661,6 +665,7 @@ done: return segv; } +#ifdef TARGET_ABI32 void cpu_loop(CPUARMState *env) { CPUState *cs = CPU(arm_env_get_cpu(env)); @@ -873,6 +878,83 @@ void cpu_loop(CPUARMState *env) } } +#else + +/* AArch64 main loop */ +void cpu_loop(CPUARMState *env) +{ + CPUState *cs = CPU(arm_env_get_cpu(env)); + int trapnr, sig; + target_siginfo_t info; + uint32_t addr; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_arm_exec(env); + cpu_exec_end(cs); + + switch (trapnr) { + case EXCP_SWI: + env->xregs[0] = do_syscall(env, + env->xregs[8], + env->xregs[0], + env->xregs[1], + env->xregs[2], + env->xregs[3], + env->xregs[4], + env->xregs[5], + 0, 0); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_UDEF: + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, &info); + break; + case EXCP_PREFETCH_ABORT: + addr = env->cp15.c6_insn; + goto do_segv; + case EXCP_DATA_ABORT: + addr = env->cp15.c6_data; + do_segv: + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = addr; + queue_signal(env, info.si_signo, &info); + break; + case EXCP_DEBUG: + case EXCP_BKPT: + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP_STREX: + if (do_strex(env)) { + addr = env->cp15.c6_data; + goto do_segv; + } + break; + default: + fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", + trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + abort(); + } + process_pending_signals(env); + } +} +#endif /* ndef TARGET_ABI32 */ + #endif #ifdef TARGET_UNICORE32 @@ -1779,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 */ @@ -1809,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 */ @@ -2318,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); @@ -2331,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) { @@ -3536,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; @@ -3550,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); @@ -3594,6 +3711,8 @@ int main(int argc, char **argv, char **envp) /* Scan interp_prefix dir for replacement files. */ init_paths(interp_prefix); + init_qemu_uname_release(); + if (cpu_model == NULL) { #if defined(TARGET_I386) #ifdef TARGET_X86_64 @@ -3725,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)); @@ -3884,6 +4012,22 @@ int main(int argc, char **argv, char **envp) cpu_x86_load_seg(env, R_FS, 0); cpu_x86_load_seg(env, R_GS, 0); #endif +#elif defined(TARGET_AARCH64) + { + int i; + + if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { + fprintf(stderr, + "The selected ARM CPU does not support 64 bit mode\n"); + exit(1); + } + + for (i = 0; i < 31; i++) { + env->xregs[i] = regs->regs[i]; + } + env->pc = regs->pc; + env->xregs[31] = regs->sp; + } #elif defined(TARGET_ARM) { int i; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 4a16e8fe1d..da64e877c7 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -74,7 +74,7 @@ struct vm86_saved_state { }; #endif -#ifdef TARGET_ARM +#if defined(TARGET_ARM) && defined(TARGET_ABI32) /* FPU emulator */ #include "nwfpe/fpa11.h" #endif @@ -98,8 +98,10 @@ struct emulated_sigtable { typedef struct TaskState { pid_t ts_tid; /* tid (or pid) of this task */ #ifdef TARGET_ARM +# ifdef TARGET_ABI32 /* FPA state */ FPA11 fpa; +# endif int swi_errno; #endif #ifdef TARGET_UNICORE32 @@ -172,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 *); @@ -195,6 +197,7 @@ extern THREAD CPUState *thread_cpu; void cpu_loop(CPUArchState *env); char *target_strerror(int err); int get_osversion(void); +void init_qemu_uname_release(void); void fork_start(void); void fork_end(int child); @@ -377,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 @@ -435,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/signal.c b/linux-user/signal.c index 23d65dab77..7751c47ef1 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1092,6 +1092,266 @@ badframe: return 0; } +#elif defined(TARGET_AARCH64) + +struct target_sigcontext { + uint64_t fault_address; + /* AArch64 registers */ + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; + /* 4K reserved for FP/SIMD state and future expansion */ + char __reserved[4096] __attribute__((__aligned__(16))); +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + target_sigset_t tuc_sigmask; + /* glibc uses a 1024-bit sigset_t */ + char __unused[1024 / 8 - sizeof(target_sigset_t)]; + /* last for future expansion */ + struct target_sigcontext tuc_mcontext; +}; + +/* + * Header to be used at the beginning of structures extending the user + * context. Such structures must be placed after the rt_sigframe on the stack + * and be 16-byte aligned. The last structure must be a dummy one with the + * magic and size set to 0. + */ +struct target_aarch64_ctx { + uint32_t magic; + uint32_t size; +}; + +#define TARGET_FPSIMD_MAGIC 0x46508001 + +struct target_fpsimd_context { + struct target_aarch64_ctx head; + uint32_t fpsr; + uint32_t fpcr; + uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */ +}; + +/* + * Auxiliary context saved in the sigcontext.__reserved array. Not exported to + * user space as it will change with the addition of new context. User space + * should check the magic/size information. + */ +struct target_aux_context { + struct target_fpsimd_context fpsimd; + /* additional context to be added before "end" */ + struct target_aarch64_ctx end; +}; + +struct target_rt_sigframe { + struct target_siginfo info; + struct target_ucontext uc; + uint64_t fp; + uint64_t lr; + uint32_t tramp[2]; +}; + +static int target_setup_sigframe(struct target_rt_sigframe *sf, + CPUARMState *env, target_sigset_t *set) +{ + int i; + struct target_aux_context *aux = + (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved; + + /* set up the stack frame for unwinding */ + __put_user(env->xregs[29], &sf->fp); + __put_user(env->xregs[30], &sf->lr); + + for (i = 0; i < 31; i++) { + __put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); + } + __put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); + __put_user(env->pc, &sf->uc.tuc_mcontext.pc); + __put_user(env->pstate, &sf->uc.tuc_mcontext.pstate); + + __put_user(/*current->thread.fault_address*/ 0, + &sf->uc.tuc_mcontext.fault_address); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]); + } + + for (i = 0; i < 32; i++) { +#ifdef TARGET_WORDS_BIGENDIAN + __put_user(env->vfp.regs[i * 2], &aux->fpsimd.vregs[i * 2 + 1]); + __put_user(env->vfp.regs[i * 2 + 1], &aux->fpsimd.vregs[i * 2]); +#else + __put_user(env->vfp.regs[i * 2], &aux->fpsimd.vregs[i * 2]); + __put_user(env->vfp.regs[i * 2 + 1], &aux->fpsimd.vregs[i * 2 + 1]); +#endif + } + __put_user(/*env->fpsr*/0, &aux->fpsimd.fpsr); + __put_user(/*env->fpcr*/0, &aux->fpsimd.fpcr); + __put_user(TARGET_FPSIMD_MAGIC, &aux->fpsimd.head.magic); + __put_user(sizeof(struct target_fpsimd_context), + &aux->fpsimd.head.size); + + /* set the "end" magic */ + __put_user(0, &aux->end.magic); + __put_user(0, &aux->end.size); + + return 0; +} + +static int target_restore_sigframe(CPUARMState *env, + struct target_rt_sigframe *sf) +{ + sigset_t set; + int i; + struct target_aux_context *aux = + (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved; + uint32_t magic, size; + + target_to_host_sigset(&set, &sf->uc.tuc_sigmask); + sigprocmask(SIG_SETMASK, &set, NULL); + + for (i = 0; i < 31; i++) { + __get_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); + } + + __get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); + __get_user(env->pc, &sf->uc.tuc_mcontext.pc); + __get_user(env->pstate, &sf->uc.tuc_mcontext.pstate); + + __get_user(magic, &aux->fpsimd.head.magic); + __get_user(size, &aux->fpsimd.head.size); + + if (magic != TARGET_FPSIMD_MAGIC + || size != sizeof(struct target_fpsimd_context)) { + return 1; + } + + for (i = 0; i < 32 * 2; i++) { + __get_user(env->vfp.regs[i], &aux->fpsimd.vregs[i]); + } + + return 0; +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env) +{ + abi_ulong sp; + + sp = env->xregs[31]; + + /* + * This is the X/Open sanctioned signal stack switching. + */ + if ((ka->sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + sp = (sp - sizeof(struct target_rt_sigframe)) & ~15; + + return sp; +} + +static void target_setup_frame(int usig, struct target_sigaction *ka, + target_siginfo_t *info, target_sigset_t *set, + CPUARMState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + + frame_addr = get_sigframe(ka, env); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + + __put_user(target_sigaltstack_used.ss_sp, + &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->xregs[31]), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + target_setup_sigframe(frame, env, set); + /* mov x8,#__NR_rt_sigreturn; svc #0 */ + __put_user(0xd2801168, &frame->tramp[0]); + __put_user(0xd4000001, &frame->tramp[1]); + env->xregs[0] = usig; + env->xregs[31] = frame_addr; + env->xregs[29] = env->xregs[31] + offsetof(struct target_rt_sigframe, fp); + env->pc = ka->_sa_handler; + env->xregs[30] = env->xregs[31] + + offsetof(struct target_rt_sigframe, tramp); + if (info) { + if (copy_siginfo_to_user(&frame->info, info)) { + goto give_sigsegv; + } + env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + } + + unlock_user_struct(frame, frame_addr, 1); + return; + + give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sig(TARGET_SIGSEGV); +} + +static void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, target_sigset_t *set, + CPUARMState *env) +{ + target_setup_frame(sig, ka, info, set, env); +} + +static void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *env) +{ + target_setup_frame(sig, ka, 0, set, env); +} + +long do_rt_sigreturn(CPUARMState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr = env->xregs[31]; + + if (frame_addr & 15) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + if (target_restore_sigframe(env, frame)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return env->xregs[0]; + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} + +long do_sigreturn(CPUARMState *env) +{ + return do_rt_sigreturn(env); +} + #elif defined(TARGET_ARM) struct target_sigcontext { 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 ecead512a0..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. */ @@ -4737,7 +4843,7 @@ static inline abi_long host_to_target_stat64(void *cpu_env, abi_ulong target_addr, struct stat *host_st) { -#ifdef TARGET_ARM +#if defined(TARGET_ARM) && defined(TARGET_ABI32) if (((CPUARMState *)cpu_env)->eabi) { struct target_eabi_stat64 *target_st; @@ -4863,12 +4969,35 @@ int host_to_target_waitstatus(int status) return status; } +static int relstr_to_int(const char *s) +{ + /* Convert a uname release string like "2.6.18" to an integer + * of the form 0x020612. (Beware that 0x020612 is *not* 2.6.12.) + */ + int i, n, tmp; + + tmp = 0; + for (i = 0; i < 3; i++) { + n = 0; + while (*s >= '0' && *s <= '9') { + n *= 10; + n += *s - '0'; + s++; + } + tmp = (tmp << 8) + n; + if (*s == '.') { + s++; + } + } + return tmp; +} + int get_osversion(void) { static int osversion; struct new_utsname buf; const char *s; - int i, n, tmp; + if (osversion) return osversion; if (qemu_uname_release && *qemu_uname_release) { @@ -4878,22 +5007,33 @@ int get_osversion(void) return 0; s = buf.release; } - tmp = 0; - for (i = 0; i < 3; i++) { - n = 0; - while (*s >= '0' && *s <= '9') { - n *= 10; - n += *s - '0'; - s++; - } - tmp = (tmp << 8) + n; - if (*s == '.') - s++; - } - osversion = tmp; + osversion = relstr_to_int(s); return osversion; } +void init_qemu_uname_release(void) +{ + /* Initialize qemu_uname_release for later use. + * If the host kernel is too old and the user hasn't asked for + * a specific fake version number, we might want to fake a minimum + * target kernel version. + */ +#ifdef UNAME_MINIMUM_RELEASE + struct new_utsname buf; + + if (qemu_uname_release && *qemu_uname_release) { + return; + } + + if (sys_uname(&buf)) { + return; + } + + if (relstr_to_int(buf.release) < relstr_to_int(UNAME_MINIMUM_RELEASE)) { + qemu_uname_release = UNAME_MINIMUM_RELEASE; + } +#endif +} static int open_self_maps(void *cpu_env, int fd) { @@ -5037,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; } } @@ -5663,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; @@ -6381,7 +6569,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_mmap case TARGET_NR_mmap: -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || \ +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || \ + (defined(TARGET_ARM) && defined(TARGET_ABI32)) || \ defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) \ || defined(TARGET_S390X) { @@ -8978,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 086fbfffe7..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 */ }; @@ -1137,7 +1149,8 @@ struct target_winsize { #define TARGET_MAP_UNINITIALIZED 0x4000000 /* for anonymous mmap, memory could be uninitialized */ #endif -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) \ +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ + || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ || defined(TARGET_CRIS) || defined(TARGET_UNICORE32) struct target_stat { unsigned short st_dev; @@ -1835,6 +1848,28 @@ struct target_stat { abi_long st_blocks; abi_ulong __unused[3]; }; +#elif defined(TARGET_AARCH64) +struct target_stat { + abi_ulong st_dev; + abi_ulong st_ino; + unsigned int st_mode; + unsigned int st_nlink; + unsigned int st_uid; + unsigned int st_gid; + abi_ulong st_rdev; + abi_ulong _pad1; + abi_long st_size; + int st_blksize; + int __pad2; + abi_long st_blocks; + abi_long target_st_atime; + abi_ulong target_st_atime_nsec; + abi_long target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_long target_st_ctime; + abi_ulong target_st_ctime_nsec; + unsigned int __unused[2]; +}; #elif defined(TARGET_OPENRISC) /* These are the asm-generic versions of the stat and stat64 structures */ @@ -1943,7 +1978,8 @@ struct target_statfs64 { uint32_t f_spare[6]; }; #elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \ - defined(TARGET_SPARC64)) && !defined(TARGET_ABI32) + defined(TARGET_SPARC64) || defined(TARGET_AARCH64)) && \ + !defined(TARGET_ABI32) struct target_statfs { abi_long f_type; abi_long f_bsize; 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); @@ -872,7 +872,7 @@ static uint64_t unassigned_mem_read(void *opaque, hwaddr addr, if (current_cpu != NULL) { cpu_unassigned_access(current_cpu, addr, false, false, 0, size); } - return -1ULL; + return 0; } static void unassigned_mem_write(void *opaque, hwaddr addr, 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..2b1ab20c54 100644 --- a/migration.c +++ b/migration.c @@ -150,6 +150,7 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) MigrationState *s = migrate_get_current(); int i; + caps = NULL; /* silence compiler warning */ for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) { if (head == NULL) { head = g_malloc0(sizeof(*caps)); @@ -567,7 +568,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 { @@ -882,6 +882,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, exp->nbdflags = nbdflags; exp->size = size == -1 ? bdrv_getlength(bs) : size; exp->close = close; + bdrv_ref(bs); return exp; } @@ -928,6 +929,10 @@ void nbd_export_close(NBDExport *exp) } nbd_export_set_name(exp, NULL); nbd_export_put(exp); + if (exp->bs) { + bdrv_unref(exp->bs); + exp->bs = NULL; + } } void nbd_export_get(NBDExport *exp) @@ -347,7 +347,7 @@ bool net_hub_flush(NetClientState *nc) QLIST_FOREACH(port, &source_port->hub->ports, next) { if (port != source_port) { - ret += qemu_net_queue_flush(port->nc.send_queue); + ret += qemu_net_queue_flush(port->nc.incoming_queue); } } return ret ? true : false; @@ -207,7 +207,7 @@ static void qemu_net_client_setup(NetClientState *nc, } QTAILQ_INSERT_TAIL(&net_clients, nc, next); - nc->send_queue = qemu_new_net_queue(nc); + nc->incoming_queue = qemu_new_net_queue(nc); nc->destructor = destructor; } @@ -289,8 +289,8 @@ static void qemu_cleanup_net_client(NetClientState *nc) static void qemu_free_net_client(NetClientState *nc) { - if (nc->send_queue) { - qemu_del_net_queue(nc->send_queue); + if (nc->incoming_queue) { + qemu_del_net_queue(nc->incoming_queue); } if (nc->peer) { nc->peer->peer = NULL; @@ -431,7 +431,7 @@ void qemu_purge_queued_packets(NetClientState *nc) return; } - qemu_net_queue_purge(nc->peer->send_queue, nc); + qemu_net_queue_purge(nc->peer->incoming_queue, nc); } void qemu_flush_queued_packets(NetClientState *nc) @@ -444,7 +444,7 @@ void qemu_flush_queued_packets(NetClientState *nc) } return; } - if (qemu_net_queue_flush(nc->send_queue)) { + if (qemu_net_queue_flush(nc->incoming_queue)) { /* We emptied the queue successfully, signal to the IO thread to repoll * the file descriptor (for tap, for example). */ @@ -468,7 +468,7 @@ static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender, return size; } - queue = sender->peer->send_queue; + queue = sender->peer->incoming_queue; return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb); } @@ -543,7 +543,7 @@ ssize_t qemu_sendv_packet_async(NetClientState *sender, return iov_size(iov, iovcnt); } - queue = sender->peer->send_queue; + queue = sender->peer->incoming_queue; return qemu_net_queue_send_iov(queue, sender, QEMU_NET_PACKET_FLAG_NONE, diff --git a/net/socket.c b/net/socket.c index e61309d8d5..fb21e20a54 100644 --- a/net/socket.c +++ b/net/socket.c @@ -262,6 +262,11 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr return -1; } + /* Allow multiple sockets to bind the same multicast ip and port by setting + * SO_REUSEADDR. This is the only situation where SO_REUSEADDR should be set + * on windows. Use socket_set_fast_reuse otherwise as it sets SO_REUSEADDR + * only on posix systems. + */ val = 1; ret = qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if (ret < 0) { @@ -510,7 +515,7 @@ static int net_socket_listen_init(NetClientState *peer, NetClientState *nc; NetSocketState *s; struct sockaddr_in saddr; - int fd, val, ret; + int fd, ret; if (parse_host_port(&saddr, host_str) < 0) return -1; @@ -522,9 +527,7 @@ static int net_socket_listen_init(NetClientState *peer, } qemu_set_nonblock(fd); - /* allow fast reuse */ - val = 1; - qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + socket_set_fast_reuse(fd); ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { @@ -645,7 +648,7 @@ static int net_socket_udp_init(NetClientState *peer, const char *lhost) { NetSocketState *s; - int fd, val, ret; + int fd, ret; struct sockaddr_in laddr, raddr; if (parse_host_port(&laddr, lhost) < 0) { @@ -661,11 +664,9 @@ static int net_socket_udp_init(NetClientState *peer, perror("socket(PF_INET, SOCK_DGRAM)"); return -1; } - val = 1; - ret = qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, - &val, sizeof(val)); + + ret = socket_set_fast_reuse(fd); if (ret < 0) { - perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); closesocket(fd); return -1; } diff --git a/net/tap-bsd.c b/net/tap-bsd.c index f61d580963..90f8a02276 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -44,8 +44,6 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, struct stat s; #endif -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ - defined(__OpenBSD__) || defined(__APPLE__) /* if no ifname is given, always start the search from tap0/tun0. */ int i; char dname[100]; @@ -76,15 +74,6 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, dname, strerror(errno)); return -1; } -#else - TFR(fd = open("/dev/tap", O_RDWR)); - if (fd < 0) { - fprintf(stderr, - "warning: could not open /dev/tap: no virtual network emulation: %s\n", - strerror(errno)); - return -1; - } -#endif #ifdef TAPGIFNAME if (ioctl(fd, TAPGIFNAME, (void *)&ifr) < 0) { diff --git a/pc-bios/README b/pc-bios/README index e404a228a4..d70be16888 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -12,7 +12,7 @@ 1275-1994 (referred to as Open Firmware) compliant firmware. The included images for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32 and Sparc64 are built from OpenBIOS SVN revision - 1198. + 1229. - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at diff --git a/pc-bios/acpi-dsdt.aml b/pc-bios/acpi-dsdt.aml Binary files differindex 48dbe3242c..528372b41e 100644 --- a/pc-bios/acpi-dsdt.aml +++ b/pc-bios/acpi-dsdt.aml diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin Binary files differindex cccc487814..697440c26c 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc Binary files differindex c6b3319fab..550273a5ef 100644 --- a/pc-bios/openbios-ppc +++ b/pc-bios/openbios-ppc diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 Binary files differindex 2aa400cfd9..01105fc904 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 Binary files differindex f6ee286034..62c9e77983 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 diff --git a/pc-bios/q35-acpi-dsdt.aml b/pc-bios/q35-acpi-dsdt.aml Binary files differindex 91ab67cada..4d23746ebf 100644 --- a/pc-bios/q35-acpi-dsdt.aml +++ b/pc-bios/q35-acpi-dsdt.aml 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 a51f7d2d6e..145eca8855 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -785,6 +785,20 @@ # # @image: the info of image used (since: 1.6) # +# @bps_max: #optional total max in bytes (Since 1.7) +# +# @bps_rd_max: #optional read max in bytes (Since 1.7) +# +# @bps_wr_max: #optional write max in bytes (Since 1.7) +# +# @iops_max: #optional total I/O operations max (Since 1.7) +# +# @iops_rd_max: #optional read I/O operations max (Since 1.7) +# +# @iops_wr_max: #optional write I/O operations max (Since 1.7) +# +# @iops_size: #optional an I/O size in bytes (Since 1.7) +# # Since: 0.14.0 # # Notes: This interface is only found in @BlockInfo. @@ -795,7 +809,11 @@ 'encrypted': 'bool', 'encryption_key_missing': 'bool', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', - 'image': 'ImageInfo' } } + 'image': 'ImageInfo', + '*bps_max': 'int', '*bps_rd_max': 'int', + '*bps_wr_max': 'int', '*iops_max': 'int', + '*iops_rd_max': 'int', '*iops_wr_max': 'int', + '*iops_size': 'int' } } ## # @BlockDeviceIoStatus: @@ -813,6 +831,35 @@ { 'enum': 'BlockDeviceIoStatus', 'data': [ 'ok', 'failed', 'nospace' ] } ## +# @BlockDeviceMapEntry: +# +# Entry in the metadata map of the device (returned by "qemu-img map") +# +# @start: Offset in the image of the first byte described by this entry +# (in bytes) +# +# @length: Length of the range described by this entry (in bytes) +# +# @depth: Number of layers (0 = top image, 1 = top image's backing file, etc.) +# before reaching one for which the range is allocated. The value is +# in the range 0 to the depth of the image chain - 1. +# +# @zero: the sectors in this range read as zeros +# +# @data: reading the image will actually read data from a file (in particular, +# if @offset is present this means that the sectors are not simply +# preallocated, but contain actual data in raw format) +# +# @offset: if present, the image file stores the data for this range in +# raw format at the given offset. +# +# Since 1.7 +## +{ 'type': 'BlockDeviceMapEntry', + 'data': { 'start': 'int', 'length': 'int', 'depth': 'int', 'zero': 'bool', + 'data': 'bool', '*offset': 'int' } } + +## # @BlockDirtyInfo: # # Block dirty bitmap information. @@ -1639,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. @@ -1700,7 +1763,8 @@ 'data': { 'blockdev-snapshot-sync': 'BlockdevSnapshot', 'drive-backup': 'DriveBackup', - 'abort': 'Abort' + 'abort': 'Abort', + 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal' } } ## @@ -1741,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. @@ -2174,6 +2285,20 @@ # # @iops_wr: write I/O operations per second # +# @bps_max: #optional total max in bytes (Since 1.7) +# +# @bps_rd_max: #optional read max in bytes (Since 1.7) +# +# @bps_wr_max: #optional write max in bytes (Since 1.7) +# +# @iops_max: #optional total I/O operations max (Since 1.7) +# +# @iops_rd_max: #optional read I/O operations max (Since 1.7) +# +# @iops_wr_max: #optional write I/O operations max (Since 1.7) +# +# @iops_size: #optional an I/O size in bytes (Since 1.7) +# # Returns: Nothing on success # If @device is not a valid block device, DeviceNotFound # @@ -2181,7 +2306,11 @@ ## { 'command': 'block_set_io_throttle', 'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', - 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int' } } + 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', + '*bps_max': 'int', '*bps_rd_max': 'int', + '*bps_wr_max': 'int', '*iops_max': 'int', + '*iops_rd_max': 'int', '*iops_wr_max': 'int', + '*iops_size': 'int' } } ## # @block-stream: diff --git a/qemu-char.c b/qemu-char.c index 6f111abfbe..e00f84c8e9 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1025,15 +1025,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; } @@ -2973,11 +2969,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) if (strstart(filename, "vc", &p)) { qemu_opt_set(opts, "backend", "vc"); if (*p == ':') { - if (sscanf(p+1, "%8[0-9]x%8[0-9]", width, height) == 2) { + if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) { /* pixels */ qemu_opt_set(opts, "width", width); qemu_opt_set(opts, "height", height); - } else if (sscanf(p+1, "%8[0-9]Cx%8[0-9]C", width, height) == 2) { + } else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) { /* chars */ qemu_opt_set(opts, "cols", width); qemu_opt_set(opts, "rows", height); @@ -3255,7 +3251,12 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, backend->kind = CHARDEV_BACKEND_KIND_MUX; backend->mux->chardev = g_strdup(bid); ret = qmp_chardev_add(id, backend, errp); - assert(!error_is_set(errp)); + if (error_is_set(errp)) { + chr = qemu_chr_find(bid); + qemu_chr_delete(chr); + chr = NULL; + goto qapi_out; + } } chr = qemu_chr_find(id); 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 4ca7e95655..da1d965f3e 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -34,9 +34,9 @@ STEXI ETEXI DEF("convert", img_convert, - "convert [-c] [-p] [-q] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename") + "convert [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename") STEXI -@item convert [-c] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("info", img_info, @@ -45,6 +45,12 @@ STEXI @item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename} ETEXI +DEF("map", img_map, + "map [-f fmt] [--output=ofmt] filename") +STEXI +@item map [-f @var{fmt}] [--output=@var{ofmt}] @var{filename} +ETEXI + DEF("snapshot", img_snapshot, "snapshot [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") STEXI @@ -61,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 b9a848db74..926f0a0feb 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -103,6 +103,8 @@ static void help(void) " '-S' indicates the consecutive number of bytes that must contain only zeros\n" " for qemu-img to create a sparse image during conversion\n" " '--output' takes the format in which the output must be done (human or json)\n" + " '-n' skips the target volume creation (useful if the volume is created\n" + " prior to running qemu-img)\n" "\n" "Parameters to check subcommand:\n" " '-r' tries to repair any inconsistencies that are found during the check.\n" @@ -264,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"); @@ -278,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; } @@ -298,7 +303,7 @@ static BlockDriverState *bdrv_new_open(const char *filename, return bs; fail: if (bs) { - bdrv_delete(bs); + bdrv_unref(bs); } return NULL; } @@ -407,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; } @@ -652,7 +657,7 @@ static int img_check(int argc, char **argv) fail: qapi_free_ImageCheck(check); - bdrv_delete(bs); + bdrv_unref(bs); return ret; } @@ -722,7 +727,7 @@ static int img_commit(int argc, char **argv) break; } - bdrv_delete(bs); + bdrv_unref(bs); if (ret) { return 1; } @@ -1104,11 +1109,11 @@ static int img_compare(int argc, char **argv) ret = 0; out: - bdrv_delete(bs2); + bdrv_unref(bs2); qemu_vfree(buf1); qemu_vfree(buf2); out2: - bdrv_delete(bs1); + bdrv_unref(bs1); out3: qemu_progress_end(); return ret; @@ -1116,7 +1121,8 @@ out3: static int img_convert(int argc, char **argv) { - int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors; + int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, + cluster_sectors, skip_create; int progress = 0, flags; const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename; BlockDriver *drv, *proto_drv; @@ -1133,14 +1139,16 @@ 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"; cache = "unsafe"; out_baseimg = NULL; compress = 0; + skip_create = 0; for(;;) { - c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:q"); + c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:qn"); if (c == -1) { break; } @@ -1197,6 +1205,9 @@ static int img_convert(int argc, char **argv) case 'q': quiet = true; break; + case 'n': + skip_create = 1; + break; } } @@ -1329,20 +1340,15 @@ static int img_convert(int argc, char **argv) } } - /* Create the new image */ - ret = bdrv_create(drv, out_filename, param); - 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 { + if (!skip_create) { + /* Create the new image */ + ret = bdrv_create(drv, out_filename, param, &local_err); + if (ret < 0) { error_report("%s: error while converting %s: %s", - out_filename, out_fmt, strerror(-ret)); + out_filename, out_fmt, error_get_pretty(local_err)); + error_free(local_err); + goto out; } - goto out; } flags = BDRV_O_RDWR; @@ -1363,6 +1369,20 @@ static int img_convert(int argc, char **argv) bdrv_get_geometry(bs[0], &bs_sectors); buf = qemu_blockalign(out_bs, IO_BUF_SIZE); + if (skip_create) { + int64_t output_length = bdrv_getlength(out_bs); + if (output_length < 0) { + error_report("unable to get output image length: %s\n", + strerror(-output_length)); + ret = -1; + goto out; + } else if (output_length < total_sectors << BDRV_SECTOR_BITS) { + error_report("output file is smaller than input file"); + ret = -1; + goto out; + } + } + if (compress) { ret = bdrv_get_info(out_bs, &bdi); if (ret < 0) { @@ -1479,21 +1499,26 @@ static int img_convert(int argc, char **argv) n = bs_offset + bs_sectors - sector_num; } - if (has_zero_init) { - /* If the output image is being created as a copy on write image, - assume that sectors which are unallocated in the input image - are present in both the output's and input's base images (no - need to copy them). */ - if (out_baseimg) { - if (!bdrv_is_allocated(bs[bs_i], sector_num - bs_offset, - n, &n1)) { - sector_num += n1; - continue; - } - /* The next 'n1' sectors are allocated in the input image. Copy - only those as they may be followed by unallocated sectors. */ - n = n1; + /* If the output image is being created as a copy on write image, + assume that sectors which are unallocated in the input image + are present in both the output's and input's base images (no + need to copy them). */ + if (out_baseimg) { + ret = bdrv_is_allocated(bs[bs_i], sector_num - bs_offset, + n, &n1); + if (ret < 0) { + error_report("error while reading metadata for sector " + "%" PRId64 ": %s", + sector_num - bs_offset, strerror(-ret)); + goto out; + } + if (!ret) { + sector_num += n1; + continue; } + /* The next 'n1' sectors are allocated in the input image. Copy + only those as they may be followed by unallocated sectors. */ + n = n1; } else { n1 = n; } @@ -1509,14 +1534,7 @@ static int img_convert(int argc, char **argv) should add a specific call to have the info to go faster */ buf1 = buf; while (n > 0) { - /* If the output image is being created as a copy on write image, - copy all sectors even the ones containing only NUL bytes, - because they may differ from the sectors in the base image. - - If the output is to a host device, we also write out - sectors that are entirely 0, since whatever data was - already there is garbage, not 0s. */ - if (!has_zero_init || out_baseimg || + if (!has_zero_init || is_allocated_sectors_min(buf1, n, &n1, min_sparse)) { ret = bdrv_write(out_bs, sector_num, buf1, n1); if (ret < 0) { @@ -1538,12 +1556,12 @@ out: free_option_parameters(param); qemu_vfree(buf); if (out_bs) { - bdrv_delete(out_bs); + bdrv_unref(out_bs); } if (bs) { for (bs_i = 0; bs_i < bs_n; bs_i++) { if (bs[bs_i]) { - bdrv_delete(bs[bs_i]); + bdrv_unref(bs[bs_i]); } } g_free(bs); @@ -1681,7 +1699,7 @@ static ImageInfoList *collect_image_info_list(const char *filename, *last = elem; last = &elem->next; - bdrv_delete(bs); + bdrv_unref(bs); filename = fmt = NULL; if (chain) { @@ -1780,6 +1798,197 @@ static int img_info(int argc, char **argv) return 0; } + +typedef struct MapEntry { + int flags; + int depth; + int64_t start; + int64_t length; + int64_t offset; + BlockDriverState *bs; +} MapEntry; + +static void dump_map_entry(OutputFormat output_format, MapEntry *e, + MapEntry *next) +{ + switch (output_format) { + case OFORMAT_HUMAN: + if ((e->flags & BDRV_BLOCK_DATA) && + !(e->flags & BDRV_BLOCK_OFFSET_VALID)) { + error_report("File contains external, encrypted or compressed clusters."); + exit(1); + } + if ((e->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) == BDRV_BLOCK_DATA) { + printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", + e->start, e->length, e->offset, e->bs->filename); + } + /* This format ignores the distinction between 0, ZERO and ZERO|DATA. + * Modify the flags here to allow more coalescing. + */ + if (next && + (next->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) != BDRV_BLOCK_DATA) { + next->flags &= ~BDRV_BLOCK_DATA; + next->flags |= BDRV_BLOCK_ZERO; + } + break; + case OFORMAT_JSON: + printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64", \"depth\": %d," + " \"zero\": %s, \"data\": %s", + (e->start == 0 ? "[" : ",\n"), + e->start, e->length, e->depth, + (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); + } + putchar('}'); + + if (!next) { + printf("]\n"); + } + break; + } +} + +static int get_block_status(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, MapEntry *e) +{ + int64_t ret; + int depth; + + /* As an optimization, we could cache the current range of unallocated + * clusters in each file of the chain, and avoid querying the same + * range repeatedly. + */ + + depth = 0; + for (;;) { + ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &nb_sectors); + if (ret < 0) { + return ret; + } + assert(nb_sectors); + if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) { + break; + } + bs = bs->backing_hd; + if (bs == NULL) { + ret = 0; + break; + } + + depth++; + } + + e->start = sector_num * BDRV_SECTOR_SIZE; + e->length = nb_sectors * BDRV_SECTOR_SIZE; + e->flags = ret & ~BDRV_BLOCK_OFFSET_MASK; + e->offset = ret & BDRV_BLOCK_OFFSET_MASK; + e->depth = depth; + e->bs = bs; + return 0; +} + +static int img_map(int argc, char **argv) +{ + int c; + OutputFormat output_format = OFORMAT_HUMAN; + BlockDriverState *bs; + const char *filename, *fmt, *output; + int64_t length; + MapEntry curr = { .length = 0 }, next; + int ret = 0; + + fmt = NULL; + output = NULL; + for (;;) { + int option_index = 0; + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"format", required_argument, 0, 'f'}, + {"output", required_argument, 0, OPTION_OUTPUT}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "f:h", + long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case '?': + case 'h': + help(); + break; + case 'f': + fmt = optarg; + break; + case OPTION_OUTPUT: + output = optarg; + break; + } + } + if (optind >= argc) { + help(); + } + filename = argv[optind++]; + + if (output && !strcmp(output, "json")) { + output_format = OFORMAT_JSON; + } else if (output && !strcmp(output, "human")) { + output_format = OFORMAT_HUMAN; + } else if (output) { + error_report("--output must be used with human or json as argument."); + return 1; + } + + bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS, true, false); + if (!bs) { + return 1; + } + + if (output_format == OFORMAT_HUMAN) { + printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File"); + } + + length = bdrv_getlength(bs); + while (curr.start + curr.length < length) { + int64_t nsectors_left; + int64_t sector_num; + int n; + + sector_num = (curr.start + curr.length) >> BDRV_SECTOR_BITS; + + /* Probe up to 1 GiB at a time. */ + nsectors_left = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE) - sector_num; + n = MIN(1 << (30 - BDRV_SECTOR_BITS), nsectors_left); + ret = get_block_status(bs, sector_num, n, &next); + + if (ret < 0) { + error_report("Could not read file metadata: %s", strerror(-ret)); + goto out; + } + + if (curr.length != 0 && curr.flags == next.flags && + curr.depth == next.depth && + ((curr.flags & BDRV_BLOCK_OFFSET_VALID) == 0 || + curr.offset + curr.length == next.offset)) { + curr.length += next.length; + continue; + } + + if (curr.length > 0) { + dump_map_entry(output_format, &curr, &next); + } + curr = next; + } + + dump_map_entry(output_format, &curr, NULL); + +out: + bdrv_unref(bs); + return ret < 0; +} + #define SNAPSHOT_LIST 1 #define SNAPSHOT_CREATE 2 #define SNAPSHOT_APPLY 3 @@ -1794,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 */ @@ -1886,16 +2096,18 @@ 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; } /* Cleanup */ - bdrv_delete(bs); + bdrv_unref(bs); if (ret) { return 1; } @@ -1912,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; @@ -2015,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; } } @@ -2076,6 +2292,11 @@ static int img_rebase(int argc, char **argv) /* If the cluster is allocated, we don't need to take action */ ret = bdrv_is_allocated(bs, sector, n, &n); + if (ret < 0) { + error_report("error while reading image metadata: %s", + strerror(-ret)); + goto out; + } if (ret) { continue; } @@ -2170,14 +2391,14 @@ out: /* Cleanup */ if (!unsafe) { if (bs_old_backing != NULL) { - bdrv_delete(bs_old_backing); + bdrv_unref(bs_old_backing); } if (bs_new_backing != NULL) { - bdrv_delete(bs_new_backing); + bdrv_unref(bs_new_backing); } } - bdrv_delete(bs); + bdrv_unref(bs); if (ret) { return 1; } @@ -2300,8 +2521,92 @@ static int img_resize(int argc, char **argv) } out: if (bs) { - bdrv_delete(bs); + bdrv_unref(bs); + } + if (ret) { + return 1; } + 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; } diff --git a/qemu-img.texi b/qemu-img.texi index 69f1bda6ae..768054e900 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -96,6 +96,14 @@ Second image format Strict mode - fail on on different image size or sector allocation @end table +Parameters to convert subcommand: + +@table @option + +@item -n +Skip the creation of the target volume +@end table + Command description: @table @option @@ -171,7 +179,7 @@ Error on reading data @end table -@item convert [-c] [-p] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} @@ -190,6 +198,11 @@ created as a copy on write image of the specified base image; the @var{backing_file} should have the same content as the input's base image, however the path, image format, etc may differ. +If the @code{-n} option is specified, the target volume creation will be +skipped. This is useful for formats such as @code{rbd} if the target +volume has already been created with site specific options that cannot +be supplied through qemu-img. + @item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename} Give information about the disk image @var{filename}. Use it in @@ -213,6 +226,61 @@ To enumerate information about each disk image in the above chain, starting from qemu-img info --backing-chain snap2.qcow2 @end example +@item map [-f @var{fmt}] [--output=@var{ofmt}] @var{filename} + +Dump the metadata of image @var{filename} and its backing file chain. +In particular, this commands dumps the allocation state of every sector +of @var{filename}, together with the topmost file that allocates it in +the backing file chain. + +Two option formats are possible. The default format (@code{human}) +only dumps known-nonzero areas of the file. Known-zero parts of the +file are omitted altogether, and likewise for parts that are not allocated +throughout the chain. @command{qemu-img} output will identify a file +from where the data can be read, and the offset in the file. Each line +will include four fields, the first three of which are hexadecimal +numbers. For example the first line of: +@example +Offset Length Mapped to File +0 0x20000 0x50000 /tmp/overlay.qcow2 +0x100000 0x10000 0x95380000 /tmp/backing.qcow2 +@end example +@noindent +means that 0x20000 (131072) bytes starting at offset 0 in the image are +available in /tmp/overlay.qcow2 (opened in @code{raw} format) starting +at offset 0x50000 (327680). Data that is compressed, encrypted, or +otherwise not available in raw format will cause an error if @code{human} +format is in use. Note that file names can include newlines, thus it is +not safe to parse this output format in scripts. + +The alternative format @code{json} will return an array of dictionaries +in JSON format. It will include similar information in +the @code{start}, @code{length}, @code{offset} fields; +it will also include other more specific information: +@itemize @minus +@item +whether the sectors contain actual data or not (boolean field @code{data}; +if false, the sectors are either unallocated or stored as optimized +all-zero clusters); + +@item +whether the data is known to read as zero (boolean field @code{zero}); + +@item +in order to make the output shorter, the target file is expressed as +a @code{depth}; for example, a depth of 2 refers to the backing file +of the backing file of @var{filename}. +@end itemize + +In JSON format, the @code{offset} field is optional; it is absent in +cases where @code{human} format would omit the entry or exit with an error. +If @code{data} is false and the @code{offset} field is present, the +corresponding sectors in the file are not yet in use, but they are +preallocated. + +For more information, consult @file{include/block/block.h} in QEMU's +source code. + @item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename} List, apply, create or delete snapshots in image @var{filename}. @@ -282,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 diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index f91b6c4f02..8565d49336 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1830,6 +1830,10 @@ static int alloc_f(BlockDriverState *bs, int argc, char **argv) sector_num = offset >> 9; while (remaining) { ret = bdrv_is_allocated(bs, sector_num, remaining, &num); + if (ret < 0) { + printf("is_allocated failed: %s\n", strerror(-ret)); + return 0; + } sector_num += num; remaining -= num; if (ret) { @@ -32,7 +32,7 @@ static char **cmdline; static int close_f(BlockDriverState *bs, int argc, char **argv) { - bdrv_delete(bs); + bdrv_unref(bs); qemuio_bs = NULL; return 0; } @@ -46,22 +46,28 @@ 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); - bdrv_delete(qemuio_bs); + 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; } @@ -422,7 +428,7 @@ int main(int argc, char **argv) bdrv_drain_all(); if (qemuio_bs) { - bdrv_delete(qemuio_bs); + bdrv_unref(qemuio_bs); } return 0; } 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-options.hx b/qemu-options.hx index d15338e879..5dc8b75cdb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -409,7 +409,11 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive, " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" " [,serial=s][,addr=A][,id=name][,aio=threads|native]\n" " [,readonly=on|off][,copy-on-read=on|off]\n" - " [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]][[,iops=i]|[[,iops_rd=r][,iops_wr=w]]\n" + " [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]]\n" + " [[,iops=i]|[[,iops_rd=r][,iops_wr=w]]]\n" + " [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n" + " [[,iops_max=im]|[[,iops_rd_max=irm][,iops_wr_max=iwm]]]\n" + " [[,iops_size=is]]\n" " use 'file' as a drive image\n", QEMU_ARCH_ALL) STEXI @item -drive @var{option}[,@var{option}[,@var{option}[,...]]] diff --git a/qemu-seccomp.c b/qemu-seccomp.c index 37d38f881c..69cee443af 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -90,6 +90,7 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = { { SCMP_SYS(getuid), 245 }, { SCMP_SYS(geteuid), 245 }, { SCMP_SYS(timer_create), 245 }, + { SCMP_SYS(times), 245 }, { SCMP_SYS(exit), 245 }, { SCMP_SYS(clock_gettime), 245 }, { SCMP_SYS(time), 245 }, 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/qga/Makefile.objs b/qga/Makefile.objs index b8d7cd0a43..1c5986c0bb 100644 --- a/qga/Makefile.objs +++ b/qga/Makefile.objs @@ -1,5 +1,8 @@ qga-obj-y = commands.o guest-agent-command-state.o main.o qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o +qga-obj-$(CONFIG_WIN32) += vss-win32.o qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o qga-obj-y += qapi-generated/qga-qmp-marshal.o + +qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/ diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 24e4ad0319..7a37f5cadd 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -15,6 +15,7 @@ #include <wtypes.h> #include <powrprof.h> #include "qga/guest-agent-core.h" +#include "qga/vss-win32.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -156,27 +157,89 @@ void qmp_guest_file_flush(int64_t handle, Error **err) */ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) { - error_set(err, QERR_UNSUPPORTED); - return 0; + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + if (ga_is_frozen(ga_state)) { + return GUEST_FSFREEZE_STATUS_FROZEN; + } + + return GUEST_FSFREEZE_STATUS_THAWED; } /* - * Walk list of mounted file systems in the guest, and freeze the ones which - * are real local file systems. + * Freeze local file systems using Volume Shadow-copy Service. + * The frozen state is limited for up to 10 seconds by VSS. */ int64_t qmp_guest_fsfreeze_freeze(Error **err) { - error_set(err, QERR_UNSUPPORTED); + int i; + Error *local_err = NULL; + + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + slog("guest-fsfreeze called"); + + /* cannot risk guest agent blocking itself on a write in this state */ + ga_set_frozen(ga_state); + + qga_vss_fsfreeze(&i, err, true); + if (error_is_set(err)) { + goto error; + } + + return i; + +error: + qmp_guest_fsfreeze_thaw(&local_err); + if (error_is_set(&local_err)) { + g_debug("cleanup thaw: %s", error_get_pretty(local_err)); + error_free(local_err); + } return 0; } /* - * Walk list of frozen file systems in the guest, and thaw them. + * Thaw local file systems using Volume Shadow-copy Service. */ int64_t qmp_guest_fsfreeze_thaw(Error **err) { - error_set(err, QERR_UNSUPPORTED); - return 0; + int i; + + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + qga_vss_fsfreeze(&i, err, false); + + ga_unset_frozen(ga_state); + return i; +} + +static void guest_fsfreeze_cleanup(void) +{ + Error *err = NULL; + + if (!vss_initialized()) { + return; + } + + if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); + } + } + + vss_deinit(true); } /* @@ -354,4 +417,7 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { + if (vss_init(true)) { + ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); + } } diff --git a/qga/main.c b/qga/main.c index 0e04e7395c..6c746c8f3a 100644 --- a/qga/main.c +++ b/qga/main.c @@ -34,6 +34,7 @@ #include "qemu/bswap.h" #ifdef _WIN32 #include "qga/service-win32.h" +#include "qga/vss-win32.h" #include <windows.h> #endif #ifdef __linux__ @@ -1031,8 +1032,15 @@ int main(int argc, char **argv) fixed_state_dir = (state_dir == dfl_pathnames.state_dir) ? NULL : state_dir; - return ga_install_service(path, log_filepath, fixed_state_dir); + if (ga_install_vss_provider()) { + return EXIT_FAILURE; + } + if (ga_install_service(path, log_filepath, fixed_state_dir)) { + return EXIT_FAILURE; + } + return 0; } else if (strcmp(service, "uninstall") == 0) { + ga_uninstall_vss_provider(); return ga_uninstall_service(); } else { printf("Unknown service command.\n"); diff --git a/qga/vss-win32.c b/qga/vss-win32.c new file mode 100644 index 0000000000..24c428842b --- /dev/null +++ b/qga/vss-win32.c @@ -0,0 +1,166 @@ +/* + * QEMU Guest Agent VSS utility functions + * + * Copyright Hitachi Data Systems Corp. 2013 + * + * Authors: + * Tomoki Sekiyama <tomoki.sekiyama@hds.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <stdio.h> +#include <windows.h> +#include "qga/guest-agent-core.h" +#include "qga/vss-win32.h" +#include "qga/vss-win32/requester.h" + +#define QGA_VSS_DLL "qga-vss.dll" + +static HMODULE provider_lib; + +/* Call a function in qga-vss.dll with the specified name */ +static HRESULT call_vss_provider_func(const char *func_name) +{ + FARPROC WINAPI func; + + g_assert(provider_lib); + + func = GetProcAddress(provider_lib, func_name); + if (!func) { + char *msg; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + fprintf(stderr, "failed to load %s from %s: %s", + func_name, QGA_VSS_DLL, msg); + LocalFree(msg); + return E_FAIL; + } + + return func(); +} + +/* Check whether this OS version supports VSS providers */ +static bool vss_check_os_version(void) +{ + OSVERSIONINFO OSver; + + OSver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&OSver); + if ((OSver.dwMajorVersion == 5 && OSver.dwMinorVersion >= 2) || + OSver.dwMajorVersion > 5) { + BOOL wow64 = false; +#ifndef _WIN64 + /* Provider doesn't work under WOW64 (32bit agent on 64bit OS) */ + if (!IsWow64Process(GetCurrentProcess(), &wow64)) { + fprintf(stderr, "failed to IsWow64Process (Error: %lx\n)\n", + GetLastError()); + return false; + } + if (wow64) { + fprintf(stderr, "Warning: Running under WOW64\n"); + } +#endif + return !wow64; + } + return false; +} + +/* Load qga-vss.dll */ +bool vss_init(bool init_requester) +{ + if (!vss_check_os_version()) { + /* Do nothing if OS doesn't support providers. */ + fprintf(stderr, "VSS provider is not supported in this OS version: " + "fsfreeze is disabled.\n"); + return false; + } + + provider_lib = LoadLibraryA(QGA_VSS_DLL); + if (!provider_lib) { + char *msg; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + fprintf(stderr, "failed to load %s: %sfsfreeze is disabled\n", + QGA_VSS_DLL, msg); + LocalFree(msg); + return false; + } + + if (init_requester) { + HRESULT hr = call_vss_provider_func("requester_init"); + if (FAILED(hr)) { + fprintf(stderr, "fsfreeze is disabled.\n"); + vss_deinit(false); + return false; + } + } + + return true; +} + +/* Unload qga-provider.dll */ +void vss_deinit(bool deinit_requester) +{ + if (deinit_requester) { + call_vss_provider_func("requester_deinit"); + } + FreeLibrary(provider_lib); + provider_lib = NULL; +} + +bool vss_initialized(void) +{ + return !!provider_lib; +} + +int ga_install_vss_provider(void) +{ + HRESULT hr; + + if (!vss_init(false)) { + fprintf(stderr, "Installation of VSS provider is skipped. " + "fsfreeze will be disabled.\n"); + return 0; + } + hr = call_vss_provider_func("COMRegister"); + vss_deinit(false); + + return SUCCEEDED(hr) ? 0 : EXIT_FAILURE; +} + +void ga_uninstall_vss_provider(void) +{ + if (!vss_init(false)) { + fprintf(stderr, "Removal of VSS provider is skipped.\n"); + return; + } + call_vss_provider_func("COMUnregister"); + vss_deinit(false); +} + +/* Call VSS requester and freeze/thaw filesystems and applications */ +void qga_vss_fsfreeze(int *nr_volume, Error **err, bool freeze) +{ + const char *func_name = freeze ? "requester_freeze" : "requester_thaw"; + QGAVSSRequesterFunc func; + ErrorSet errset = { + .error_set = (ErrorSetFunc)error_set_win32, + .errp = (void **)err, + .err_class = ERROR_CLASS_GENERIC_ERROR + }; + + func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name); + if (!func) { + error_setg_win32(err, GetLastError(), "failed to load %s from %s", + func_name, QGA_VSS_DLL); + return; + } + + func(nr_volume, &errset); +} diff --git a/qga/vss-win32.h b/qga/vss-win32.h new file mode 100644 index 0000000000..db8fbe5208 --- /dev/null +++ b/qga/vss-win32.h @@ -0,0 +1,27 @@ +/* + * QEMU Guest Agent VSS utility declarations + * + * Copyright Hitachi Data Systems Corp. 2013 + * + * Authors: + * Tomoki Sekiyama <tomoki.sekiyama@hds.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef VSS_WIN32_H +#define VSS_WIN32_H + +#include "qapi/error.h" + +bool vss_init(bool init_requester); +void vss_deinit(bool deinit_requester); +bool vss_initialized(void); + +int ga_install_vss_provider(void); +void ga_uninstall_vss_provider(void); + +void qga_vss_fsfreeze(int *nr_volume, Error **err, bool freeze); + +#endif diff --git a/qga/vss-win32/Makefile.objs b/qga/vss-win32/Makefile.objs new file mode 100644 index 0000000000..6a69d5008d --- /dev/null +++ b/qga/vss-win32/Makefile.objs @@ -0,0 +1,23 @@ +# rules to build qga-vss.dll + +qga-vss-dll-obj-y += requester.o provider.o install.o + +obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y)) +$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -fstack-protector-all, $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor + +$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static +$(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def + $(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS)," LINK $(TARGET_DIR)$@") + + +# rules to build qga-provider.tlb +# Currently, only native build is supported because building .tlb +# (TypeLibrary) from .idl requires WindowsSDK and MIDL (and cl.exe in VC++). +MIDL=$(WIN_SDK)/Bin/midl + +$(obj)/qga-vss.tlb: $(SRC_PATH)/$(obj)/qga-vss.idl +ifeq ($(WIN_SDK),"") + $(call quiet-command,cp $(dir $<)qga-vss.tlb $@, " COPY $(TARGET_DIR)$@") +else + $(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<," MIDL $(TARGET_DIR)$@") +endif diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp new file mode 100644 index 0000000000..37731a7271 --- /dev/null +++ b/qga/vss-win32/install.cpp @@ -0,0 +1,458 @@ +/* + * QEMU Guest Agent win32 VSS Provider installer + * + * Copyright Hitachi Data Systems Corp. 2013 + * + * Authors: + * Tomoki Sekiyama <tomoki.sekiyama@hds.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <stdio.h> +#include <string.h> + +#include "vss-common.h" +#include "inc/win2003/vscoordint.h" + +#include <comadmin.h> +#include <wbemidl.h> +#include <comdef.h> +#include <comutil.h> + +extern HINSTANCE g_hinstDll; + +const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1, + {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} }; +const GUID IID_ICOMAdminCatalog = { 0xDD662187, 0xDFC2, 0x11d1, + {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} }; +const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0, + {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; +const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf, + {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; + +void errmsg(DWORD err, const char *text) +{ + /* + * `text' contains function call statement when errmsg is called via chk(). + * To make error message more readable, we cut off the text after '('. + * If text doesn't contains '(', negative precision is given, which is + * treated as though it were missing. + */ + char *msg = NULL, *nul = strchr(text, '('); + int len = nul ? nul - text : -1; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + fprintf(stderr, "%.*s. (Error: %lx) %s\n", len, text, err, msg); + LocalFree(msg); +} + +static void errmsg_dialog(DWORD err, const char *text, const char *opt = "") +{ + char *msg, buf[512]; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s", text, opt, err, msg); + MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME, MB_OK|MB_ICONERROR); + LocalFree(msg); +} + +#define _chk(hr, status, msg, err_label) \ + do { \ + hr = (status); \ + if (FAILED(hr)) { \ + errmsg(hr, msg); \ + goto err_label; \ + } \ + } while (0) + +#define chk(status) _chk(hr, status, "Failed to " #status, out) + +void __stdcall _com_issue_error(HRESULT hr) +{ + errmsg(hr, "Unexpected error in COM"); +} + +template<class T> +HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val) +{ + return pObj->put_Value(_bstr_t(name), _variant_t(val)); +} + +/* Lookup Administrators group name from winmgmt */ +static HRESULT GetAdminName(_bstr_t *name) +{ + HRESULT hr; + COMPointer<IWbemLocator> pLoc; + COMPointer<IWbemServices> pSvc; + COMPointer<IEnumWbemClassObject> pEnum; + COMPointer<IWbemClassObject> pWobj; + ULONG returned; + _variant_t var; + + chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID *)pLoc.replace())); + chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL, + 0, 0, 0, pSvc.replace())); + chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, + NULL, RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE)); + chk(pSvc->ExecQuery(_bstr_t(L"WQL"), + _bstr_t(L"select * from Win32_Account where " + "SID='S-1-5-32-544' and localAccount=TRUE"), + WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, + NULL, pEnum.replace())); + if (!pEnum) { + hr = E_FAIL; + errmsg(hr, "Failed to query for Administrators"); + goto out; + } + chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned)); + if (returned == 0) { + hr = E_FAIL; + errmsg(hr, "No Administrators found"); + goto out; + } + + chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0)); + try { + *name = var; + } catch(...) { + hr = E_FAIL; + errmsg(hr, "Failed to get name of Administrators"); + goto out; + } + +out: + return hr; +} + +/* Find and iterate QGA VSS provider in COM+ Application Catalog */ +static HRESULT QGAProviderFind( + HRESULT (*found)(ICatalogCollection *, int, void *), void *arg) +{ + HRESULT hr; + COMInitializer initializer; + COMPointer<IUnknown> pUnknown; + COMPointer<ICOMAdminCatalog> pCatalog; + COMPointer<ICatalogCollection> pColl; + COMPointer<ICatalogObject> pObj; + _variant_t var; + long i, n; + + chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, + IID_IUnknown, (void **)pUnknown.replace())); + chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, + (void **)pCatalog.replace())); + chk(pCatalog->GetCollection(_bstr_t(L"Applications"), + (IDispatch **)pColl.replace())); + chk(pColl->Populate()); + + chk(pColl->get_Count(&n)); + for (i = n - 1; i >= 0; i--) { + chk(pColl->get_Item(i, (IDispatch **)pObj.replace())); + chk(pObj->get_Value(_bstr_t(L"Name"), &var)); + if (var == _variant_t(QGA_PROVIDER_LNAME)) { + if (FAILED(found(pColl, i, arg))) { + goto out; + } + } + } + chk(pColl->SaveChanges(&n)); + +out: + return hr; +} + +/* Count QGA VSS provider in COM+ Application Catalog */ +static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg) +{ + (*(int *)arg)++; + return S_OK; +} + +/* Remove QGA VSS provider from COM+ Application Catalog Collection */ +static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg) +{ + HRESULT hr; + + fprintf(stderr, "Removing COM+ Application: %s\n", QGA_PROVIDER_NAME); + chk(coll->Remove(i)); +out: + return hr; +} + +/* Unregister this module from COM+ Applications Catalog */ +STDAPI COMUnregister(void) +{ + HRESULT hr; + + DllUnregisterServer(); + chk(QGAProviderFind(QGAProviderRemove, NULL)); +out: + return hr; +} + +/* Register this module to COM+ Applications Catalog */ +STDAPI COMRegister(void) +{ + HRESULT hr; + COMInitializer initializer; + COMPointer<IUnknown> pUnknown; + COMPointer<ICOMAdminCatalog> pCatalog; + COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole; + COMPointer<ICatalogObject> pObj; + long n; + _bstr_t name; + _variant_t key; + CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH]; + bool unregisterOnFailure = false; + int count = 0; + + if (!g_hinstDll) { + errmsg(E_FAIL, "Failed to initialize DLL"); + return E_FAIL; + } + + chk(QGAProviderFind(QGAProviderCount, (void *)&count)); + if (count) { + errmsg(E_ABORT, "QGA VSS Provider is already installed"); + return E_ABORT; + } + + chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, + IID_IUnknown, (void **)pUnknown.replace())); + chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, + (void **)pCatalog.replace())); + + /* Install COM+ Component */ + + chk(pCatalog->GetCollection(_bstr_t(L"Applications"), + (IDispatch **)pApps.replace())); + chk(pApps->Populate()); + chk(pApps->Add((IDispatch **)&pObj)); + chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME)); + chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME)); + chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true)); + chk(put_Value(pObj, L"Authentication", short(6))); + chk(put_Value(pObj, L"AuthenticationCapability", short(2))); + chk(put_Value(pObj, L"ImpersonationLevel", short(2))); + chk(pApps->SaveChanges(&n)); + + /* The app should be deleted if something fails after SaveChanges */ + unregisterOnFailure = true; + + chk(pObj->get_Key(&key)); + + if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { + hr = HRESULT_FROM_WIN32(GetLastError()); + errmsg(hr, "GetModuleFileName failed"); + goto out; + } + n = strlen(dllPath); + if (n < 3) { + hr = E_FAIL; + errmsg(hr, "Failed to lookup dll"); + goto out; + } + strcpy(tlbPath, dllPath); + strcpy(tlbPath+n-3, "tlb"); + fprintf(stderr, "Registering " QGA_PROVIDER_NAME ":\n"); + fprintf(stderr, " %s\n", dllPath); + fprintf(stderr, " %s\n", tlbPath); + if (!PathFileExists(tlbPath)) { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + errmsg(hr, "Failed to lookup tlb"); + goto out; + } + + chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME), + _bstr_t(dllPath), _bstr_t(tlbPath), + _bstr_t(""))); + + /* Setup roles of the applicaion */ + + chk(pApps->GetCollection(_bstr_t(L"Roles"), key, + (IDispatch **)pRoles.replace())); + chk(pRoles->Populate()); + chk(pRoles->Add((IDispatch **)pObj.replace())); + chk(put_Value(pObj, L"Name", L"Administrators")); + chk(put_Value(pObj, L"Description", L"Administrators group")); + chk(pRoles->SaveChanges(&n)); + chk(pObj->get_Key(&key)); + + /* Setup users in the role */ + + chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key, + (IDispatch **)pUsersInRole.replace())); + chk(pUsersInRole->Populate()); + + chk(pUsersInRole->Add((IDispatch **)pObj.replace())); + chk(GetAdminName(&name)); + chk(put_Value(pObj, L"User", _bstr_t(".\\") + name)); + + chk(pUsersInRole->Add((IDispatch **)pObj.replace())); + chk(put_Value(pObj, L"User", L"SYSTEM")); + chk(pUsersInRole->SaveChanges(&n)); + +out: + if (unregisterOnFailure && FAILED(hr)) { + COMUnregister(); + } + + return hr; +} + + +static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data) +{ + HKEY hKey; + LONG ret; + DWORD size; + + ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); + if (ret != ERROR_SUCCESS) { + goto out; + } + + if (data != NULL) { + size = strlen(data) + 1; + } else { + size = 0; + } + + ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size); + RegCloseKey(hKey); + +out: + if (ret != ERROR_SUCCESS) { + /* As we cannot printf within DllRegisterServer(), show a dialog. */ + errmsg_dialog(ret, "Cannot add registry", key); + return FALSE; + } + return TRUE; +} + +/* Register this dll as a VSS provider */ +STDAPI DllRegisterServer(void) +{ + COMInitializer initializer; + COMPointer<IVssAdmin> pVssAdmin; + HRESULT hr = E_FAIL; + char dllPath[MAX_PATH]; + char key[256]; + + if (!g_hinstDll) { + errmsg_dialog(hr, "Module instance is not available"); + goto out; + } + + /* Add this module to registery */ + + sprintf(key, "CLSID\\%s", g_szClsid); + if (!CreateRegistryKey(key, NULL, g_szClsid)) { + goto out; + } + + if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { + errmsg_dialog(GetLastError(), "GetModuleFileName failed"); + goto out; + } + + sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid); + if (!CreateRegistryKey(key, NULL, dllPath)) { + goto out; + } + + if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) { + goto out; + } + + sprintf(key, "CLSID\\%s\\ProgID", g_szClsid); + if (!CreateRegistryKey(key, NULL, g_szProgid)) { + goto out; + } + + if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) { + goto out; + } + + sprintf(key, "%s\\CLSID", g_szProgid); + if (!CreateRegistryKey(key, NULL, g_szClsid)) { + goto out; + } + + hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL, + IID_IVssAdmin, (void **)pVssAdmin.replace()); + if (FAILED(hr)) { + errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed"); + goto out; + } + + hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider, + const_cast<WCHAR*>(QGA_PROVIDER_LNAME), + VSS_PROV_SOFTWARE, + const_cast<WCHAR*>(QGA_PROVIDER_VERSION), + g_gProviderVersion); + if (FAILED(hr)) { + errmsg_dialog(hr, "RegisterProvider failed"); + } + +out: + if (FAILED(hr)) { + DllUnregisterServer(); + } + + return hr; +} + +/* Unregister this VSS hardware provider from the system */ +STDAPI DllUnregisterServer(void) +{ + TCHAR key[256]; + COMInitializer initializer; + COMPointer<IVssAdmin> pVssAdmin; + + HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator, + NULL, CLSCTX_ALL, IID_IVssAdmin, + (void **)pVssAdmin.replace()); + if (SUCCEEDED(hr)) { + hr = pVssAdmin->UnregisterProvider(g_gProviderId); + } else { + errmsg(hr, "CoCreateInstance(VSSCoordinator) failed"); + } + + sprintf(key, "CLSID\\%s", g_szClsid); + SHDeleteKey(HKEY_CLASSES_ROOT, key); + SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid); + + return S_OK; /* Uninstall should never fail */ +} + + +/* Support function to convert ASCII string into BSTR (used in _bstr_t) */ +namespace _com_util +{ + BSTR WINAPI ConvertStringToBSTR(const char *ascii) { + int len = strlen(ascii); + BSTR bstr = SysAllocStringLen(NULL, len); + + if (!bstr) { + return NULL; + } + + if (mbstowcs(bstr, ascii, len) == (size_t)-1) { + fprintf(stderr, "Failed to convert string '%s' into BSTR", ascii); + bstr[0] = 0; + } + return bstr; + } +} diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp new file mode 100644 index 0000000000..bf42b5e95f --- /dev/null +++ b/qga/vss-win32/provider.cpp @@ -0,0 +1,523 @@ +/* + * QEMU Guest Agent win32 VSS Provider implementations + * + * Copyright Hitachi Data Systems Corp. 2013 + * + * Authors: + * Tomoki Sekiyama <tomoki.sekiyama@hds.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <stdio.h> +#include "vss-common.h" +#include "inc/win2003/vscoordint.h" +#include "inc/win2003/vsprov.h" + +#define VSS_TIMEOUT_MSEC (60*1000) + +static long g_nComObjsInUse; +HINSTANCE g_hinstDll; + +/* VSS common GUID's */ + +const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4, + {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} }; +const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3, + {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; + +const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344, + {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} }; +const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3, + {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} }; +const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778, + {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} }; +const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe, + {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} }; + +const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3, + {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; + + +void LockModule(BOOL lock) +{ + if (lock) { + InterlockedIncrement(&g_nComObjsInUse); + } else { + InterlockedDecrement(&g_nComObjsInUse); + } +} + +/* Empty enumerator for VssObject */ + +class CQGAVSSEnumObject : public IVssEnumObject +{ +public: + STDMETHODIMP QueryInterface(REFIID riid, void **ppObj); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + /* IVssEnumObject Methods */ + STDMETHODIMP Next( + ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched); + STDMETHODIMP Skip(ULONG celt); + STDMETHODIMP Reset(void); + STDMETHODIMP Clone(IVssEnumObject **ppenum); + + /* CQGAVSSEnumObject Methods */ + CQGAVSSEnumObject(); + ~CQGAVSSEnumObject(); + +private: + long m_nRefCount; +}; + +CQGAVSSEnumObject::CQGAVSSEnumObject() +{ + m_nRefCount = 0; + LockModule(TRUE); +} + +CQGAVSSEnumObject::~CQGAVSSEnumObject() +{ + LockModule(FALSE); +} + +STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj) +{ + if (riid == IID_IUnknown || riid == IID_IVssEnumObject) { + *ppObj = static_cast<void*>(static_cast<IVssEnumObject*>(this)); + AddRef(); + return S_OK; + } + *ppObj = NULL; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef() +{ + return InterlockedIncrement(&m_nRefCount); +} + +STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release() +{ + long nRefCount = InterlockedDecrement(&m_nRefCount); + if (m_nRefCount == 0) { + delete this; + } + return nRefCount; +} + +STDMETHODIMP CQGAVSSEnumObject::Next( + ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched) +{ + *pceltFetched = 0; + return S_FALSE; +} + +STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt) +{ + return S_FALSE; +} + +STDMETHODIMP CQGAVSSEnumObject::Reset(void) +{ + return S_OK; +} + +STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum) +{ + return E_NOTIMPL; +} + + +/* QGAVssProvider */ + +class CQGAVssProvider : + public IVssSoftwareSnapshotProvider, + public IVssProviderCreateSnapshotSet, + public IVssProviderNotifications +{ +public: + STDMETHODIMP QueryInterface(REFIID riid, void **ppObj); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + /* IVssSoftwareSnapshotProvider Methods */ + STDMETHODIMP SetContext(LONG lContext); + STDMETHODIMP GetSnapshotProperties( + VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp); + STDMETHODIMP Query( + VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType, + VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum); + STDMETHODIMP DeleteSnapshots( + VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, + BOOL bForceDelete, LONG *plDeletedSnapshots, + VSS_ID *pNondeletedSnapshotID); + STDMETHODIMP BeginPrepareSnapshot( + VSS_ID SnapshotSetId, VSS_ID SnapshotId, + VSS_PWSZ pwszVolumeName, LONG lNewContext); + STDMETHODIMP IsVolumeSupported( + VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider); + STDMETHODIMP IsVolumeSnapshotted( + VSS_PWSZ pwszVolumeName, BOOL *pbSnapshotsPresent, + LONG *plSnapshotCompatibility); + STDMETHODIMP SetSnapshotProperty( + VSS_ID SnapshotId, VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, + VARIANT vProperty); + STDMETHODIMP RevertToSnapshot(VSS_ID SnapshotId); + STDMETHODIMP QueryRevertStatus(VSS_PWSZ pwszVolume, IVssAsync **ppAsync); + + /* IVssProviderCreateSnapshotSet Methods */ + STDMETHODIMP EndPrepareSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP PreCommitSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP CommitSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP PostCommitSnapshots( + VSS_ID SnapshotSetId, LONG lSnapshotsCount); + STDMETHODIMP PreFinalCommitSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP PostFinalCommitSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP AbortSnapshots(VSS_ID SnapshotSetId); + + /* IVssProviderNotifications Methods */ + STDMETHODIMP OnLoad(IUnknown *pCallback); + STDMETHODIMP OnUnload(BOOL bForceUnload); + + /* CQGAVssProvider Methods */ + CQGAVssProvider(); + ~CQGAVssProvider(); + +private: + long m_nRefCount; +}; + +CQGAVssProvider::CQGAVssProvider() +{ + m_nRefCount = 0; + LockModule(TRUE); +} + +CQGAVssProvider::~CQGAVssProvider() +{ + LockModule(FALSE); +} + +STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj) +{ + if (riid == IID_IUnknown) { + *ppObj = static_cast<void*>(this); + AddRef(); + return S_OK; + } + if (riid == IID_IVssSoftwareSnapshotProvider) { + *ppObj = static_cast<void*>( + static_cast<IVssSoftwareSnapshotProvider*>(this)); + AddRef(); + return S_OK; + } + if (riid == IID_IVssProviderCreateSnapshotSet) { + *ppObj = static_cast<void*>( + static_cast<IVssProviderCreateSnapshotSet*>(this)); + AddRef(); + return S_OK; + } + if (riid == IID_IVssProviderNotifications) { + *ppObj = static_cast<void*>( + static_cast<IVssProviderNotifications*>(this)); + AddRef(); + return S_OK; + } + *ppObj = NULL; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef() +{ + return InterlockedIncrement(&m_nRefCount); +} + +STDMETHODIMP_(ULONG) CQGAVssProvider::Release() +{ + long nRefCount = InterlockedDecrement(&m_nRefCount); + if (m_nRefCount == 0) { + delete this; + } + return nRefCount; +} + + +/* + * IVssSoftwareSnapshotProvider methods + */ + +STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::GetSnapshotProperties( + VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp) +{ + return VSS_E_OBJECT_NOT_FOUND; +} + +STDMETHODIMP CQGAVssProvider::Query( + VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType, + VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum) +{ + try { + *ppEnum = new CQGAVSSEnumObject; + } catch (...) { + return E_OUTOFMEMORY; + } + (*ppEnum)->AddRef(); + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::DeleteSnapshots( + VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, + BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( + VSS_ID SnapshotSetId, VSS_ID SnapshotId, + VSS_PWSZ pwszVolumeName, LONG lNewContext) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::IsVolumeSupported( + VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider) +{ + *pbSupportedByThisProvider = TRUE; + + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName, + BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility) +{ + *pbSnapshotsPresent = FALSE; + *plSnapshotCompatibility = 0; + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId, + VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CQGAVssProvider::QueryRevertStatus( + VSS_PWSZ pwszVolume, IVssAsync **ppAsync) +{ + return E_NOTIMPL; +} + + +/* + * IVssProviderCreateSnapshotSet methods + */ + +STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId) +{ + HRESULT hr = S_OK; + HANDLE hEventFrozen, hEventThaw, hEventTimeout; + + hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); + if (hEventFrozen == INVALID_HANDLE_VALUE) { + return E_FAIL; + } + + hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW); + if (hEventThaw == INVALID_HANDLE_VALUE) { + CloseHandle(hEventFrozen); + return E_FAIL; + } + + hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); + if (hEventTimeout == INVALID_HANDLE_VALUE) { + CloseHandle(hEventFrozen); + CloseHandle(hEventThaw); + return E_FAIL; + } + + /* Send event to qemu-ga to notify filesystem is frozen */ + SetEvent(hEventFrozen); + + /* Wait until the snapshot is taken by the host. */ + if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) { + /* Send event to qemu-ga to notify the provider is timed out */ + SetEvent(hEventTimeout); + hr = E_ABORT; + } + + CloseHandle(hEventThaw); + CloseHandle(hEventFrozen); + CloseHandle(hEventTimeout); + return hr; +} + +STDMETHODIMP CQGAVssProvider::PostCommitSnapshots( + VSS_ID SnapshotSetId, LONG lSnapshotsCount) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +/* + * IVssProviderNotifications methods + */ + +STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload) +{ + return S_OK; +} + + +/* + * CQGAVssProviderFactory class + */ + +class CQGAVssProviderFactory : public IClassFactory +{ +public: + STDMETHODIMP QueryInterface(REFIID riid, void **ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP CreateInstance( + IUnknown *pUnknownOuter, REFIID iid, void **ppv); + STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; } + + CQGAVssProviderFactory(); + ~CQGAVssProviderFactory(); + +private: + long m_nRefCount; +}; + +CQGAVssProviderFactory::CQGAVssProviderFactory() +{ + m_nRefCount = 0; + LockModule(TRUE); +} + +CQGAVssProviderFactory::~CQGAVssProviderFactory() +{ + LockModule(FALSE); +} + +STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv) +{ + if (riid == IID_IUnknown || riid == IID_IClassFactory) { + *ppv = static_cast<void*>(this); + AddRef(); + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef() +{ + return InterlockedIncrement(&m_nRefCount); +} + +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release() +{ + long nRefCount = InterlockedDecrement(&m_nRefCount); + if (m_nRefCount == 0) { + delete this; + } + return nRefCount; +} + +STDMETHODIMP CQGAVssProviderFactory::CreateInstance( + IUnknown *pUnknownOuter, REFIID iid, void **ppv) +{ + CQGAVssProvider *pObj; + + if (pUnknownOuter) { + return CLASS_E_NOAGGREGATION; + } + try { + pObj = new CQGAVssProvider; + } catch (...) { + return E_OUTOFMEMORY; + } + HRESULT hr = pObj->QueryInterface(iid, ppv); + if (FAILED(hr)) { + delete pObj; + } + return hr; +} + + +/* + * DLL functions + */ + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) +{ + CQGAVssProviderFactory *factory; + try { + factory = new CQGAVssProviderFactory; + } catch (...) { + return E_OUTOFMEMORY; + } + factory->AddRef(); + HRESULT hr = factory->QueryInterface(riid, ppv); + factory->Release(); + return hr; +} + +STDAPI DllCanUnloadNow() +{ + return g_nComObjsInUse == 0 ? S_OK : S_FALSE; +} + +EXTERN_C +BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved) +{ + if (dwReason == DLL_PROCESS_ATTACH) { + g_hinstDll = hinstDll; + DisableThreadLibraryCalls(hinstDll); + } + return TRUE; +} diff --git a/qga/vss-win32/qga-vss.def b/qga/vss-win32/qga-vss.def new file mode 100644 index 0000000000..927782c31b --- /dev/null +++ b/qga/vss-win32/qga-vss.def @@ -0,0 +1,13 @@ +LIBRARY "QGA-PROVIDER.DLL" + +EXPORTS + COMRegister PRIVATE + COMUnregister PRIVATE + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + requester_init PRIVATE + requester_deinit PRIVATE + requester_freeze PRIVATE + requester_thaw PRIVATE diff --git a/qga/vss-win32/qga-vss.idl b/qga/vss-win32/qga-vss.idl new file mode 100644 index 0000000000..17abca0da5 --- /dev/null +++ b/qga/vss-win32/qga-vss.idl @@ -0,0 +1,20 @@ +import "oaidl.idl"; +import "ocidl.idl"; + +[ + uuid(103B8142-6CE5-48A7-BDE1-794D3192FCF1), + version(1.0), + helpstring("QGAVSSProvider Type Library") +] +library QGAVSSHWProviderLib +{ + importlib("stdole2.tlb"); + [ + uuid(6E6A3492-8D4D-440C-9619-5E5D0CC31CA8), + helpstring("QGAVSSProvider Class") + ] + coclass QGAVSSHWProvider + { + [default] interface IUnknown; + }; +}; diff --git a/qga/vss-win32/qga-vss.tlb b/qga/vss-win32/qga-vss.tlb Binary files differnew file mode 100644 index 0000000000..226452a186 --- /dev/null +++ b/qga/vss-win32/qga-vss.tlb diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp new file mode 100644 index 0000000000..1e8dd3dfa8 --- /dev/null +++ b/qga/vss-win32/requester.cpp @@ -0,0 +1,507 @@ +/* + * QEMU Guest Agent win32 VSS Requester implementations + * + * Copyright Hitachi Data Systems Corp. 2013 + * + * Authors: + * Tomoki Sekiyama <tomoki.sekiyama@hds.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <stdio.h> +#include "vss-common.h" +#include "requester.h" +#include "assert.h" +#include "inc/win2003/vswriter.h" +#include "inc/win2003/vsbackup.h" + +/* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */ +#define VSS_TIMEOUT_FREEZE_MSEC 10000 + +/* Call QueryStatus every 10 ms while waiting for frozen event */ +#define VSS_TIMEOUT_EVENT_MSEC 10 + +#define err_set(e, err, fmt, ...) \ + ((e)->error_set((e)->errp, err, (e)->err_class, fmt, ## __VA_ARGS__)) +#define err_is_set(e) ((e)->errp && *(e)->errp) + + +/* Handle to VSSAPI.DLL */ +static HMODULE hLib; + +/* Functions in VSSAPI.DLL */ +typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)( + OUT IVssBackupComponents**); +typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*); +static t_CreateVssBackupComponents pCreateVssBackupComponents; +static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties; + +/* Variables used while applications and filesystes are frozen by VSS */ +static struct QGAVSSContext { + IVssBackupComponents *pVssbc; /* VSS requester interface */ + IVssAsync *pAsyncSnapshot; /* async info of VSS snapshot operation */ + HANDLE hEventFrozen; /* notify fs/writer freeze from provider */ + HANDLE hEventThaw; /* request provider to thaw */ + HANDLE hEventTimeout; /* notify timeout in provider */ + int cFrozenVols; /* number of frozen volumes */ +} vss_ctx; + +STDAPI requester_init(void) +{ + vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; + vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; + vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; + + COMInitializer initializer; /* to call CoInitializeSecurity */ + HRESULT hr = CoInitializeSecurity( + NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL); + if (FAILED(hr)) { + fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr); + return hr; + } + + hLib = LoadLibraryA("VSSAPI.DLL"); + if (!hLib) { + fprintf(stderr, "failed to load VSSAPI.DLL\n"); + return HRESULT_FROM_WIN32(GetLastError()); + } + + pCreateVssBackupComponents = (t_CreateVssBackupComponents) + GetProcAddress(hLib, +#ifdef _WIN64 /* 64bit environment */ + "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z" +#else /* 32bit environment */ + "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z" +#endif + ); + if (!pCreateVssBackupComponents) { + fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n"); + return HRESULT_FROM_WIN32(GetLastError()); + } + + pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties) + GetProcAddress(hLib, "VssFreeSnapshotProperties"); + if (!pVssFreeSnapshotProperties) { + fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n"); + return HRESULT_FROM_WIN32(GetLastError()); + } + + return S_OK; +} + +static void requester_cleanup(void) +{ + if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) { + CloseHandle(vss_ctx.hEventFrozen); + vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; + } + if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) { + CloseHandle(vss_ctx.hEventThaw); + vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; + } + if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) { + CloseHandle(vss_ctx.hEventTimeout); + vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; + } + if (vss_ctx.pAsyncSnapshot) { + vss_ctx.pAsyncSnapshot->Release(); + vss_ctx.pAsyncSnapshot = NULL; + } + if (vss_ctx.pVssbc) { + vss_ctx.pVssbc->Release(); + vss_ctx.pVssbc = NULL; + } + vss_ctx.cFrozenVols = 0; +} + +STDAPI requester_deinit(void) +{ + requester_cleanup(); + + pCreateVssBackupComponents = NULL; + pVssFreeSnapshotProperties = NULL; + if (hLib) { + FreeLibrary(hLib); + hLib = NULL; + } + + return S_OK; +} + +static HRESULT WaitForAsync(IVssAsync *pAsync) +{ + HRESULT ret, hr; + + do { + hr = pAsync->Wait(); + if (FAILED(hr)) { + ret = hr; + break; + } + hr = pAsync->QueryStatus(&ret, NULL); + if (FAILED(hr)) { + ret = hr; + break; + } + } while (ret == VSS_S_ASYNC_PENDING); + + return ret; +} + +static void AddComponents(ErrorSet *errset) +{ + unsigned int cWriters, i; + VSS_ID id, idInstance, idWriter; + BSTR bstrWriterName = NULL; + VSS_USAGE_TYPE usage; + VSS_SOURCE_TYPE source; + unsigned int cComponents, c1, c2, j; + COMPointer<IVssExamineWriterMetadata> pMetadata; + COMPointer<IVssWMComponent> pComponent; + PVSSCOMPONENTINFO info; + HRESULT hr; + + hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters); + if (FAILED(hr)) { + err_set(errset, hr, "failed to get writer metadata count"); + goto out; + } + + for (i = 0; i < cWriters; i++) { + hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace()); + if (FAILED(hr)) { + err_set(errset, hr, "failed to get writer metadata of %d/%d", + i, cWriters); + goto out; + } + + hr = pMetadata->GetIdentity(&idInstance, &idWriter, + &bstrWriterName, &usage, &source); + if (FAILED(hr)) { + err_set(errset, hr, "failed to get identity of writer %d/%d", + i, cWriters); + goto out; + } + + hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents); + if (FAILED(hr)) { + err_set(errset, hr, "failed to get file counts of %S", + bstrWriterName); + goto out; + } + + for (j = 0; j < cComponents; j++) { + hr = pMetadata->GetComponent(j, pComponent.replace()); + if (FAILED(hr)) { + err_set(errset, hr, + "failed to get component %d/%d of %S", + j, cComponents, bstrWriterName); + goto out; + } + + hr = pComponent->GetComponentInfo(&info); + if (FAILED(hr)) { + err_set(errset, hr, + "failed to get component info %d/%d of %S", + j, cComponents, bstrWriterName); + goto out; + } + + if (info->bSelectable) { + hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter, + info->type, + info->bstrLogicalPath, + info->bstrComponentName); + if (FAILED(hr)) { + err_set(errset, hr, "failed to add component %S(%S)", + info->bstrComponentName, bstrWriterName); + goto out; + } + } + SysFreeString(bstrWriterName); + bstrWriterName = NULL; + pComponent->FreeComponentInfo(info); + info = NULL; + } + } +out: + if (bstrWriterName) { + SysFreeString(bstrWriterName); + } + if (pComponent && info) { + pComponent->FreeComponentInfo(info); + } +} + +void requester_freeze(int *num_vols, ErrorSet *errset) +{ + COMPointer<IVssAsync> pAsync; + HANDLE volume; + HRESULT hr; + LONG ctx; + GUID guidSnapshotSet = GUID_NULL; + SECURITY_DESCRIPTOR sd; + SECURITY_ATTRIBUTES sa; + WCHAR short_volume_name[64], *display_name = short_volume_name; + DWORD wait_status; + int num_fixed_drives = 0, i; + + if (vss_ctx.pVssbc) { /* already frozen */ + *num_vols = 0; + return; + } + + CoInitialize(NULL); + + assert(pCreateVssBackupComponents != NULL); + hr = pCreateVssBackupComponents(&vss_ctx.pVssbc); + if (FAILED(hr)) { + err_set(errset, hr, "failed to create VSS backup components"); + goto out; + } + + hr = vss_ctx.pVssbc->InitializeForBackup(); + if (FAILED(hr)) { + err_set(errset, hr, "failed to initialize for backup"); + goto out; + } + + hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false); + if (FAILED(hr)) { + err_set(errset, hr, "failed to set backup state"); + goto out; + } + + /* + * Currently writable snapshots are not supported. + * To prevent the final commit (which requires to write to snapshots), + * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here. + */ + ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE | + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY; + hr = vss_ctx.pVssbc->SetContext(ctx); + if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) { + /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */ + ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE; + hr = vss_ctx.pVssbc->SetContext(ctx); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to set backup context"); + goto out; + } + + hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace()); + if (SUCCEEDED(hr)) { + hr = WaitForAsync(pAsync); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to gather writer metadata"); + goto out; + } + + AddComponents(errset); + if (err_is_set(errset)) { + goto out; + } + + hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet); + if (FAILED(hr)) { + err_set(errset, hr, "failed to start snapshot set"); + goto out; + } + + volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name)); + if (volume == INVALID_HANDLE_VALUE) { + err_set(errset, hr, "failed to find first volume"); + goto out; + } + for (;;) { + if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) { + VSS_ID pid; + hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name, + g_gProviderId, &pid); + if (FAILED(hr)) { + WCHAR volume_path_name[PATH_MAX]; + if (GetVolumePathNamesForVolumeNameW( + short_volume_name, volume_path_name, + sizeof(volume_path_name), NULL) && *volume_path_name) { + display_name = volume_path_name; + } + err_set(errset, hr, "failed to add %S to snapshot set", + display_name); + FindVolumeClose(volume); + goto out; + } + num_fixed_drives++; + } + if (!FindNextVolumeW(volume, short_volume_name, + sizeof(short_volume_name))) { + FindVolumeClose(volume); + break; + } + } + + if (num_fixed_drives == 0) { + goto out; /* If there is no fixed drive, just exit. */ + } + + hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace()); + if (SUCCEEDED(hr)) { + hr = WaitForAsync(pAsync); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to prepare for backup"); + goto out; + } + + hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace()); + if (SUCCEEDED(hr)) { + hr = WaitForAsync(pAsync); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to gather writer status"); + goto out; + } + + /* Allow unrestricted access to events */ + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); + if (vss_ctx.hEventFrozen == INVALID_HANDLE_VALUE) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_FROZEN); + goto out; + } + vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); + if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_THAW); + goto out; + } + vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); + if (vss_ctx.hEventTimeout == INVALID_HANDLE_VALUE) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_TIMEOUT); + goto out; + } + + /* + * Start VSS quiescing operations. + * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen + * after the applications and filesystems are frozen. + */ + hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot); + if (FAILED(hr)) { + err_set(errset, hr, "failed to do snapshot set"); + goto out; + } + + /* Need to call QueryStatus several times to make VSS provider progress */ + for (i = 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i++) { + HRESULT hr2 = vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL); + if (FAILED(hr2)) { + err_set(errset, hr, "failed to do snapshot set"); + goto out; + } + if (hr != VSS_S_ASYNC_PENDING) { + err_set(errset, E_FAIL, + "DoSnapshotSet exited without Frozen event"); + goto out; + } + wait_status = WaitForSingleObject(vss_ctx.hEventFrozen, + VSS_TIMEOUT_EVENT_MSEC); + if (wait_status != WAIT_TIMEOUT) { + break; + } + } + if (wait_status != WAIT_OBJECT_0) { + err_set(errset, E_FAIL, + "couldn't receive Frozen event from VSS provider"); + goto out; + } + + *num_vols = vss_ctx.cFrozenVols = num_fixed_drives; + return; + +out: + if (vss_ctx.pVssbc) { + vss_ctx.pVssbc->AbortBackup(); + } + requester_cleanup(); + CoUninitialize(); +} + + +void requester_thaw(int *num_vols, ErrorSet *errset) +{ + COMPointer<IVssAsync> pAsync; + + if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { + /* + * In this case, DoSnapshotSet is aborted or not started, + * and no volumes must be frozen. We return without an error. + */ + *num_vols = 0; + return; + } + + /* Tell the provider that the snapshot is finished. */ + SetEvent(vss_ctx.hEventThaw); + + assert(vss_ctx.pVssbc); + assert(vss_ctx.pAsyncSnapshot); + + HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot); + switch (hr) { + case VSS_S_ASYNC_FINISHED: + hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace()); + if (SUCCEEDED(hr)) { + hr = WaitForAsync(pAsync); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to complete backup"); + } + break; + + case (HRESULT)VSS_E_OBJECT_NOT_FOUND: + /* + * On Windows earlier than 2008 SP2 which does not support + * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not + * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. However, as + * the system had been frozen until fsfreeze-thaw command was issued, + * we ignore this error. + */ + vss_ctx.pVssbc->AbortBackup(); + break; + + case VSS_E_UNEXPECTED_PROVIDER_ERROR: + if (WaitForSingleObject(vss_ctx.hEventTimeout, 0) != WAIT_OBJECT_0) { + err_set(errset, hr, "unexpected error in VSS provider"); + break; + } + /* fall through if hEventTimeout is signaled */ + + case (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT: + err_set(errset, hr, "couldn't hold writes: " + "fsfreeze is limited up to 10 seconds"); + break; + + default: + err_set(errset, hr, "failed to do snapshot set"); + } + + if (err_is_set(errset)) { + vss_ctx.pVssbc->AbortBackup(); + } + *num_vols = vss_ctx.cFrozenVols; + requester_cleanup(); + + CoUninitialize(); +} diff --git a/qga/vss-win32/requester.h b/qga/vss-win32/requester.h new file mode 100644 index 0000000000..cffec01791 --- /dev/null +++ b/qga/vss-win32/requester.h @@ -0,0 +1,42 @@ +/* + * QEMU Guest Agent VSS requester declarations + * + * Copyright Hitachi Data Systems Corp. 2013 + * + * Authors: + * Tomoki Sekiyama <tomoki.sekiyama@hds.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef VSS_WIN32_REQUESTER_H +#define VSS_WIN32_REQUESTER_H + +#include "qemu/compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Callback to set Error; used to avoid linking glib to the DLL */ +typedef void (*ErrorSetFunc)(void **errp, int win32_err, int err_class, + const char *fmt, ...) GCC_FMT_ATTR(4, 5); +typedef struct ErrorSet { + ErrorSetFunc error_set; + void **errp; + int err_class; +} ErrorSet; + +STDAPI requester_init(void); +STDAPI requester_deinit(void); + +typedef void (*QGAVSSRequesterFunc)(int *, ErrorSet *); +void requester_freeze(int *num_vols, ErrorSet *errset); +void requester_thaw(int *num_vols, ErrorSet *errset); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/qga/vss-win32/vss-common.h b/qga/vss-win32/vss-common.h new file mode 100644 index 0000000000..ce14e14290 --- /dev/null +++ b/qga/vss-win32/vss-common.h @@ -0,0 +1,129 @@ +/* + * QEMU Guest Agent win32 VSS common declarations + * + * Copyright Hitachi Data Systems Corp. 2013 + * + * Authors: + * Tomoki Sekiyama <tomoki.sekiyama@hds.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef VSS_WIN32_H +#define VSS_WIN32_H + +#define __MIDL_user_allocate_free_DEFINED__ +#include "config-host.h" +#include <windows.h> +#include <shlwapi.h> + +/* Reduce warnings to include vss.h */ + +/* Ignore annotations for MS IDE */ +#define __in IN +#define __out OUT +#define __RPC_unique_pointer +#define __RPC_string +#define __RPC__deref_inout_opt +#define __RPC__out +#ifndef __RPC__out_ecount_part +#define __RPC__out_ecount_part(x, y) +#endif +#define _declspec(x) +#undef uuid +#define uuid(x) + +/* Undef some duplicated error codes redefined in vss.h */ +#undef VSS_E_BAD_STATE +#undef VSS_E_PROVIDER_NOT_REGISTERED +#undef VSS_E_PROVIDER_VETO +#undef VSS_E_OBJECT_NOT_FOUND +#undef VSS_E_VOLUME_NOT_SUPPORTED +#undef VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER +#undef VSS_E_OBJECT_ALREADY_EXISTS +#undef VSS_E_UNEXPECTED_PROVIDER_ERROR +#undef VSS_E_INVALID_XML_DOCUMENT +#undef VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED +#undef VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED + +/* + * VSS headers must be installed from Microsoft VSS SDK 7.2 available at: + * http://www.microsoft.com/en-us/download/details.aspx?id=23490 + */ +#include "inc/win2003/vss.h" + +/* Macros to convert char definitions to wchar */ +#define _L(a) L##a +#define L(a) _L(a) + +/* Constants for QGA VSS Provider */ + +#define QGA_PROVIDER_NAME "QEMU Guest Agent VSS Provider" +#define QGA_PROVIDER_LNAME L(QGA_PROVIDER_NAME) +#define QGA_PROVIDER_VERSION L(QEMU_VERSION) + +#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen" +#define EVENT_NAME_THAW "Global\\QGAVSSEvent-thaw" +#define EVENT_NAME_TIMEOUT "Global\\QGAVSSEvent-timeout" + +const GUID g_gProviderId = { 0x3629d4ed, 0xee09, 0x4e0e, + {0x9a, 0x5c, 0x6d, 0x8b, 0xa2, 0x87, 0x2a, 0xef} }; +const GUID g_gProviderVersion = { 0x11ef8b15, 0xcac6, 0x40d6, + {0x8d, 0x5c, 0x8f, 0xfc, 0x16, 0x3f, 0x24, 0xca} }; + +const CLSID CLSID_QGAVSSProvider = { 0x6e6a3492, 0x8d4d, 0x440c, + {0x96, 0x19, 0x5e, 0x5d, 0x0c, 0xc3, 0x1c, 0xa8} }; + +const TCHAR g_szClsid[] = TEXT("{6E6A3492-8D4D-440C-9619-5E5D0CC31CA8}"); +const TCHAR g_szProgid[] = TEXT("QGAVSSProvider"); + +/* Enums undefined in VSS SDK 7.2 but defined in newer Windows SDK */ +enum __VSS_VOLUME_SNAPSHOT_ATTRIBUTES { + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY = 0x00000002, + VSS_VOLSNAP_ATTR_TXF_RECOVERY = 0x02000000 +}; + + +/* COM pointer utility; call ->Release() when it goes out of scope */ +template <class T> +class COMPointer { + COMPointer(const COMPointer<T> &p) { } /* no copy */ + T *p; +public: + COMPointer &operator=(T *new_p) + { + /* Assignment of a new T* (or NULL) causes release of previous p */ + if (p && p != new_p) { + p->Release(); + } + p = new_p; + return *this; + } + /* Replace by assignment to the pointer of p */ + T **replace(void) + { + *this = NULL; + return &p; + } + /* Make COMPointer be used like T* */ + operator T*() { return p; } + T *operator->(void) { return p; } + T &operator*(void) { return *p; } + operator bool() { return !!p; } + + COMPointer(T *p = NULL) : p(p) { } + ~COMPointer() { *this = NULL; } /* Automatic release */ +}; + +/* + * COM initializer; this should declared before COMPointer to uninitialize COM + * after releasing COM objects. + */ +class COMInitializer { +public: + COMInitializer() { CoInitialize(NULL); } + ~COMInitializer() { CoUninitialize(); } +}; + +#endif diff --git a/qmp-commands.hx b/qmp-commands.hx index 8a8f342eab..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?," @@ -1389,7 +1477,7 @@ EQMP { .name = "block_set_io_throttle", - .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l", + .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?", .mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle, }, @@ -1408,6 +1496,13 @@ Arguments: - "iops": total I/O operations per second (json-int) - "iops_rd": read I/O operations per second (json-int) - "iops_wr": write I/O operations per second (json-int) +- "bps_max": total max in bytes (json-int) +- "bps_rd_max": read max in bytes (json-int) +- "bps_wr_max": write max in bytes (json-int) +- "iops_max": total I/O operations max (json-int) +- "iops_rd_max": read I/O operations max (json-int) +- "iops_wr_max": write I/O operations max (json-int) +- "iops_size": I/O size in bytes when limiting (json-int) Example: @@ -1417,7 +1512,14 @@ Example: "bps_wr": 0, "iops": 0, "iops_rd": 0, - "iops_wr": 0 } } + "iops_wr": 0, + "bps_max": 8000000, + "bps_rd_max": 0, + "bps_wr_max": 0, + "iops_max": 0, + "iops_rd_max": 0, + "iops_wr_max": 0, + "iops_size": 0 } } <- { "return": {} } EQMP @@ -1758,6 +1860,13 @@ Each json-object contain the following: - "iops": limit total I/O operations per second (json-int) - "iops_rd": limit read operations per second (json-int) - "iops_wr": limit write operations per second (json-int) + - "bps_max": total max in bytes (json-int) + - "bps_rd_max": read max in bytes (json-int) + - "bps_wr_max": write max in bytes (json-int) + - "iops_max": total I/O operations max (json-int) + - "iops_rd_max": read I/O operations max (json-int) + - "iops_wr_max": write I/O operations max (json-int) + - "iops_size": I/O size when limiting by iops (json-int) - "image": the detail of the image, it is a json-object containing the following: - "filename": image file name (json-string) @@ -1827,6 +1936,13 @@ Example: "iops":1000000, "iops_rd":0, "iops_wr":0, + "bps_max": 8000000, + "bps_rd_max": 0, + "bps_wr_max": 0, + "iops_max": 0, + "iops_rd_max": 0, + "iops_wr_max": 0, + "iops_size": 0, "image":{ "filename":"disks/test.qcow2", "format":"qcow2", 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); } } diff --git a/roms/Makefile b/roms/Makefile index 7a228aed8f..10d5a65d61 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -1,6 +1,8 @@ -vgabios_variants := stdvga cirrus vmware qxl +vgabios_variants := stdvga cirrus vmware qxl isavga +vgabios_targets := $(subst -isavga,,$(patsubst %,vgabios-%.bin,$(vgabios_variants))) pxerom_variants := e1000 eepro100 ne2k_pci pcnet rtl8139 virtio +pxerom_targets := 8086100e 80861209 10500940 10222000 10ec8139 1af41000 pxe-rom-e1000 efi-rom-e1000 : VID := 8086 pxe-rom-e1000 efi-rom-e1000 : DID := 100e @@ -16,6 +18,22 @@ pxe-rom-virtio efi-rom-virtio : VID := 1af4 pxe-rom-virtio efi-rom-virtio : DID := 1000 # +# cross compiler auto detection +# +path := $(subst :, ,$(PATH)) +system := $(shell uname -s | tr "A-Z" "a-z") + +# first find cross binutils in path +find-cross-ld = $(firstword $(wildcard $(patsubst %,%/$(1)-*$(system)*-ld,$(path)))) +# then check we have cross gcc too +find-cross-gcc = $(firstword $(wildcard $(patsubst %ld,%gcc,$(call find-cross-ld,$(1))))) +# finally strip off path + toolname so we get the prefix +find-cross-prefix = $(subst gcc,,$(notdir $(call find-cross-gcc,$(1)))) + +powerpc64_cross_prefix := $(call find-cross-prefix,powerpc64) +x86_64_cross_prefix := $(call find-cross-prefix,x86_64) + +# # EfiRom utility is shipped with edk2 / tianocore, in BaseTools/ # # We need that to combine multiple images (legacy bios, @@ -32,46 +50,91 @@ default: @echo " bios -- update bios.bin (seabios)" @echo " seavgabios -- update vgabios binaries (seabios)" @echo " lgplvgabios -- update vgabios binaries (lgpl)" + @echo " sgabios -- update sgabios binaries" @echo " pxerom -- update nic roms (bios only)" @echo " efirom -- update nic roms (bios+efi, this needs" @echo " the EfiRom utility from edk2 / tianocore)" + @echo " slof -- update slof.bin" -bios: config.seabios - sh configure-seabios.sh $< - make -C seabios out/bios.bin - cp seabios/out/bios.bin ../pc-bios/bios.bin - cp seabios/out/*dsdt.aml ../pc-bios/ +bios: build-seabios-config-seabios + cp seabios/builds/seabios/bios.bin ../pc-bios/bios.bin + cp seabios/builds/seabios/*dsdt.aml ../pc-bios/ seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants)) -seavgabios-%: config.vga.% - sh configure-seabios.sh $< - make -C seabios out/vgabios.bin - cp seabios/out/vgabios.bin ../pc-bios/vgabios-$*.bin +seavgabios-isavga: build-seabios-config-vga-isavga + cp seabios/builds/vga-isavga/vgabios.bin ../pc-bios/vgabios.bin + +seavgabios-%: build-seabios-config-vga-% + cp seabios/builds/vga-$*/vgabios.bin ../pc-bios/vgabios-$*.bin + +build-seabios-config-%: config.% + mkdir -p seabios/builds/$* + cp $< seabios/builds/$*/.config + $(MAKE) $(MAKEFLAGS) -C seabios \ + KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ + OUT=$(CURDIR)/seabios/builds/$*/ oldnoconfig + $(MAKE) $(MAKEFLAGS) -C seabios \ + KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ + OUT=$(CURDIR)/seabios/builds/$*/ all + lgplvgabios: $(patsubst %,lgplvgabios-%,$(vgabios_variants)) -lgplvgabios-%: - make -C vgabios vgabios-$*.bin +lgplvgabios-isavga: build-lgplvgabios + cp vgabios/VGABIOS-lgpl-latest.bin ../pc-bios/vgabios.bin +lgplvgabios-%: build-lgplvgabios cp vgabios/VGABIOS-lgpl-latest.$*.bin ../pc-bios/vgabios-$*.bin +build-lgplvgabios: + $(MAKE) $(MAKEFLAGS) -C vgabios $(vgabios_targets) + + +.PHONY: sgabios +sgabios: + $(MAKE) $(MAKEFLAGS) -C sgabios + cp sgabios/sgabios.bin ../pc-bios + + pxerom: $(patsubst %,pxe-rom-%,$(pxerom_variants)) -pxe-rom-%: ipxe/src/config/local/general.h - make -C ipxe/src bin/$(VID)$(DID).rom +pxe-rom-%: build-pxe-roms cp ipxe/src/bin/$(VID)$(DID).rom ../pc-bios/pxe-$*.rom efirom: $(patsubst %,efi-rom-%,$(pxerom_variants)) -efi-rom-%: ipxe/src/config/local/general.h - make -C ipxe/src bin/$(VID)$(DID).rom - make -C ipxe/src bin-i386-efi/$(VID)$(DID).efidrv - make -C ipxe/src bin-x86_64-efi/$(VID)$(DID).efidrv +efi-rom-%: build-pxe-roms build-efi-roms $(EFIROM) -f "0x$(VID)" -i "0x$(DID)" -l 0x02 \ -b ipxe/src/bin/$(VID)$(DID).rom \ -ec ipxe/src/bin-i386-efi/$(VID)$(DID).efidrv \ -ec ipxe/src/bin-x86_64-efi/$(VID)$(DID).efidrv \ -o ../pc-bios/efi-$*.rom +build-pxe-roms: ipxe/src/config/local/general.h + $(MAKE) $(MAKEFLAGS) -C ipxe/src GITVERSION="" \ + CROSS_COMPILE=$(x86_64_cross_prefix) \ + $(patsubst %,bin/%.rom,$(pxerom_targets)) + +build-efi-roms: build-pxe-roms ipxe/src/config/local/general.h + $(MAKE) $(MAKEFLAGS) -C ipxe/src GITVERSION="" \ + CROSS_COMPILE=$(x86_64_cross_prefix) \ + $(patsubst %,bin-i386-efi/%.efidrv,$(pxerom_targets)) \ + $(patsubst %,bin-x86_64-efi/%.efidrv,$(pxerom_targets)) + ipxe/src/config/local/%: config.ipxe.% cp $< $@ + + +slof: + $(MAKE) $(MAKEFLAGS) -C SLOF CROSS=$(powerpc64_cross_prefix) qemu + cp SLOF/boot_rom.bin ../pc-bios/slof.bin + + +clean: + rm -rf seabios/.config seabios/out seabios/builds + $(MAKE) $(MAKEFLAGS) -C vgabios clean + rm -f vgabios/VGABIOS-lgpl-latest* + $(MAKE) $(MAKEFLAGS) -C sgabios clean + rm -f sgabios/.depend + $(MAKE) $(MAKEFLAGS) -C ipxe/src veryclean + $(MAKE) $(MAKEFLAGS) -C SLOF clean diff --git a/roms/config.vga.cirrus b/roms/config.vga-cirrus index c8fe58239f..c8fe58239f 100644 --- a/roms/config.vga.cirrus +++ b/roms/config.vga-cirrus diff --git a/roms/config.vga.isavga b/roms/config.vga-isavga index e55e294a0c..e55e294a0c 100644 --- a/roms/config.vga.isavga +++ b/roms/config.vga-isavga diff --git a/roms/config.vga.qxl b/roms/config.vga-qxl index d393f0c34f..d393f0c34f 100644 --- a/roms/config.vga.qxl +++ b/roms/config.vga-qxl diff --git a/roms/config.vga.stdvga b/roms/config.vga-stdvga index 7d063b787c..7d063b787c 100644 --- a/roms/config.vga.stdvga +++ b/roms/config.vga-stdvga diff --git a/roms/config.vga.vmware b/roms/config.vga-vmware index eb10427afd..eb10427afd 100644 --- a/roms/config.vga.vmware +++ b/roms/config.vga-vmware diff --git a/roms/openbios b/roms/openbios -Subproject 0f3d51ef22ec9166beb3ed434d253029ed7cfe8 +Subproject d363cf50c50c268da7e6d0bf707adde1893d1ab diff --git a/roms/seabios b/roms/seabios -Subproject d4f7d90f47462b4e8836899adc5060fbde5253e +Subproject ece025f5980bae88fa677bc9c0d24d2e580e205 @@ -8,9 +8,13 @@ MAKEFLAGS += -rR %.d: %.h: %.c: +%.cpp: %.m: %.mak: +# Flags for C++ compilation +QEMU_CXXFLAGS = -D__STDC_LIMIT_MACROS $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls, $(QEMU_CFLAGS)) + # Flags for dependency generation QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d @@ -50,6 +54,9 @@ endif %.o: %.asm $(call quiet-command,$(AS) $(ASFLAGS) -o $@ $<," AS $(TARGET_DIR)$@") +%.o: %.cpp + $(call quiet-command,$(CXX) $(QEMU_INCLUDES) $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CXX $(TARGET_DIR)$@") + %.o: %.m $(call quiet-command,$(OBJCC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@") @@ -70,7 +77,7 @@ quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1)) cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \ >/dev/null 2>&1 && echo OK), $2, $3) -VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.texi %.sh %.rc +VPATH_SUFFIXES = %.c %.h %.S %.cpp %.m %.mak %.texi %.sh %.rc set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1))) # find-in-path @@ -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/scripts/checkpatch.pl b/scripts/checkpatch.pl index ec0aa4cd93..9d46e5a104 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1363,7 +1363,7 @@ sub process { # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; - if ($realfile =~ /(Makefile|Kconfig|\.c|\.h|\.S|\.tmpl)$/) { + if ($realfile =~ /(Makefile|Kconfig|\.c|\.cpp|\.h|\.S|\.tmpl)$/) { ERROR("do not set execute permissions for source files\n" . $permhere); } } @@ -1460,7 +1460,7 @@ sub process { } # check we are in a valid source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); + next if ($realfile !~ /\.(h|c|cpp|s|S|pl|sh)$/); #80 column limit if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && @@ -1495,7 +1495,7 @@ sub process { } # check we are in a valid source file C or perl if not then ignore this hunk - next if ($realfile !~ /\.(h|c|pl)$/); + next if ($realfile !~ /\.(h|c|cpp|pl)$/); # in QEMU, no tabs are allowed if ($rawline =~ /^\+.*\t/) { @@ -1505,7 +1505,7 @@ sub process { } # check we are in a valid C source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c)$/); + next if ($realfile !~ /\.(h|c|cpp)$/); # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { @@ -1969,6 +1969,9 @@ sub process { asm|__asm__)$/x) { + # Ignore 'catch (...)' in C++ + } elsif ($name =~ /^catch$/ && $realfile =~ /(\.cpp|\.h)$/) { + # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. @@ -1992,7 +1995,7 @@ sub process { \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| - \?|: + \?|::|: }x; my @elements = split(/($ops|;)/, $opline); my $off = 0; @@ -2062,6 +2065,10 @@ sub process { # // is a comment } elsif ($op eq '//') { + # Ignore : used in class declaration in C++ + } elsif ($opv eq ':B' && $ctx =~ /Wx[WE]/ && + $line =~ /class/ && $realfile =~ /(\.cpp|\.h)$/) { + # No spaces for: # -> # : when part of a bitfield @@ -2088,7 +2095,10 @@ sub process { } elsif ($op eq '!' || $op eq '~' || $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { - if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + if ($op eq '~' && $ca =~ /::$/ && $realfile =~ /(\.cpp|\.h)$/) { + # '~' used as a name of Destructor + + } elsif ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { ERROR("space required before that '$op' $at\n" . $hereptr); } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { @@ -2135,6 +2145,18 @@ sub process { } elsif ($ctx !~ /[EWC]x[CWE]/) { my $ok = 0; + if ($realfile =~ /\.cpp|\.h$/) { + # Ignore template arguments <...> in C++ + if (($op eq '<' || $op eq '>') && $line =~ /<.*>/) { + $ok = 1; + } + + # Ignore :: in C++ + if ($op eq '::') { + $ok = 1; + } + } + # Ignore email addresses <foo@bar> if (($op eq '<' && $cc =~ /^\S+\@\S+>/) || diff --git a/scripts/extract-vsssdk-headers b/scripts/extract-vsssdk-headers new file mode 100755 index 0000000000..9e38510f04 --- /dev/null +++ b/scripts/extract-vsssdk-headers @@ -0,0 +1,35 @@ +#! /bin/bash + +# extract-vsssdk-headers +# Author: Paolo Bonzini <pbonzini@redhat.com> + +set -e +if test $# != 1 || ! test -f "$1"; then + echo 'Usage: extract-vsssdk-headers /path/to/setup.exe' >&2 + exit 1 +fi + +if ! command -v msiextract > /dev/null; then + echo 'msiextract not found. Please install msitools.' >&2 + exit 1 +fi + +if test -e inc; then + echo '"inc" already exists.' >&2 + exit 1 +fi + +# Extract .MSI file in the .exe, looking for the OLE compound +# document signature. Extra data at the end does not matter. +export LC_ALL=C +MAGIC=$'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1' +offset=$(grep -abom1 "$MAGIC" "$1" | sed -n 's/:/\n/; P') +tmpdir=$(mktemp -d) +trap 'rm -fr -- "$tmpdir" vsssdk.msi' EXIT HUP INT QUIT ALRM TERM +tail -c +$(($offset+1)) -- "$1" > vsssdk.msi + +# Now extract the files. +msiextract -C $tmpdir vsssdk.msi +mv "$tmpdir/Program Files/Microsoft/VSSSDK72/inc" inc +echo 'Extracted SDK headers into "inc" directory.' +exit 0 diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 86de9800ea..5222463893 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -51,7 +51,10 @@ def generate_fwd_enum_struct(name, members): return mcgen(''' typedef struct %(name)sList { - %(name)s value; + union { + %(name)s value; + uint64_t padding; + }; struct %(name)sList *next; } %(name)sList; ''', diff --git a/scripts/qapi.py b/scripts/qapi.py index 1069310f8d..750e9fb552 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -236,9 +236,19 @@ def c_var(name, protect=True): # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html # excluding _.* gcc_words = set(['asm', 'typeof']) + # C++ ISO/IEC 14882:2003 2.11 + cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', + 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', + 'namespace', 'new', 'operator', 'private', 'protected', + 'public', 'reinterpret_cast', 'static_cast', 'template', + 'this', 'throw', 'true', 'try', 'typeid', 'typename', + 'using', 'virtual', 'wchar_t', + # alternative representations + 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', + 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) # namespace pollution: polluted_words = set(['unix']) - if protect and (name in c89_words | c99_words | c11_words | gcc_words | polluted_words): + if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words): return "q_" + name return name.replace('-', '_').lstrip("*") diff --git a/QMP/qemu-ga-client b/scripts/qmp/qemu-ga-client index 46676c3750..b5f7e7c5ff 100755 --- a/QMP/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -267,7 +267,9 @@ def main(address, cmd, args): print('Hint: qemu is not running?') sys.exit(1) - if cmd != 'ping': + if cmd == 'fsfreeze' and args[0] == 'freeze': + client.sync(60) + elif cmd != 'ping': client.sync() globals()['_cmd_' + cmd](client, args) 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/scripts/refresh-pxe-roms.sh b/scripts/refresh-pxe-roms.sh index 14d586070f..90fc0b374d 100755 --- a/scripts/refresh-pxe-roms.sh +++ b/scripts/refresh-pxe-roms.sh @@ -21,79 +21,11 @@ # Usage: Run from root of qemu tree # ./scripts/refresh-pxe-roms.sh -QEMU_DIR=$PWD -ROM_DIR="pc-bios" -BUILD_DIR="roms/ipxe" -LOCAL_CONFIG="src/config/local/general.h" - -function cleanup () -{ - if [ -n "$SAVED_CONFIG" ]; then - cp "$SAVED_CONFIG" "$BUILD_DIR"/"$LOCAL_CONFIG" - rm "$SAVED_CONFIG" - fi - cd "$QEMU_DIR" -} - -function make_rom () -{ - cd "$BUILD_DIR"/src - - BUILD_LOG=$(mktemp) - - echo Building "$2"... - make bin/"$1".rom > "$BUILD_LOG" 2>&1 - if [ $? -ne 0 ]; then - echo Build failed - tail --lines=100 "$BUILD_LOG" - rm "$BUILD_LOG" - cleanup - exit 1 - fi - rm "$BUILD_LOG" - - cp bin/"$1".rom "$QEMU_DIR"/"$ROM_DIR"/"$2" - - cd "$QEMU_DIR" -} - -if [ ! -d "$QEMU_DIR"/"$ROM_DIR" ]; then - echo "error: can't find $ROM_DIR directory," \ - "run me from the root of the qemu tree" - exit 1 -fi - -if [ ! -d "$BUILD_DIR"/src ]; then - echo "error: $BUILD_DIR not populated, try:" - echo " git submodule init $BUILD_DIR" - echo " git submodule update $BUILD_DIR" - exit 1 -fi - -if [ -e "$BUILD_DIR"/"$LOCAL_CONFIG" ]; then - SAVED_CONFIG=$(mktemp) - cp "$BUILD_DIR"/"$LOCAL_CONFIG" "$SAVED_CONFIG" -fi - -echo "#undef BANNER_TIMEOUT" > "$BUILD_DIR"/"$LOCAL_CONFIG" -echo "#define BANNER_TIMEOUT 0" >> "$BUILD_DIR"/"$LOCAL_CONFIG" - -IPXE_VERSION=$(cd "$BUILD_DIR" && git describe --tags) -if [ -z "$IPXE_VERSION" ]; then - echo "error: unable to retrieve git version" - cleanup - exit 1 +targets="pxerom" +if test -x "$(which EfiRom 2>/dev/null)"; then + targets="$targets efirom" fi -echo "#undef PRODUCT_NAME" >> "$BUILD_DIR"/"$LOCAL_CONFIG" -echo "#define PRODUCT_NAME \"iPXE $IPXE_VERSION\"" >> "$BUILD_DIR"/"$LOCAL_CONFIG" - -make_rom 8086100e pxe-e1000.rom -make_rom 80861209 pxe-eepro100.rom -make_rom 10500940 pxe-ne2k_pci.rom -make_rom 10222000 pxe-pcnet.rom -make_rom 10ec8139 pxe-rtl8139.rom -make_rom 1af41000 pxe-virtio.rom - -echo done -cleanup +cd roms +make -j4 $targets || exit 1 +make clean 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/misc.c b/slirp/misc.c index c0d489950a..6c1636f7b6 100644 --- a/slirp/misc.c +++ b/slirp/misc.c @@ -212,8 +212,7 @@ fork_exec(struct socket *so, const char *ex, int do_pty) so->s = accept(s, (struct sockaddr *)&addr, &addrlen); } while (so->s < 0 && errno == EINTR); closesocket(s); - opt = 1; - qemu_setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)); + socket_set_fast_reuse(so->s); opt = 1; qemu_setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); qemu_set_nonblock(so->s); 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/slirp/socket.c b/slirp/socket.c index 25d60e7a89..37ac5cf2fb 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -627,9 +627,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, addr.sin_port = hport; if (((s = qemu_socket(AF_INET,SOCK_STREAM,0)) < 0) || -#ifndef _WIN32 - (qemu_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0) || -#endif + (socket_set_fast_reuse(s) < 0) || (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) || (listen(s,1) < 0)) { int tmperrno = errno; /* Don't clobber the real reason we failed */ diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index 043f28fcae..7571c5a282 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -337,8 +337,7 @@ int tcp_fconnect(struct socket *so) struct sockaddr_in addr; qemu_set_nonblock(s); - opt = 1; - qemu_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + socket_set_fast_reuse(s); opt = 1; qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); @@ -426,8 +425,7 @@ void tcp_connect(struct socket *inso) return; } qemu_set_nonblock(s); - opt = 1; - qemu_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)); + socket_set_fast_reuse(s); opt = 1; qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); socket_set_nodelay(s); diff --git a/slirp/udp.c b/slirp/udp.c index b105f871f3..8cc6cb66da 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -354,7 +354,7 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, { struct sockaddr_in addr; struct socket *so; - socklen_t addrlen = sizeof(struct sockaddr_in), opt = 1; + socklen_t addrlen = sizeof(struct sockaddr_in); so = socreate(slirp); if (!so) { @@ -372,7 +372,7 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, udp_detach(so); return NULL; } - qemu_setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)); + socket_set_fast_reuse(so->s); getsockname(so->s,(struct sockaddr *)&addr,&addrlen); so->so_fport = addr.sin_port; 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-arm/Makefile.objs b/target-arm/Makefile.objs index 2d9f77fa9b..6453f5c011 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -5,3 +5,4 @@ obj-$(CONFIG_NO_KVM) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o obj-y += gdbstub.o +obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 9f47baebf8..b55306a3c3 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -130,6 +130,18 @@ typedef struct ARMCPU { uint32_t reset_auxcr; } ARMCPU; +#define TYPE_AARCH64_CPU "aarch64-cpu" +#define AARCH64_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(AArch64CPUClass, (klass), TYPE_AARCH64_CPU) +#define AARCH64_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AArch64CPUClass, (obj), TYPE_AArch64_CPU) + +typedef struct AArch64CPUClass { + /*< private >*/ + ARMCPUClass parent_class; + /*< public >*/ +} AArch64CPUClass; + static inline ARMCPU *arm_env_get_cpu(CPUARMState *env) { return container_of(env, ARMCPU, env); @@ -161,4 +173,11 @@ int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void arm_gt_ptimer_cb(void *opaque); void arm_gt_vtimer_cb(void *opaque); +#ifdef TARGET_AARCH64 +void aarch64_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags); +int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +#endif + #endif diff --git a/target-arm/cpu.c b/target-arm/cpu.c index b2556c66b4..d40f2a7a4f 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -84,6 +84,11 @@ static void arm_cpu_reset(CPUState *s) env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; } + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + /* 64 bit CPUs always start in 64 bit mode */ + env->aarch64 = 1; + } + #if defined(CONFIG_USER_ONLY) env->uncached_cpsr = ARM_CPU_MODE_USR; /* For user mode we must enable access to coprocessors */ @@ -108,7 +113,7 @@ static void arm_cpu_reset(CPUState *s) modified flash and reset itself. However images loaded via -kernel have not been copied yet, so load the values directly from there. */ - env->regs[13] = ldl_p(rom); + env->regs[13] = ldl_p(rom) & 0xFFFFFFFC; pc = ldl_p(rom + 4); env->thumb = pc & 1; env->regs[15] = pc & ~1; @@ -288,8 +293,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) acc->parent_realize(dev, errp); } -/* CPU models */ - static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; @@ -309,6 +312,9 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) return oc; } +/* CPU models. These are not needed for the AArch64 linux-user build. */ +#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) + static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -822,6 +828,7 @@ static void pxa270c5_initfn(Object *obj) cpu->reset_sctlr = 0x00000078; } +#ifdef CONFIG_USER_ONLY static void arm_any_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -832,8 +839,14 @@ static void arm_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_V7MP); +#ifdef TARGET_AARCH64 + set_feature(&cpu->env, ARM_FEATURE_AARCH64); +#endif cpu->midr = 0xffffffff; } +#endif + +#endif /* !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) */ typedef struct ARMCPUInfo { const char *name; @@ -842,6 +855,7 @@ typedef struct ARMCPUInfo { } ARMCPUInfo; static const ARMCPUInfo arm_cpus[] = { +#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) { .name = "arm926", .initfn = arm926_initfn }, { .name = "arm946", .initfn = arm946_initfn }, { .name = "arm1026", .initfn = arm1026_initfn }, @@ -874,7 +888,10 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "pxa270-b1", .initfn = pxa270b1_initfn }, { .name = "pxa270-c0", .initfn = pxa270c0_initfn }, { .name = "pxa270-c5", .initfn = pxa270c5_initfn }, +#ifdef CONFIG_USER_ONLY { .name = "any", .initfn = arm_any_initfn }, +#endif +#endif }; static void arm_cpu_class_init(ObjectClass *oc, void *data) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index f2abdf37ce..2c56740bf6 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -19,13 +19,19 @@ #ifndef CPU_ARM_H #define CPU_ARM_H -#define TARGET_LONG_BITS 32 +#include "config.h" -#define ELF_MACHINE EM_ARM +#if defined(TARGET_AARCH64) + /* AArch64 definitions */ +# define TARGET_LONG_BITS 64 +# define ELF_MACHINE EM_AARCH64 +#else +# define TARGET_LONG_BITS 32 +# define ELF_MACHINE EM_ARM +#endif #define CPUArchState struct CPUARMState -#include "config.h" #include "qemu-common.h" #include "exec/cpu-defs.h" @@ -97,6 +103,20 @@ typedef struct ARMGenericTimer { typedef struct CPUARMState { /* Regs for current mode. */ uint32_t regs[16]; + + /* 32/64 switch only happens when taking and returning from + * exceptions so the overlap semantics are taken care of then + * instead of having a complicated union. + */ + /* Regs for A64 mode. */ + uint64_t xregs[32]; + uint64_t pc; + /* TODO: pstate doesn't correspond to an architectural register; + * it would be better modelled as the underlying fields. + */ + uint32_t pstate; + uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */ + /* Frequently accessed CPSR bits are stored separately for efficiency. This contains all the other bits. Use cpsr_{read,write} to access the whole CPSR. */ @@ -175,6 +195,11 @@ typedef struct CPUARMState { uint32_t c15_power_control; /* power control */ } cp15; + /* System registers (AArch64) */ + struct { + uint64_t tpidr_el0; + } sr; + struct { uint32_t other_sp; uint32_t vecbase; @@ -191,7 +216,22 @@ typedef struct CPUARMState { /* VFP coprocessor state. */ struct { - float64 regs[32]; + /* VFP/Neon register state. Note that the mapping between S, D and Q + * views of the register bank differs between AArch64 and AArch32: + * In AArch32: + * Qn = regs[2n+1]:regs[2n] + * Dn = regs[n] + * Sn = regs[n/2] bits 31..0 for even n, and bits 63..32 for odd n + * (and regs[32] to regs[63] are inaccessible) + * In AArch64: + * Qn = regs[2n+1]:regs[2n] + * Dn = regs[2n] + * Sn = regs[2n] bits 31..0 + * This corresponds to the architecturally defined mapping between + * the two execution states, and means we do not need to explicitly + * map these registers when changing states. + */ + float64 regs[64]; uint32_t xregs[16]; /* We store these fpcsr fields separately for convenience. */ @@ -261,6 +301,20 @@ int bank_number(int mode); void switch_mode(CPUARMState *, int); uint32_t do_arm_semihosting(CPUARMState *env); +static inline bool is_a64(CPUARMState *env) +{ + return env->aarch64; +} + +#define PSTATE_N_SHIFT 3 +#define PSTATE_N (1 << PSTATE_N_SHIFT) +#define PSTATE_Z_SHIFT 2 +#define PSTATE_Z (1 << PSTATE_Z_SHIFT) +#define PSTATE_C_SHIFT 1 +#define PSTATE_C (1 << PSTATE_C_SHIFT) +#define PSTATE_V_SHIFT 0 +#define PSTATE_V (1 << PSTATE_V_SHIFT) + /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero is returned if the signal was handled by the virtual CPU. */ @@ -270,22 +324,22 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw, int mmu_idx); #define cpu_handle_mmu_fault cpu_arm_handle_mmu_fault -#define CPSR_M (0x1f) -#define CPSR_T (1 << 5) -#define CPSR_F (1 << 6) -#define CPSR_I (1 << 7) -#define CPSR_A (1 << 8) -#define CPSR_E (1 << 9) -#define CPSR_IT_2_7 (0xfc00) -#define CPSR_GE (0xf << 16) -#define CPSR_RESERVED (0xf << 20) -#define CPSR_J (1 << 24) -#define CPSR_IT_0_1 (3 << 25) -#define CPSR_Q (1 << 27) -#define CPSR_V (1 << 28) -#define CPSR_C (1 << 29) -#define CPSR_Z (1 << 30) -#define CPSR_N (1 << 31) +#define CPSR_M (0x1fU) +#define CPSR_T (1U << 5) +#define CPSR_F (1U << 6) +#define CPSR_I (1U << 7) +#define CPSR_A (1U << 8) +#define CPSR_E (1U << 9) +#define CPSR_IT_2_7 (0xfc00U) +#define CPSR_GE (0xfU << 16) +#define CPSR_RESERVED (0xfU << 20) +#define CPSR_J (1U << 24) +#define CPSR_IT_0_1 (3U << 25) +#define CPSR_Q (1U << 27) +#define CPSR_V (1U << 28) +#define CPSR_C (1U << 29) +#define CPSR_Z (1U << 30) +#define CPSR_N (1U << 31) #define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V) #define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7) @@ -409,6 +463,7 @@ enum arm_features { ARM_FEATURE_PXN, /* has Privileged Execute Never bit */ ARM_FEATURE_LPAE, /* has Large Physical Address Extension */ ARM_FEATURE_V8, + ARM_FEATURE_AARCH64, /* supports 64 bit mode */ }; static inline int arm_feature(CPUARMState *env, int feature) @@ -729,8 +784,13 @@ bool write_cpustate_to_list(ARMCPU *cpu); #define TARGET_PAGE_BITS 10 #endif -#define TARGET_PHYS_ADDR_SPACE_BITS 40 -#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#if defined(TARGET_AARCH64) +# define TARGET_PHYS_ADDR_SPACE_BITS 48 +# define TARGET_VIRT_ADDR_SPACE_BITS 64 +#else +# define TARGET_PHYS_ADDR_SPACE_BITS 40 +# define TARGET_VIRT_ADDR_SPACE_BITS 32 +#endif static inline CPUARMState *cpu_init(const char *cpu_model) { @@ -757,7 +817,13 @@ static inline int cpu_mmu_index (CPUARMState *env) #include "exec/cpu-all.h" -/* Bit usage in the TB flags field: */ +/* Bit usage in the TB flags field: bit 31 indicates whether we are + * in 32 or 64 bit mode. The meaning of the other bits depends on that. + */ +#define ARM_TBFLAG_AARCH64_STATE_SHIFT 31 +#define ARM_TBFLAG_AARCH64_STATE_MASK (1U << ARM_TBFLAG_AARCH64_STATE_SHIFT) + +/* Bit usage when in AArch32 state: */ #define ARM_TBFLAG_THUMB_SHIFT 0 #define ARM_TBFLAG_THUMB_MASK (1 << ARM_TBFLAG_THUMB_SHIFT) #define ARM_TBFLAG_VECLEN_SHIFT 1 @@ -772,9 +838,12 @@ static inline int cpu_mmu_index (CPUARMState *env) #define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) #define ARM_TBFLAG_BSWAP_CODE_SHIFT 16 #define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT) -/* Bits 31..17 are currently unused. */ + +/* Bit usage when in AArch64 state: currently no bits defined */ /* some convenience accessor macros */ +#define ARM_TBFLAG_AARCH64_STATE(F) \ + (((F) & ARM_TBFLAG_AARCH64_STATE_MASK) >> ARM_TBFLAG_AARCH64_STATE_SHIFT) #define ARM_TBFLAG_THUMB(F) \ (((F) & ARM_TBFLAG_THUMB_MASK) >> ARM_TBFLAG_THUMB_SHIFT) #define ARM_TBFLAG_VECLEN(F) \ @@ -793,25 +862,31 @@ static inline int cpu_mmu_index (CPUARMState *env) static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { - int privmode; - *pc = env->regs[15]; - *cs_base = 0; - *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) - | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) - | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) - | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) - | (env->bswap_code << ARM_TBFLAG_BSWAP_CODE_SHIFT); - if (arm_feature(env, ARM_FEATURE_M)) { - privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1)); + if (is_a64(env)) { + *pc = env->pc; + *flags = ARM_TBFLAG_AARCH64_STATE_MASK; } else { - privmode = (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR; - } - if (privmode) { - *flags |= ARM_TBFLAG_PRIV_MASK; - } - if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { - *flags |= ARM_TBFLAG_VFPEN_MASK; + int privmode; + *pc = env->regs[15]; + *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) + | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) + | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) + | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) + | (env->bswap_code << ARM_TBFLAG_BSWAP_CODE_SHIFT); + if (arm_feature(env, ARM_FEATURE_M)) { + privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1)); + } else { + privmode = (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR; + } + if (privmode) { + *flags |= ARM_TBFLAG_PRIV_MASK; + } + if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { + *flags |= ARM_TBFLAG_VFPEN_MASK; + } } + + *cs_base = 0; } static inline bool cpu_has_work(CPUState *cpu) @@ -822,8 +897,17 @@ static inline bool cpu_has_work(CPUState *cpu) #include "exec/exec-all.h" +static inline void cpu_pc_from_tb(CPUARMState *env, TranslationBlock *tb) +{ + if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { + env->pc = tb->pc; + } else { + env->regs[15] = tb->pc; + } +} + /* Load an instruction and return it in the standard little-endian order */ -static inline uint32_t arm_ldl_code(CPUARMState *env, uint32_t addr, +static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr, bool do_swap) { uint32_t insn = cpu_ldl_code(env, addr); @@ -834,7 +918,7 @@ static inline uint32_t arm_ldl_code(CPUARMState *env, uint32_t addr, } /* Ditto, for a halfword (Thumb) instruction */ -static inline uint16_t arm_lduw_code(CPUARMState *env, uint32_t addr, +static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr, bool do_swap) { uint16_t insn = cpu_lduw_code(env, addr); diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c new file mode 100644 index 0000000000..3e99c2140a --- /dev/null +++ b/target-arm/cpu64.c @@ -0,0 +1,118 @@ +/* + * QEMU AArch64 CPU + * + * Copyright (c) 2013 Linaro Ltd + * + * 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/gpl-2.0.html> + */ + +#include "cpu.h" +#include "qemu-common.h" +#if !defined(CONFIG_USER_ONLY) +#include "hw/loader.h" +#endif +#include "hw/arm/arm.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" + +static inline void set_feature(CPUARMState *env, int feature) +{ + env->features |= 1ULL << feature; +} + +#ifdef CONFIG_USER_ONLY +static void aarch64_any_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_VFP4); + set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); + set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); + set_feature(&cpu->env, ARM_FEATURE_V7MP); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); +} +#endif + +typedef struct ARMCPUInfo { + const char *name; + void (*initfn)(Object *obj); + void (*class_init)(ObjectClass *oc, void *data); +} ARMCPUInfo; + +static const ARMCPUInfo aarch64_cpus[] = { +#ifdef CONFIG_USER_ONLY + { .name = "any", .initfn = aarch64_any_initfn }, +#endif +}; + +static void aarch64_cpu_initfn(Object *obj) +{ +} + +static void aarch64_cpu_finalizefn(Object *obj) +{ +} + +static void aarch64_cpu_class_init(ObjectClass *oc, void *data) +{ + CPUClass *cc = CPU_CLASS(oc); + + cc->dump_state = aarch64_cpu_dump_state; + cc->gdb_read_register = aarch64_cpu_gdb_read_register; + cc->gdb_write_register = aarch64_cpu_gdb_write_register; + cc->gdb_num_core_regs = 34; + cc->gdb_core_xml_file = "aarch64-core.xml"; +} + +static void aarch64_cpu_register(const ARMCPUInfo *info) +{ + TypeInfo type_info = { + .parent = TYPE_AARCH64_CPU, + .instance_size = sizeof(ARMCPU), + .instance_init = info->initfn, + .class_size = sizeof(ARMCPUClass), + .class_init = info->class_init, + }; + + type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); +} + +static const TypeInfo aarch64_cpu_type_info = { + .name = TYPE_AARCH64_CPU, + .parent = TYPE_ARM_CPU, + .instance_size = sizeof(ARMCPU), + .instance_init = aarch64_cpu_initfn, + .instance_finalize = aarch64_cpu_finalizefn, + .abstract = true, + .class_size = sizeof(AArch64CPUClass), + .class_init = aarch64_cpu_class_init, +}; + +static void aarch64_cpu_register_types(void) +{ + int i; + + type_register_static(&aarch64_cpu_type_info); + for (i = 0; i < ARRAY_SIZE(aarch64_cpus); i++) { + aarch64_cpu_register(&aarch64_cpus[i]); + } +} + +type_init(aarch64_cpu_register_types) diff --git a/target-arm/gdbstub64.c b/target-arm/gdbstub64.c new file mode 100644 index 0000000000..7cb6a7c0e0 --- /dev/null +++ b/target-arm/gdbstub64.c @@ -0,0 +1,73 @@ +/* + * ARM gdb server stub: AArch64 specific functions. + * + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + if (n < 31) { + /* Core integer register. */ + return gdb_get_reg64(mem_buf, env->xregs[n]); + } + switch (n) { + case 31: + return gdb_get_reg64(mem_buf, env->xregs[31]); + break; + case 32: + return gdb_get_reg64(mem_buf, env->pc); + break; + case 33: + return gdb_get_reg32(mem_buf, env->pstate); + } + /* Unknown register. */ + return 0; +} + +int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint64_t tmp; + + tmp = ldq_p(mem_buf); + + if (n < 31) { + /* Core integer register. */ + env->xregs[n] = tmp; + return 8; + } + switch (n) { + case 31: + env->xregs[31] = tmp; + return 8; + case 32: + env->pc = tmp; + return 8; + case 33: + /* CPSR */ + env->pstate = tmp; + return 4; + } + /* Unknown register. */ + return 0; +} diff --git a/target-arm/helper.c b/target-arm/helper.c index e51ef20aea..2a98be7436 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2,6 +2,7 @@ #include "exec/gdbstub.h" #include "helper.h" #include "qemu/host-utils.h" +#include "sysemu/arch_init.h" #include "sysemu/sysemu.h" #include "qemu/bitops.h" @@ -972,7 +973,7 @@ static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) static inline bool extended_addresses_enabled(CPUARMState *env) { return arm_feature(env, ARM_FEATURE_LPAE) - && (env->cp15.c2_control & (1 << 31)); + && (env->cp15.c2_control & (1U << 31)); } static int ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -1385,7 +1386,7 @@ static int mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri, * so these bits always RAZ. */ if (arm_feature(env, ARM_FEATURE_V7MP)) { - mpidr |= (1 << 31); + mpidr |= (1U << 31); /* Cores which are uniprocessor (non-coherent) * but still implement the MP extensions set * bit 30. (For instance, A9UP.) However we do @@ -1829,6 +1830,37 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) g_slist_free(list); } +static void arm_cpu_add_definition(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **cpu_list = user_data; + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + const char *typename; + + typename = object_class_get_name(oc); + info = g_malloc0(sizeof(*info)); + info->name = g_strndup(typename, + strlen(typename) - strlen("-" TYPE_ARM_CPU)); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = *cpu_list; + *cpu_list = entry; +} + +CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + GSList *list; + + list = object_class_get_list(TYPE_ARM_CPU, false); + g_slist_foreach(list, arm_cpu_add_definition, &cpu_list); + g_slist_free(list); + + return cpu_list; +} + void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, const ARMCPRegInfo *r, void *opaque) { diff --git a/target-arm/iwmmxt_helper.c b/target-arm/iwmmxt_helper.c index 7953b53f7e..e6cfa62da8 100644 --- a/target-arm/iwmmxt_helper.c +++ b/target-arm/iwmmxt_helper.c @@ -577,7 +577,7 @@ uint64_t HELPER(iwmmxt_rorl)(CPUARMState *env, uint64_t x, uint32_t n) uint64_t HELPER(iwmmxt_rorq)(CPUARMState *env, uint64_t x, uint32_t n) { - x = (x >> n) | (x << (64 - n)); + x = ror64(x, n); env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); return x; } diff --git a/target-arm/machine.c b/target-arm/machine.c index 5b6f3754ca..74f010f637 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -37,11 +37,11 @@ static const VMStateInfo vmstate_fpscr = { static const VMStateDescription vmstate_vfp = { .name = "cpu/vfp", - .version_id = 2, - .minimum_version_id = 2, - .minimum_version_id_old = 2, + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, .fields = (VMStateField[]) { - VMSTATE_FLOAT64_ARRAY(env.vfp.regs, ARMCPU, 32), + VMSTATE_FLOAT64_ARRAY(env.vfp.regs, ARMCPU, 64), /* The xregs array is a little awkward because element 1 (FPSCR) * requires a specific accessor, so we have to split it up in * the vmstate: diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c new file mode 100644 index 0000000000..f120088607 --- /dev/null +++ b/target-arm/translate-a64.c @@ -0,0 +1,139 @@ +/* + * AArch64 translation + * + * Copyright (c) 2013 Alexander Graf <agraf@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include "cpu.h" +#include "tcg-op.h" +#include "qemu/log.h" +#include "translate.h" +#include "qemu/host-utils.h" + +#include "helper.h" +#define GEN_HELPER 1 +#include "helper.h" + +static TCGv_i64 cpu_X[32]; +static TCGv_i64 cpu_pc; +static TCGv_i32 pstate; + +static const char *regnames[] = { + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", + "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", + "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp" +}; + +/* initialize TCG globals. */ +void a64_translate_init(void) +{ + int i; + + cpu_pc = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUARMState, pc), + "pc"); + for (i = 0; i < 32; i++) { + cpu_X[i] = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUARMState, xregs[i]), + regnames[i]); + } + + pstate = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUARMState, pstate), + "pstate"); +} + +void aarch64_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + int i; + + cpu_fprintf(f, "PC=%016"PRIx64" SP=%016"PRIx64"\n", + env->pc, env->xregs[31]); + for (i = 0; i < 31; i++) { + cpu_fprintf(f, "X%02d=%016"PRIx64, i, env->xregs[i]); + if ((i % 4) == 3) { + cpu_fprintf(f, "\n"); + } else { + cpu_fprintf(f, " "); + } + } + cpu_fprintf(f, "PSTATE=%c%c%c%c\n", + env->pstate & PSTATE_N ? 'n' : '.', + env->pstate & PSTATE_Z ? 'z' : '.', + env->pstate & PSTATE_C ? 'c' : '.', + env->pstate & PSTATE_V ? 'v' : '.'); + cpu_fprintf(f, "\n"); +} + +void gen_a64_set_pc_im(uint64_t val) +{ + tcg_gen_movi_i64(cpu_pc, val); +} + +static void gen_exception(int excp) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_movi_i32(tmp, excp); + gen_helper_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); +} + +static void gen_exception_insn(DisasContext *s, int offset, int excp) +{ + gen_a64_set_pc_im(s->pc - offset); + gen_exception(excp); + s->is_jmp = DISAS_JUMP; +} + +static void real_unallocated_encoding(DisasContext *s) +{ + fprintf(stderr, "Unknown instruction: %#x\n", s->insn); + gen_exception_insn(s, 4, EXCP_UDEF); +} + +#define unallocated_encoding(s) do { \ + fprintf(stderr, "unallocated encoding at line: %d\n", __LINE__); \ + real_unallocated_encoding(s); \ + } while (0) + +void disas_a64_insn(CPUARMState *env, DisasContext *s) +{ + uint32_t insn; + + insn = arm_ldl_code(env, s->pc, s->bswap_code); + s->insn = insn; + s->pc += 4; + + switch ((insn >> 24) & 0x1f) { + default: + unallocated_encoding(s); + break; + } + + if (unlikely(s->singlestep_enabled) && (s->is_jmp == DISAS_TB_JUMP)) { + /* go through the main loop for single step */ + s->is_jmp = DISAS_JUMP; + } +} diff --git a/target-arm/translate.c b/target-arm/translate.c index 4f4a0a97d2..998bde268d 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -28,6 +28,7 @@ #include "disas/disas.h" #include "tcg-op.h" #include "qemu/log.h" +#include "qemu/bitops.h" #include "helper.h" #define GEN_HELPER 1 @@ -46,29 +47,7 @@ #define ARCH(x) do { if (!ENABLE_ARCH_##x) goto illegal_op; } while(0) -/* internal defines */ -typedef struct DisasContext { - target_ulong pc; - int is_jmp; - /* Nonzero if this instruction has been conditionally skipped. */ - int condjmp; - /* The label that will be jumped to when the instruction is skipped. */ - int condlabel; - /* Thumb-2 conditional execution bits. */ - int condexec_mask; - int condexec_cond; - struct TranslationBlock *tb; - int singlestep_enabled; - int thumb; - int bswap_code; -#if !defined(CONFIG_USER_ONLY) - int user; -#endif - int vfp_enabled; - int vec_len; - int vec_stride; -} DisasContext; - +#include "translate.h" static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; #if defined(CONFIG_USER_ONLY) @@ -82,7 +61,7 @@ static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; #define DISAS_WFI 4 #define DISAS_SWI 5 -static TCGv_ptr cpu_env; +TCGv_ptr cpu_env; /* We reuse the same 64-bit temporaries for efficiency. */ static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; static TCGv_i32 cpu_R[16]; @@ -135,6 +114,8 @@ void arm_translate_init(void) offsetof(CPUARMState, exclusive_info), "exclusive_info"); #endif + a64_translate_init(); + #define GEN_HELPER 2 #include "helper.h" } @@ -842,9 +823,97 @@ static inline void store_reg_from_load(CPUARMState *env, DisasContext *s, } } -static inline void gen_set_pc_im(uint32_t val) +/* Abstractions of "generate code to do a guest load/store for + * AArch32", where a vaddr is always 32 bits (and is zero + * extended if we're a 64 bit core) and data is also + * 32 bits unless specifically doing a 64 bit access. + * These functions work like tcg_gen_qemu_{ld,st}* except + * that their arguments are TCGv_i32 rather than TCGv. + */ +#if TARGET_LONG_BITS == 32 + +#define DO_GEN_LD(OP) \ +static inline void gen_aa32_##OP(TCGv_i32 val, TCGv_i32 addr, int index) \ +{ \ + tcg_gen_qemu_##OP(val, addr, index); \ +} + +#define DO_GEN_ST(OP) \ +static inline void gen_aa32_##OP(TCGv_i32 val, TCGv_i32 addr, int index) \ +{ \ + tcg_gen_qemu_##OP(val, addr, index); \ +} + +static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) +{ + tcg_gen_qemu_ld64(val, addr, index); +} + +static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) +{ + tcg_gen_qemu_st64(val, addr, index); +} + +#else + +#define DO_GEN_LD(OP) \ +static inline void gen_aa32_##OP(TCGv_i32 val, TCGv_i32 addr, int index) \ +{ \ + TCGv addr64 = tcg_temp_new(); \ + TCGv val64 = tcg_temp_new(); \ + tcg_gen_extu_i32_i64(addr64, addr); \ + tcg_gen_qemu_##OP(val64, addr64, index); \ + tcg_temp_free(addr64); \ + tcg_gen_trunc_i64_i32(val, val64); \ + tcg_temp_free(val64); \ +} + +#define DO_GEN_ST(OP) \ +static inline void gen_aa32_##OP(TCGv_i32 val, TCGv_i32 addr, int index) \ +{ \ + TCGv addr64 = tcg_temp_new(); \ + TCGv val64 = tcg_temp_new(); \ + tcg_gen_extu_i32_i64(addr64, addr); \ + tcg_gen_extu_i32_i64(val64, val); \ + tcg_gen_qemu_##OP(val64, addr64, index); \ + tcg_temp_free(addr64); \ + tcg_temp_free(val64); \ +} + +static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) +{ + TCGv addr64 = tcg_temp_new(); + tcg_gen_extu_i32_i64(addr64, addr); + tcg_gen_qemu_ld64(val, addr64, index); + tcg_temp_free(addr64); +} + +static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) { - tcg_gen_movi_i32(cpu_R[15], val); + TCGv addr64 = tcg_temp_new(); + tcg_gen_extu_i32_i64(addr64, addr); + tcg_gen_qemu_st64(val, addr64, index); + tcg_temp_free(addr64); +} + +#endif + +DO_GEN_LD(ld8s) +DO_GEN_LD(ld8u) +DO_GEN_LD(ld16s) +DO_GEN_LD(ld16u) +DO_GEN_LD(ld32u) +DO_GEN_ST(st8) +DO_GEN_ST(st16) +DO_GEN_ST(st32) + +static inline void gen_set_pc_im(DisasContext *s, target_ulong val) +{ + if (s->aarch64) { + gen_a64_set_pc_im(val); + } else { + tcg_gen_movi_i32(cpu_R[15], val); + } } /* Force a TB lookup after an instruction that changes the CPU state. */ @@ -1071,18 +1140,20 @@ VFP_GEN_FIX(ulto) static inline void gen_vfp_ld(DisasContext *s, int dp, TCGv_i32 addr) { - if (dp) - tcg_gen_qemu_ld64(cpu_F0d, addr, IS_USER(s)); - else - tcg_gen_qemu_ld32u(cpu_F0s, addr, IS_USER(s)); + if (dp) { + gen_aa32_ld64(cpu_F0d, addr, IS_USER(s)); + } else { + gen_aa32_ld32u(cpu_F0s, addr, IS_USER(s)); + } } static inline void gen_vfp_st(DisasContext *s, int dp, TCGv_i32 addr) { - if (dp) - tcg_gen_qemu_st64(cpu_F0d, addr, IS_USER(s)); - else - tcg_gen_qemu_st32(cpu_F0s, addr, IS_USER(s)); + if (dp) { + gen_aa32_st64(cpu_F0d, addr, IS_USER(s)); + } else { + gen_aa32_st32(cpu_F0s, addr, IS_USER(s)); + } } static inline long @@ -1419,24 +1490,24 @@ static int disas_iwmmxt_insn(CPUARMState *env, DisasContext *s, uint32_t insn) if (insn & ARM_CP_RW_BIT) { if ((insn >> 28) == 0xf) { /* WLDRW wCx */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); iwmmxt_store_creg(wrd, tmp); } else { i = 1; if (insn & (1 << 8)) { if (insn & (1 << 22)) { /* WLDRD */ - tcg_gen_qemu_ld64(cpu_M0, addr, IS_USER(s)); + gen_aa32_ld64(cpu_M0, addr, IS_USER(s)); i = 0; } else { /* WLDRW wRd */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); } } else { tmp = tcg_temp_new_i32(); if (insn & (1 << 22)) { /* WLDRH */ - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); } else { /* WLDRB */ - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); } } if (i) { @@ -1448,24 +1519,24 @@ static int disas_iwmmxt_insn(CPUARMState *env, DisasContext *s, uint32_t insn) } else { if ((insn >> 28) == 0xf) { /* WSTRW wCx */ tmp = iwmmxt_load_creg(wrd); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); } else { gen_op_iwmmxt_movq_M0_wRn(wrd); tmp = tcg_temp_new_i32(); if (insn & (1 << 8)) { if (insn & (1 << 22)) { /* WSTRD */ - tcg_gen_qemu_st64(cpu_M0, addr, IS_USER(s)); + gen_aa32_st64(cpu_M0, addr, IS_USER(s)); } else { /* WSTRW wRd */ tcg_gen_trunc_i64_i32(tmp, cpu_M0); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); } } else { if (insn & (1 << 22)) { /* WSTRH */ tcg_gen_trunc_i64_i32(tmp, cpu_M0); - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); } else { /* WSTRB */ tcg_gen_trunc_i64_i32(tmp, cpu_M0); - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); } } } @@ -2530,15 +2601,15 @@ static TCGv_i32 gen_load_and_replicate(DisasContext *s, TCGv_i32 addr, int size) TCGv_i32 tmp = tcg_temp_new_i32(); switch (size) { case 0: - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); gen_neon_dup_u8(tmp, 0); break; case 1: - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); gen_neon_dup_low16(tmp); break; case 2: - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); break; default: /* Avoid compiler warnings. */ abort(); @@ -3348,17 +3419,17 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) return 0; } -static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest) +static inline void gen_goto_tb(DisasContext *s, int n, target_ulong dest) { TranslationBlock *tb; tb = s->tb; if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { tcg_gen_goto_tb(n); - gen_set_pc_im(dest); + gen_set_pc_im(s, dest); tcg_gen_exit_tb((uintptr_t)tb + n); } else { - gen_set_pc_im(dest); + gen_set_pc_im(s, dest); tcg_gen_exit_tb(0); } } @@ -3487,7 +3558,7 @@ gen_set_condexec (DisasContext *s) static void gen_exception_insn(DisasContext *s, int offset, int excp) { gen_set_condexec(s); - gen_set_pc_im(s->pc - offset); + gen_set_pc_im(s, s->pc - offset); gen_exception(excp); s->is_jmp = DISAS_JUMP; } @@ -3496,7 +3567,7 @@ static void gen_nop_hint(DisasContext *s, int val) { switch (val) { case 3: /* wfi */ - gen_set_pc_im(s->pc); + gen_set_pc_im(s, s->pc); s->is_jmp = DISAS_WFI; break; case 2: /* wfe */ @@ -3816,11 +3887,11 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) if (size == 3) { tmp64 = tcg_temp_new_i64(); if (load) { - tcg_gen_qemu_ld64(tmp64, addr, IS_USER(s)); + gen_aa32_ld64(tmp64, addr, IS_USER(s)); neon_store_reg64(tmp64, rd); } else { neon_load_reg64(tmp64, rd); - tcg_gen_qemu_st64(tmp64, addr, IS_USER(s)); + gen_aa32_st64(tmp64, addr, IS_USER(s)); } tcg_temp_free_i64(tmp64); tcg_gen_addi_i32(addr, addr, stride); @@ -3829,21 +3900,21 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) if (size == 2) { if (load) { tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); neon_store_reg(rd, pass, tmp); } else { tmp = neon_load_reg(rd, pass); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, stride); } else if (size == 1) { if (load) { tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); tcg_gen_addi_i32(addr, addr, stride); tmp2 = tcg_temp_new_i32(); - tcg_gen_qemu_ld16u(tmp2, addr, IS_USER(s)); + gen_aa32_ld16u(tmp2, addr, IS_USER(s)); tcg_gen_addi_i32(addr, addr, stride); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp, tmp, tmp2); @@ -3853,10 +3924,10 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) tmp = neon_load_reg(rd, pass); tmp2 = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp2, tmp, 16); - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, stride); - tcg_gen_qemu_st16(tmp2, addr, IS_USER(s)); + gen_aa32_st16(tmp2, addr, IS_USER(s)); tcg_temp_free_i32(tmp2); tcg_gen_addi_i32(addr, addr, stride); } @@ -3865,7 +3936,7 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) TCGV_UNUSED_I32(tmp2); for (n = 0; n < 4; n++) { tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); tcg_gen_addi_i32(addr, addr, stride); if (n == 0) { tmp2 = tmp; @@ -3885,7 +3956,7 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) } else { tcg_gen_shri_i32(tmp, tmp2, n * 8); } - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, stride); } @@ -4009,13 +4080,13 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) tmp = tcg_temp_new_i32(); switch (size) { case 0: - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); break; case 1: - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); break; case 2: - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); break; default: /* Avoid compiler warnings. */ abort(); @@ -4033,13 +4104,13 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) tcg_gen_shri_i32(tmp, tmp, shift); switch (size) { case 0: - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); break; case 1: - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); break; case 2: - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); break; } tcg_temp_free_i32(tmp); @@ -6273,7 +6344,7 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) if (isread) { return 1; } - gen_set_pc_im(s->pc); + gen_set_pc_im(s, s->pc); s->is_jmp = DISAS_WFI; return 0; default: @@ -6293,7 +6364,7 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) tmp64 = tcg_const_i64(ri->resetvalue); } else if (ri->readfn) { TCGv_ptr tmpptr; - gen_set_pc_im(s->pc); + gen_set_pc_im(s, s->pc); tmp64 = tcg_temp_new_i64(); tmpptr = tcg_const_ptr(ri); gen_helper_get_cp_reg64(tmp64, cpu_env, tmpptr); @@ -6316,7 +6387,7 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) tmp = tcg_const_i32(ri->resetvalue); } else if (ri->readfn) { TCGv_ptr tmpptr; - gen_set_pc_im(s->pc); + gen_set_pc_im(s, s->pc); tmp = tcg_temp_new_i32(); tmpptr = tcg_const_ptr(ri); gen_helper_get_cp_reg(tmp, cpu_env, tmpptr); @@ -6351,7 +6422,7 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmphi); if (ri->writefn) { TCGv_ptr tmpptr = tcg_const_ptr(ri); - gen_set_pc_im(s->pc); + gen_set_pc_im(s, s->pc); gen_helper_set_cp_reg64(cpu_env, tmpptr, tmp64); tcg_temp_free_ptr(tmpptr); } else { @@ -6362,7 +6433,7 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) if (ri->writefn) { TCGv_i32 tmp; TCGv_ptr tmpptr; - gen_set_pc_im(s->pc); + gen_set_pc_im(s, s->pc); tmp = load_reg(s, rt); tmpptr = tcg_const_ptr(ri); gen_helper_set_cp_reg(cpu_env, tmpptr, tmp); @@ -6463,14 +6534,14 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, switch (size) { case 0: - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); break; case 1: - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); break; case 2: case 3: - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); break; default: abort(); @@ -6481,7 +6552,7 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv_i32 tmp2 = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp2, addr, 4); tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, tmp2, IS_USER(s)); + gen_aa32_ld32u(tmp, tmp2, IS_USER(s)); tcg_temp_free_i32(tmp2); tcg_gen_mov_i32(cpu_exclusive_high, tmp); store_reg(s, rt2, tmp); @@ -6523,14 +6594,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tmp = tcg_temp_new_i32(); switch (size) { case 0: - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); break; case 1: - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); break; case 2: case 3: - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); break; default: abort(); @@ -6541,7 +6612,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, TCGv_i32 tmp2 = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp2, addr, 4); tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, tmp2, IS_USER(s)); + gen_aa32_ld32u(tmp, tmp2, IS_USER(s)); tcg_temp_free_i32(tmp2); tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_high, fail_label); tcg_temp_free_i32(tmp); @@ -6549,14 +6620,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tmp = load_reg(s, rt); switch (size) { case 0: - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); break; case 1: - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); break; case 2: case 3: - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); break; default: abort(); @@ -6565,7 +6636,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, if (size == 3) { tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rt2); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } tcg_gen_movi_i32(cpu_R[rd], 0); @@ -6612,11 +6683,11 @@ static void gen_srs(DisasContext *s, } tcg_gen_addi_i32(addr, addr, offset); tmp = load_reg(s, 14); - tcg_gen_qemu_st32(tmp, addr, 0); + gen_aa32_st32(tmp, addr, 0); tcg_temp_free_i32(tmp); tmp = load_cpu_field(spsr); tcg_gen_addi_i32(addr, addr, 4); - tcg_gen_qemu_st32(tmp, addr, 0); + gen_aa32_st32(tmp, addr, 0); tcg_temp_free_i32(tmp); if (writeback) { switch (amode) { @@ -6762,10 +6833,10 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) tcg_gen_addi_i32(addr, addr, offset); /* Load PC into tmp and CPSR into tmp2. */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, 0); + gen_aa32_ld32u(tmp, addr, 0); tcg_gen_addi_i32(addr, addr, 4); tmp2 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, addr, 0); + gen_aa32_ld32u(tmp2, addr, 0); if (insn & (1 << 21)) { /* Base writeback. */ switch (i) { @@ -7321,13 +7392,13 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) tmp = tcg_temp_new_i32(); switch (op1) { case 0: /* lda */ - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); break; case 2: /* ldab */ - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); break; case 3: /* ldah */ - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); break; default: abort(); @@ -7338,13 +7409,13 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) tmp = load_reg(s, rm); switch (op1) { case 0: /* stl */ - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); break; case 2: /* stlb */ - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); break; case 3: /* stlh */ - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); break; default: abort(); @@ -7399,11 +7470,11 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) tmp = load_reg(s, rm); tmp2 = tcg_temp_new_i32(); if (insn & (1 << 22)) { - tcg_gen_qemu_ld8u(tmp2, addr, IS_USER(s)); - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp2, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); } else { - tcg_gen_qemu_ld32u(tmp2, addr, IS_USER(s)); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp2, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); } tcg_temp_free_i32(tmp); tcg_temp_free_i32(addr); @@ -7425,14 +7496,14 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) tmp = tcg_temp_new_i32(); switch(sh) { case 1: - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); break; case 2: - tcg_gen_qemu_ld8s(tmp, addr, IS_USER(s)); + gen_aa32_ld8s(tmp, addr, IS_USER(s)); break; default: case 3: - tcg_gen_qemu_ld16s(tmp, addr, IS_USER(s)); + gen_aa32_ld16s(tmp, addr, IS_USER(s)); break; } load = 1; @@ -7442,21 +7513,21 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (sh & 1) { /* store */ tmp = load_reg(s, rd); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rd + 1); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); load = 0; } else { /* load */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); store_reg(s, rd, tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); rd++; load = 1; } @@ -7464,7 +7535,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } else { /* store */ tmp = load_reg(s, rd); - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); load = 0; } @@ -7797,17 +7868,17 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) /* load */ tmp = tcg_temp_new_i32(); if (insn & (1 << 22)) { - tcg_gen_qemu_ld8u(tmp, tmp2, i); + gen_aa32_ld8u(tmp, tmp2, i); } else { - tcg_gen_qemu_ld32u(tmp, tmp2, i); + gen_aa32_ld32u(tmp, tmp2, i); } } else { /* store */ tmp = load_reg(s, rd); if (insn & (1 << 22)) { - tcg_gen_qemu_st8(tmp, tmp2, i); + gen_aa32_st8(tmp, tmp2, i); } else { - tcg_gen_qemu_st32(tmp, tmp2, i); + gen_aa32_st32(tmp, tmp2, i); } tcg_temp_free_i32(tmp); } @@ -7874,7 +7945,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (insn & (1 << 20)) { /* load */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); if (user) { tmp2 = tcg_const_i32(i); gen_helper_set_user_reg(cpu_env, tmp2, tmp); @@ -7901,7 +7972,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } else { tmp = load_reg(s, i); } - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } j++; @@ -7957,8 +8028,8 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) tcg_gen_movi_i32(tmp, val); store_reg(s, 14, tmp); } - offset = (((int32_t)insn << 8) >> 8); - val += (offset << 2) + 4; + offset = sextract32(insn << 2, 0, 26); + val += offset + 4; gen_jmp(s, val); } break; @@ -7971,7 +8042,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) break; case 0xf: /* swi */ - gen_set_pc_im(s->pc); + gen_set_pc_im(s, s->pc); s->is_jmp = DISAS_SWI; break; default: @@ -8160,20 +8231,20 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (insn & (1 << 20)) { /* ldrd */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); store_reg(s, rs, tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); store_reg(s, rd, tmp); } else { /* strd */ tmp = load_reg(s, rs); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rd); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } if (insn & (1 << 21)) { @@ -8211,11 +8282,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_add_i32(addr, addr, tmp); tcg_temp_free_i32(tmp); tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); } else { /* tbb */ tcg_temp_free_i32(tmp); tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); } tcg_temp_free_i32(addr); tcg_gen_shli_i32(tmp, tmp, 1); @@ -8252,13 +8323,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); switch (op) { case 0: /* ldab */ - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); break; case 1: /* ldah */ - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); break; case 2: /* lda */ - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); break; default: abort(); @@ -8268,13 +8339,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: /* stlb */ - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); break; case 1: /* stlh */ - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); break; case 2: /* stl */ - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); break; default: abort(); @@ -8302,10 +8373,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_addi_i32(addr, addr, -8); /* Load PC into tmp and CPSR into tmp2. */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, 0); + gen_aa32_ld32u(tmp, addr, 0); tcg_gen_addi_i32(addr, addr, 4); tmp2 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, addr, 0); + gen_aa32_ld32u(tmp2, addr, 0); if (insn & (1 << 21)) { /* Base writeback. */ if (insn & (1 << 24)) { @@ -8344,7 +8415,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (insn & (1 << 20)) { /* Load. */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); if (i == 15) { gen_bx(s, tmp); } else if (i == rn) { @@ -8356,7 +8427,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } else { /* Store. */ tmp = load_reg(s, i); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, 4); @@ -9134,19 +9205,19 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); switch (op) { case 0: - tcg_gen_qemu_ld8u(tmp, addr, user); + gen_aa32_ld8u(tmp, addr, user); break; case 4: - tcg_gen_qemu_ld8s(tmp, addr, user); + gen_aa32_ld8s(tmp, addr, user); break; case 1: - tcg_gen_qemu_ld16u(tmp, addr, user); + gen_aa32_ld16u(tmp, addr, user); break; case 5: - tcg_gen_qemu_ld16s(tmp, addr, user); + gen_aa32_ld16s(tmp, addr, user); break; case 2: - tcg_gen_qemu_ld32u(tmp, addr, user); + gen_aa32_ld32u(tmp, addr, user); break; default: tcg_temp_free_i32(tmp); @@ -9163,13 +9234,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: - tcg_gen_qemu_st8(tmp, addr, user); + gen_aa32_st8(tmp, addr, user); break; case 1: - tcg_gen_qemu_st16(tmp, addr, user); + gen_aa32_st16(tmp, addr, user); break; case 2: - tcg_gen_qemu_st32(tmp, addr, user); + gen_aa32_st32(tmp, addr, user); break; default: tcg_temp_free_i32(tmp); @@ -9306,7 +9377,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, val); tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); tcg_temp_free_i32(addr); store_reg(s, rd, tmp); break; @@ -9509,28 +9580,28 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) switch (op) { case 0: /* str */ - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); break; case 1: /* strh */ - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); break; case 2: /* strb */ - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); break; case 3: /* ldrsb */ - tcg_gen_qemu_ld8s(tmp, addr, IS_USER(s)); + gen_aa32_ld8s(tmp, addr, IS_USER(s)); break; case 4: /* ldr */ - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); break; case 5: /* ldrh */ - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); break; case 6: /* ldrb */ - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); break; case 7: /* ldrsh */ - tcg_gen_qemu_ld16s(tmp, addr, IS_USER(s)); + gen_aa32_ld16s(tmp, addr, IS_USER(s)); break; } if (op >= 3) { /* load */ @@ -9552,12 +9623,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -9574,12 +9645,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + gen_aa32_ld8u(tmp, addr, IS_USER(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + gen_aa32_st8(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -9596,12 +9667,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + gen_aa32_ld16u(tmp, addr, IS_USER(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + gen_aa32_st16(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -9617,12 +9688,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -9690,12 +9761,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* pop */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); store_reg(s, i, tmp); } else { /* push */ tmp = load_reg(s, i); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } /* advance to the next address. */ @@ -9707,13 +9778,13 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* pop pc */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); /* don't set the pc until the rest of the instruction has completed */ } else { /* push lr */ tmp = load_reg(s, 14); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, 4); @@ -9839,7 +9910,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + gen_aa32_ld32u(tmp, addr, IS_USER(s)); if (i == rn) { loaded_var = tmp; } else { @@ -9848,7 +9919,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } else { /* store */ tmp = load_reg(s, i); - tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + gen_aa32_st32(tmp, addr, IS_USER(s)); tcg_temp_free_i32(tmp); } /* advance to the next address */ @@ -9875,7 +9946,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (cond == 0xf) { /* swi */ - gen_set_pc_im(s->pc); + gen_set_pc_im(s, s->pc); s->is_jmp = DISAS_SWI; break; } @@ -9932,7 +10003,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, uint16_t *gen_opc_end; int j, lj; target_ulong pc_start; - uint32_t next_page_start; + target_ulong next_page_start; int num_insns; int max_insns; @@ -9947,16 +10018,32 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, dc->pc = pc_start; dc->singlestep_enabled = cs->singlestep_enabled; dc->condjmp = 0; - dc->thumb = ARM_TBFLAG_THUMB(tb->flags); - dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); - dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; - dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; + + if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { + dc->aarch64 = 1; + dc->thumb = 0; + dc->bswap_code = 0; + dc->condexec_mask = 0; + dc->condexec_cond = 0; #if !defined(CONFIG_USER_ONLY) - dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0); + dc->user = 0; #endif - dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags); - dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); - dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); + dc->vfp_enabled = 0; + dc->vec_len = 0; + dc->vec_stride = 0; + } else { + dc->aarch64 = 0; + dc->thumb = ARM_TBFLAG_THUMB(tb->flags); + dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); + dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; + dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; +#if !defined(CONFIG_USER_ONLY) + dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0); +#endif + dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags); + dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); + dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); + } cpu_F0s = tcg_temp_new_i32(); cpu_F1s = tcg_temp_new_i32(); cpu_F0d = tcg_temp_new_i64(); @@ -10018,7 +10105,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, do { #ifdef CONFIG_USER_ONLY /* Intercept jump to the magic kernel page. */ - if (dc->pc >= 0xffff0000) { + if (!dc->aarch64 && dc->pc >= 0xffff0000) { /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_exception(EXCP_KERNEL_TRAP); @@ -10066,7 +10153,9 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, tcg_gen_debug_insn_start(dc->pc); } - if (dc->thumb) { + if (dc->aarch64) { + disas_a64_insn(env, dc); + } else if (dc->thumb) { disas_thumb_insn(env, dc); if (dc->condexec_mask) { dc->condexec_cond = (dc->condexec_cond & 0xe) @@ -10086,7 +10175,8 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, } if (tcg_check_temp_count()) { - fprintf(stderr, "TCG temporary leak before %08x\n", dc->pc); + fprintf(stderr, "TCG temporary leak before "TARGET_FMT_lx"\n", + dc->pc); } /* Translation stops when a conditional branch is encountered. @@ -10124,7 +10214,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, gen_set_label(dc->condlabel); } if (dc->condjmp || !dc->is_jmp) { - gen_set_pc_im(dc->pc); + gen_set_pc_im(dc, dc->pc); dc->condjmp = 0; } gen_set_condexec(dc); @@ -10258,6 +10348,10 @@ void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, int pc_pos) { - env->regs[15] = tcg_ctx.gen_opc_pc[pc_pos]; + if (is_a64(env)) { + env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + } else { + env->regs[15] = tcg_ctx.gen_opc_pc[pc_pos]; + } env->condexec_bits = gen_opc_condexec_bits[pc_pos]; } diff --git a/target-arm/translate.h b/target-arm/translate.h new file mode 100644 index 0000000000..67c776053b --- /dev/null +++ b/target-arm/translate.h @@ -0,0 +1,49 @@ +#ifndef TARGET_ARM_TRANSLATE_H +#define TARGET_ARM_TRANSLATE_H + +/* internal defines */ +typedef struct DisasContext { + target_ulong pc; + uint32_t insn; + int is_jmp; + /* Nonzero if this instruction has been conditionally skipped. */ + int condjmp; + /* The label that will be jumped to when the instruction is skipped. */ + int condlabel; + /* Thumb-2 conditional execution bits. */ + int condexec_mask; + int condexec_cond; + struct TranslationBlock *tb; + int singlestep_enabled; + int thumb; + int bswap_code; +#if !defined(CONFIG_USER_ONLY) + int user; +#endif + int vfp_enabled; + int vec_len; + int vec_stride; + int aarch64; +} DisasContext; + +extern TCGv_ptr cpu_env; + +#ifdef TARGET_AARCH64 +void a64_translate_init(void); +void disas_a64_insn(CPUARMState *env, DisasContext *s); +void gen_a64_set_pc_im(uint64_t val); +#else +static inline void a64_translate_init(void) +{ +} + +static inline void disas_a64_insn(CPUARMState *env, DisasContext *s) +{ +} + +static inline void gen_a64_set_pc_im(uint64_t val) +{ +} +#endif + +#endif /* TARGET_ARM_TRANSLATE_H */ diff --git a/target-i386/arch_memory_mapping.c b/target-i386/arch_memory_mapping.c index 2566a040a6..462f984a26 100644 --- a/target-i386/arch_memory_mapping.c +++ b/target-i386/arch_memory_mapping.c @@ -75,7 +75,7 @@ static void walk_pte2(MemoryMappingList *list, } /* PAE Paging or IA-32e Paging */ -#define PLM4_ADDR_MASK 0xffffffffff000 /* selects bits 51:12 */ +#define PLM4_ADDR_MASK 0xffffffffff000ULL /* selects bits 51:12 */ static void walk_pde(MemoryMappingList *list, hwaddr pde_start_addr, int32_t a20_mask, target_ulong start_line_addr) 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/arm/tcg-target.c b/tcg/arm/tcg-target.c index eb0e84ce44..622cc49aa7 100644 --- a/tcg/arm/tcg-target.c +++ b/tcg/arm/tcg-target.c @@ -175,20 +175,12 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); #ifdef CONFIG_SOFTMMU - /* r0-r2 will be overwritten when reading the tlb entry, + /* r0-r2,lr will be overwritten when reading the tlb entry, so don't use these. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); -#endif - break; - case 'L': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); -#ifdef CONFIG_SOFTMMU - /* r1 is still needed to load data_reg or data_reg2, - so don't use it. */ - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R14); #endif break; @@ -207,6 +199,7 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) /* Avoid clashes with registers being used for helper args */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); #endif + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R14); #endif break; @@ -320,6 +313,9 @@ typedef enum { INSN_STRB_REG = 0x06400000, INSN_LDRD_IMM = 0x004000d0, + INSN_LDRD_REG = 0x000000d0, + INSN_STRD_IMM = 0x004000f0, + INSN_STRD_REG = 0x000000f0, } ARMInsn; #define SHIFT_IMM_LSL(im) (((im) << 7) | 0x00) @@ -379,13 +375,17 @@ static inline void tcg_out_b_noaddr(TCGContext *s, int cond) /* We pay attention here to not modify the branch target by skipping the corresponding bytes. This ensure that caches and memory are kept coherent during retranslation. */ -#ifdef HOST_WORDS_BIGENDIAN - tcg_out8(s, (cond << 4) | 0x0a); - s->code_ptr += 3; -#else s->code_ptr += 3; tcg_out8(s, (cond << 4) | 0x0a); -#endif +} + +static inline void tcg_out_bl_noaddr(TCGContext *s, int cond) +{ + /* We pay attention here to not modify the branch target by skipping + the corresponding bytes. This ensure that caches and memory are + kept coherent during retranslation. */ + s->code_ptr += 3; + tcg_out8(s, (cond << 4) | 0x0b); } static inline void tcg_out_bl(TCGContext *s, int cond, int32_t offset) @@ -810,6 +810,30 @@ static inline void tcg_out_st32_r(TCGContext *s, int cond, TCGReg rt, tcg_out_memop_r(s, cond, INSN_STR_REG, rt, rn, rm, 1, 1, 0); } +static inline void tcg_out_ldrd_8(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm8) +{ + tcg_out_memop_8(s, cond, INSN_LDRD_IMM, rt, rn, imm8, 1, 0); +} + +static inline void tcg_out_ldrd_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 0); +} + +static inline void tcg_out_strd_8(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, int imm8) +{ + tcg_out_memop_8(s, cond, INSN_STRD_IMM, rt, rn, imm8, 1, 0); +} + +static inline void tcg_out_strd_r(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_STRD_REG, rt, rn, rm, 1, 1, 0); +} + /* Register pre-increment with base writeback. */ static inline void tcg_out_ld32_rwb(TCGContext *s, int cond, TCGReg rt, TCGReg rn, TCGReg rm) @@ -975,34 +999,27 @@ static inline void tcg_out_st8(TCGContext *s, int cond, tcg_out_st8_12(s, cond, rd, rn, offset); } -/* The _goto case is normally between TBs within the same code buffer, - * and with the code buffer limited to 16MB we shouldn't need the long - * case. - * - * .... except to the prologue that is in its own buffer. +/* The _goto case is normally between TBs within the same code buffer, and + * with the code buffer limited to 16MB we wouldn't need the long case. + * But we also use it for the tail-call to the qemu_ld/st helpers, which does. */ static inline void tcg_out_goto(TCGContext *s, int cond, uint32_t addr) { - int32_t val; + int32_t disp = addr - (tcg_target_long) s->code_ptr; - if (addr & 1) { - /* goto to a Thumb destination isn't supported */ - tcg_abort(); + if ((addr & 1) == 0 && disp - 8 < 0x01fffffd && disp - 8 > -0x01fffffd) { + tcg_out_b(s, cond, disp); + return; } - val = addr - (tcg_target_long) s->code_ptr; - if (val - 8 < 0x01fffffd && val - 8 > -0x01fffffd) - tcg_out_b(s, cond, val); - else { - if (cond == COND_AL) { - tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); - tcg_out32(s, addr); - } else { - tcg_out_movi32(s, cond, TCG_REG_TMP, val - 8); - tcg_out_dat_reg(s, cond, ARITH_ADD, - TCG_REG_PC, TCG_REG_PC, - TCG_REG_TMP, SHIFT_IMM_LSL(0)); + tcg_out_movi32(s, cond, TCG_REG_TMP, addr); + if (use_armv5t_instructions) { + tcg_out_bx(s, cond, TCG_REG_TMP); + } else { + if (addr & 1) { + tcg_abort(); } + tcg_out_mov_reg(s, cond, TCG_REG_PC, TCG_REG_TMP); } } @@ -1057,23 +1074,29 @@ static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index) } #ifdef 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, +/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, + * int mmu_idx, uintptr_t ra) + */ +static const void * const qemu_ld_helpers[8] = { + helper_ret_ldub_mmu, + helper_ret_lduw_mmu, + helper_ret_ldul_mmu, + helper_ret_ldq_mmu, + + helper_ret_ldsb_mmu, + helper_ret_ldsw_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, }; /* Helper routines for marshalling helper function arguments into @@ -1117,53 +1140,62 @@ static TCGReg tcg_out_arg_reg64(TCGContext *s, TCGReg argreg, if (argreg & 1) { argreg++; } - argreg = tcg_out_arg_reg32(s, argreg, arglo); - argreg = tcg_out_arg_reg32(s, argreg, arghi); - return argreg; + if (use_armv6_instructions && argreg >= 4 + && (arglo & 1) == 0 && arghi == arglo + 1) { + tcg_out_strd_8(s, COND_AL, arglo, + TCG_REG_CALL_STACK, (argreg - 4) * 4); + return argreg + 2; + } else { + argreg = tcg_out_arg_reg32(s, argreg, arglo); + argreg = tcg_out_arg_reg32(s, argreg, arghi); + return argreg; + } } #define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS) -/* Load and compare a TLB entry, leaving the flags set. Leaves R2 pointing - to the tlb entry. Clobbers R1 and TMP. */ +/* We're expecting to use an 8-bit immediate and to mask. */ +QEMU_BUILD_BUG_ON(CPU_TLB_BITS > 8); -static void tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, - int s_bits, int tlb_offset) +/* We're expecting to use an 8-bit immediate add + 8-bit ldrd offset. + Using the offset of the second entry in the last tlb table ensures + that we can index all of the elements of the first entry. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1]) + > 0xffff); + +/* Load and compare a TLB entry, leaving the flags set. Returns the register + containing the addend of the tlb entry. Clobbers R0, R1, R2, TMP. */ + +static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, + int s_bits, int mem_index, bool is_load) { TCGReg base = TCG_AREG0; + 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); /* Should generate something like the following: - * pre-v7: * shr tmp, addr_reg, #TARGET_PAGE_BITS (1) - * add r2, env, #off & 0xff00 + * add r2, env, #high * and r0, tmp, #(CPU_TLB_SIZE - 1) (2) * add r2, r2, r0, lsl #CPU_TLB_ENTRY_BITS (3) - * ldr r0, [r2, #off & 0xff]! (4) + * ldr r0, [r2, #cmp] (4) * tst addr_reg, #s_mask - * cmpeq r0, tmp, lsl #TARGET_PAGE_BITS (5) - * - * v7 (not implemented yet): - * ubfx r2, addr_reg, #TARGET_PAGE_BITS, #CPU_TLB_BITS (1) - * movw tmp, #~TARGET_PAGE_MASK & ~s_mask - * movw r0, #off - * add r2, env, r2, lsl #CPU_TLB_ENTRY_BITS (2) - * bic tmp, addr_reg, tmp - * ldr r0, [r2, r0]! (3) - * cmp r0, tmp (4) + * ldr r1, [r2, #add] (5) + * cmpeq r0, tmp, lsl #TARGET_PAGE_BITS */ -# if CPU_TLB_BITS > 8 -# error -# endif tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, addrlo, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); - /* We assume that the offset is contained within 16 bits. */ - assert((tlb_offset & ~0xffff) == 0); - if (tlb_offset > 0xff) { + /* We checked that the offset is contained within 16 bits above. */ + if (add_off > 0xfff || (use_armv6_instructions && cmp_off > 0xff)) { tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R2, base, - (24 << 7) | (tlb_offset >> 8)); - tlb_offset &= 0xff; + (24 << 7) | (cmp_off >> 8)); base = TCG_REG_R2; + add_off -= cmp_off & 0xff00; + cmp_off &= 0xff; } tcg_out_dat_imm(s, COND_AL, ARITH_AND, @@ -1175,14 +1207,11 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, but due to how the pointer needs setting up, ldm isn't useful. Base arm5 doesn't have ldrd, but armv5te does. */ if (use_armv6_instructions && TARGET_LONG_BITS == 64) { - tcg_out_memop_8(s, COND_AL, INSN_LDRD_IMM, TCG_REG_R0, - TCG_REG_R2, tlb_offset, 1, 1); + tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_REG_R2, cmp_off); } else { - tcg_out_memop_12(s, COND_AL, INSN_LDR_IMM, TCG_REG_R0, - TCG_REG_R2, tlb_offset, 1, 1); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_R2, cmp_off); if (TARGET_LONG_BITS == 64) { - tcg_out_memop_12(s, COND_AL, INSN_LDR_IMM, TCG_REG_R1, - TCG_REG_R2, 4, 1, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2, cmp_off + 4); } } @@ -1192,6 +1221,9 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, 0, addrlo, (1 << s_bits) - 1); } + /* Load the tlb addend. */ + tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R2, add_off); + tcg_out_dat_reg(s, (s_bits ? COND_EQ : COND_AL), ARITH_CMP, 0, TCG_REG_R0, TCG_REG_TMP, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); @@ -1199,6 +1231,8 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, TCG_REG_R1, addrhi, SHIFT_IMM_LSL(0)); } + + return TCG_REG_R2; } /* Record the context of a call to the out of line helper code for the slow @@ -1232,7 +1266,8 @@ static void add_qemu_ldst_label(TCGContext *s, int is_ld, int opc, static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { TCGReg argreg, data_reg, data_reg2; - uint8_t *start; + int opc = lb->opc; + uintptr_t func; reloc_pc24(lb->label_ptr[0], (tcg_target_long)s->code_ptr); @@ -1243,46 +1278,46 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) argreg = tcg_out_arg_reg32(s, argreg, lb->addrlo_reg); } argreg = tcg_out_arg_imm32(s, argreg, lb->mem_index); - tcg_out_call(s, (tcg_target_long) qemu_ld_helpers[lb->opc & 3]); + argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); + + /* For armv6 we can use the canonical unsigned helpers and minimize + icache usage. For pre-armv6, use the signed helpers since we do + not have a single insn sign-extend. */ + if (use_armv6_instructions) { + func = (uintptr_t)qemu_ld_helpers[opc & 3]; + } else { + func = (uintptr_t)qemu_ld_helpers[opc]; + if (opc & 4) { + opc = 2; + } + } + tcg_out_call(s, func); data_reg = lb->datalo_reg; data_reg2 = lb->datahi_reg; - - start = s->code_ptr; - switch (lb->opc) { + switch (opc) { case 0 | 4: tcg_out_ext8s(s, COND_AL, data_reg, TCG_REG_R0); break; case 1 | 4: tcg_out_ext16s(s, COND_AL, data_reg, TCG_REG_R0); break; - case 0: - case 1: - case 2: default: tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_R0); break; case 3: - tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_R0); - tcg_out_mov_reg(s, COND_AL, data_reg2, TCG_REG_R1); - break; - } - - /* For GETPC_LDST in exec-all.h, we architect exactly 2 insns between - the call and the branch back to straight-line code. Note that the - moves above could be elided by register allocation, nor do we know - which code alternative we chose for extension. */ - switch (s->code_ptr - start) { - case 0: - tcg_out_nop(s); - /* FALLTHRU */ - case 4: - tcg_out_nop(s); - /* FALLTHRU */ - case 8: + if (data_reg != TCG_REG_R1) { + tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_R0); + tcg_out_mov_reg(s, COND_AL, data_reg2, TCG_REG_R1); + } else if (data_reg2 != TCG_REG_R0) { + tcg_out_mov_reg(s, COND_AL, data_reg2, TCG_REG_R1); + tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_R0); + } else { + tcg_out_mov_reg(s, COND_AL, TCG_REG_TMP, TCG_REG_R0); + tcg_out_mov_reg(s, COND_AL, data_reg2, TCG_REG_R1); + tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_TMP); + } break; - default: - abort(); } tcg_out_goto(s, COND_AL, (tcg_target_long)lb->raddr); @@ -1320,13 +1355,10 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) } argreg = tcg_out_arg_imm32(s, argreg, lb->mem_index); - tcg_out_call(s, (tcg_target_long) qemu_st_helpers[lb->opc & 3]); + argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); - /* For GETPC_LDST in exec-all.h, we architect exactly 2 insns between - the call and the branch back to straight-line code. */ - tcg_out_nop(s); - tcg_out_nop(s); - tcg_out_goto(s, COND_AL, (tcg_target_long)lb->raddr); + /* Tail-call to the helper, which will return to the fast path. */ + tcg_out_goto(s, COND_AL, (tcg_target_long) qemu_st_helpers[lb->opc & 3]); } #endif /* SOFTMMU */ @@ -1336,7 +1368,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) bool bswap; #ifdef CONFIG_SOFTMMU int mem_index, s_bits; - TCGReg addr_reg2; + TCGReg addr_reg2, addend; uint8_t *label_ptr; #endif #ifdef TARGET_WORDS_BIGENDIAN @@ -1353,53 +1385,63 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) mem_index = *args; s_bits = opc & 3; - tcg_out_tlb_read(s, addr_reg, addr_reg2, s_bits, - offsetof(CPUArchState, tlb_table[mem_index][0].addr_read)); + addend = tcg_out_tlb_read(s, addr_reg, addr_reg2, s_bits, mem_index, 1); + /* This a conditional BL only to load a pointer within this opcode into LR + for the slow path. We will not be using the value for a tail call. */ label_ptr = s->code_ptr; - tcg_out_b_noaddr(s, COND_NE); - - tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2, - offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_read)); + tcg_out_bl_noaddr(s, COND_NE); switch (opc) { case 0: - tcg_out_ld8_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_ld8_r(s, COND_AL, data_reg, addr_reg, addend); break; case 0 | 4: - tcg_out_ld8s_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_ld8s_r(s, COND_AL, data_reg, addr_reg, addend); break; case 1: - tcg_out_ld16u_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_ld16u_r(s, COND_AL, data_reg, addr_reg, addend); if (bswap) { tcg_out_bswap16(s, COND_AL, data_reg, data_reg); } break; case 1 | 4: if (bswap) { - tcg_out_ld16u_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_ld16u_r(s, COND_AL, data_reg, addr_reg, addend); tcg_out_bswap16s(s, COND_AL, data_reg, data_reg); } else { - tcg_out_ld16s_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_ld16s_r(s, COND_AL, data_reg, addr_reg, addend); } break; case 2: default: - tcg_out_ld32_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_ld32_r(s, COND_AL, data_reg, addr_reg, addend); if (bswap) { tcg_out_bswap32(s, COND_AL, data_reg, data_reg); } break; case 3: - if (bswap) { - tcg_out_ld32_rwb(s, COND_AL, data_reg2, TCG_REG_R1, addr_reg); - tcg_out_ld32_12(s, COND_AL, data_reg, TCG_REG_R1, 4); - tcg_out_bswap32(s, COND_AL, data_reg2, data_reg2); - tcg_out_bswap32(s, COND_AL, data_reg, data_reg); - } else { - tcg_out_ld32_rwb(s, COND_AL, data_reg, TCG_REG_R1, addr_reg); - tcg_out_ld32_12(s, COND_AL, data_reg2, TCG_REG_R1, 4); + { + /* Be careful not to modify data_reg and data_reg2 + for the slow path below. */ + TCGReg dl = (bswap ? data_reg2 : data_reg); + TCGReg dh = (bswap ? data_reg : data_reg2); + + if (use_armv6_instructions && (dl & 1) == 0 && dh == dl + 1) { + tcg_out_ldrd_r(s, COND_AL, dl, addr_reg, addend); + } else if (dl != addend) { + tcg_out_ld32_rwb(s, COND_AL, dl, addend, addr_reg); + tcg_out_ld32_12(s, COND_AL, dh, addend, 4); + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_TMP, + addend, addr_reg, SHIFT_IMM_LSL(0)); + tcg_out_ld32_12(s, COND_AL, dl, TCG_REG_TMP, 0); + tcg_out_ld32_12(s, COND_AL, dh, TCG_REG_TMP, 4); + } + if (bswap) { + tcg_out_bswap32(s, COND_AL, dh, dh); + tcg_out_bswap32(s, COND_AL, dl, dl); + } } break; } @@ -1450,9 +1492,13 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) } break; case 3: - /* TODO: use block load - - * check that data_reg2 > data_reg or the other way */ - if (data_reg == addr_reg) { + if (use_armv6_instructions && !bswap + && (data_reg & 1) == 0 && data_reg2 == data_reg + 1) { + tcg_out_ldrd_8(s, COND_AL, data_reg, addr_reg, 0); + } else if (use_armv6_instructions && bswap + && (data_reg2 & 1) == 0 && data_reg == data_reg2 + 1) { + tcg_out_ldrd_8(s, COND_AL, data_reg2, addr_reg, 0); + } else if (data_reg == addr_reg) { tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4); tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0); } else { @@ -1474,7 +1520,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) bool bswap; #ifdef CONFIG_SOFTMMU int mem_index, s_bits; - TCGReg addr_reg2; + TCGReg addr_reg2, addend; uint8_t *label_ptr; #endif #ifdef TARGET_WORDS_BIGENDIAN @@ -1491,51 +1537,49 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) mem_index = *args; s_bits = opc & 3; - tcg_out_tlb_read(s, addr_reg, addr_reg2, s_bits, - offsetof(CPUArchState, - tlb_table[mem_index][0].addr_write)); - - label_ptr = s->code_ptr; - tcg_out_b_noaddr(s, COND_NE); - - tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2, - offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_write)); + addend = tcg_out_tlb_read(s, addr_reg, addr_reg2, s_bits, mem_index, 0); switch (opc) { case 0: - tcg_out_st8_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, addend); break; case 1: if (bswap) { - tcg_out_bswap16st(s, COND_AL, TCG_REG_R0, data_reg); - tcg_out_st16_r(s, COND_AL, TCG_REG_R0, addr_reg, TCG_REG_R1); + tcg_out_bswap16st(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st16_r(s, COND_EQ, TCG_REG_R0, addr_reg, addend); } else { - tcg_out_st16_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_st16_r(s, COND_EQ, data_reg, addr_reg, addend); } break; case 2: default: if (bswap) { - tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); - tcg_out_st32_r(s, COND_AL, TCG_REG_R0, addr_reg, TCG_REG_R1); + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st32_r(s, COND_EQ, TCG_REG_R0, addr_reg, addend); } else { - tcg_out_st32_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1); + tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, addend); } break; case 3: if (bswap) { - tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg2); - tcg_out_st32_rwb(s, COND_AL, TCG_REG_R0, TCG_REG_R1, addr_reg); - tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); - tcg_out_st32_12(s, COND_AL, TCG_REG_R0, TCG_REG_R1, 4); + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg2); + tcg_out_st32_rwb(s, COND_EQ, TCG_REG_R0, addend, addr_reg); + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_EQ, TCG_REG_R0, addend, 4); + } else if (use_armv6_instructions + && (data_reg & 1) == 0 && data_reg2 == data_reg + 1) { + tcg_out_strd_r(s, COND_EQ, data_reg, addr_reg, addend); } else { - tcg_out_st32_rwb(s, COND_AL, data_reg, TCG_REG_R1, addr_reg); - tcg_out_st32_12(s, COND_AL, data_reg2, TCG_REG_R1, 4); + tcg_out_st32_rwb(s, COND_EQ, data_reg, addend, addr_reg); + tcg_out_st32_12(s, COND_EQ, data_reg2, addend, 4); } break; } + /* The conditional call must come last, as we're going to return here. */ + label_ptr = s->code_ptr; + tcg_out_bl_noaddr(s, COND_NE); + add_qemu_ldst_label(s, 0, opc, data_reg, data_reg2, addr_reg, addr_reg2, mem_index, s->code_ptr, label_ptr); #else /* !CONFIG_SOFTMMU */ @@ -1576,13 +1620,14 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) } break; case 3: - /* TODO: use block store - - * check that data_reg2 > data_reg or the other way */ if (bswap) { tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg2); tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0); tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 4); + } else if (use_armv6_instructions + && (data_reg & 1) == 0 && data_reg2 == data_reg + 1) { + tcg_out_strd_8(s, COND_AL, data_reg, addr_reg, 0); } else { tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4); @@ -1991,7 +2036,7 @@ static const TCGTargetOpDef arm_op_defs[] = { { INDEX_op_qemu_ld16u, { "r", "l" } }, { INDEX_op_qemu_ld16s, { "r", "l" } }, { INDEX_op_qemu_ld32, { "r", "l" } }, - { INDEX_op_qemu_ld64, { "L", "L", "l" } }, + { INDEX_op_qemu_ld64, { "r", "r", "l" } }, { INDEX_op_qemu_st8, { "s", "s" } }, { INDEX_op_qemu_st16, { "s", "s" } }, @@ -2003,7 +2048,7 @@ static const TCGTargetOpDef arm_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, { "L", "L", "l", "l" } }, + { INDEX_op_qemu_ld64, { "r", "r", "l", "l" } }, { INDEX_op_qemu_st8, { "s", "s", "s" } }, { INDEX_op_qemu_st16, { "s", "s", "s" } }, diff --git a/tcg/optimize.c b/tcg/optimize.c index b29bf25b67..89e2d6a3b3 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -238,20 +238,16 @@ static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y) return (int64_t)x >> (int64_t)y; case INDEX_op_rotr_i32: - x = ((uint32_t)x << (32 - y)) | ((uint32_t)x >> y); - return x; + return ror32(x, y); case INDEX_op_rotr_i64: - x = ((uint64_t)x << (64 - y)) | ((uint64_t)x >> y); - return x; + return ror64(x, y); case INDEX_op_rotl_i32: - x = ((uint32_t)x << y) | ((uint32_t)x >> (32 - y)); - return x; + return rol32(x, y); case INDEX_op_rotl_i64: - x = ((uint64_t)x << y) | ((uint64_t)x >> (64 - y)); - return x; + return rol64(x, y); CASE_OP_32_64(not): return ~x; 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)); } } diff --git a/tcg/tci/tcg-target.c b/tcg/tci/tcg-target.c index 233ab3bf35..4976becbe7 100644 --- a/tcg/tci/tcg-target.c +++ b/tcg/tci/tcg-target.c @@ -670,7 +670,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: - /* TODO: Implementation of rotl_i64, rotr_i64 missing in tci.c. */ case INDEX_op_rotl_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */ case INDEX_op_rotr_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */ tcg_out_r(s, args[0]); @@ -688,13 +688,13 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) t0 = *tb_ptr++; t1 = tci_read_ri32(&tb_ptr); t2 = tci_read_ri32(&tb_ptr); - tci_write_reg32(t0, (t1 << t2) | (t1 >> (32 - t2))); + tci_write_reg32(t0, rol32(t1, t2)); break; case INDEX_op_rotr_i32: t0 = *tb_ptr++; t1 = tci_read_ri32(&tb_ptr); t2 = tci_read_ri32(&tb_ptr); - tci_write_reg32(t0, (t1 >> t2) | (t1 << (32 - t2))); + tci_write_reg32(t0, ror32(t1, t2)); break; #endif #if TCG_TARGET_HAS_deposit_i32 @@ -952,8 +952,16 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) break; #if TCG_TARGET_HAS_rot_i64 case INDEX_op_rotl_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(&tb_ptr); + t2 = tci_read_ri64(&tb_ptr); + tci_write_reg64(t0, rol64(t1, t2)); + break; case INDEX_op_rotr_i64: - TODO(); + t0 = *tb_ptr++; + t1 = tci_read_ri64(&tb_ptr); + t2 = tci_read_ri64(&tb_ptr); + tci_write_reg64(t0, ror64(t1, t2)); break; #endif #if TCG_TARGET_HAS_deposit_i64 @@ -1085,7 +1093,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 +1104,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 +1115,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 +1126,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 +1138,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 +1149,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 +1161,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 +1175,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 +1190,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 +1201,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 +1212,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 +1223,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..425757cfe1 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,8 +5,11 @@ check-qjson check-qlist check-qstring test-aio +test-bitops +test-throttle test-cutils test-hbitmap +test-int128 test-iov test-mul64 test-qapi-types.[ch] @@ -20,3 +23,4 @@ test-thread-pool test-x86-cpuid test-xbzrle *-test +qapi-schema/*.test.* diff --git a/tests/Makefile b/tests/Makefile index baba9e95ad..915ae5e2d1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -31,6 +31,7 @@ check-unit-y += tests/test-visitor-serialization$(EXESUF) check-unit-y += tests/test-iov$(EXESUF) gcov-files-test-iov-y = util/iov.c check-unit-y += tests/test-aio$(EXESUF) +check-unit-y += tests/test-throttle$(EXESUF) gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c check-unit-y += tests/test-thread-pool$(EXESUF) @@ -120,6 +121,7 @@ tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a +tests/test-throttle$(EXESUF): tests/test-throttle.o $(block-obj-y) libqemuutil.a libqemustub.a tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a @@ -172,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 @@ -250,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 @@ -258,10 +261,10 @@ check-tests/test-qapi.py: tests/test-qapi.py .PHONY: $(patsubst %, check-%, $(check-qapi-schema-y)) $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json - $(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py <$^ >$*.out 2>$*.err; echo $$? >$*.exit, " TEST $*.out") - @diff -q $(SRC_PATH)/$*.out $*.out - @diff -q $(SRC_PATH)/$*.err $*.err - @diff -q $(SRC_PATH)/$*.exit $*.exit + $(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py <$^ >$*.test.out 2>$*.test.err; echo $$? >$*.test.exit, " TEST $*.out") + @diff -q $(SRC_PATH)/$*.out $*.test.out + @diff -q $(SRC_PATH)/$*.err $*.test.err + @diff -q $(SRC_PATH)/$*.exit $*.test.exit # Consolidated targets diff --git a/tests/qemu-iotests/.gitignore b/tests/qemu-iotests/.gitignore index 62b4002995..0541f80daa 100644 --- a/tests/qemu-iotests/.gitignore +++ b/tests/qemu-iotests/.gitignore @@ -2,6 +2,7 @@ check.log check.time *.out.bad *.notrun +socket_scm_helper # ignore everything in the scratch directory scratch/ 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/026.out b/tests/qemu-iotests/026.out index fb4f20e7cd..15045799a2 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -5,16 +5,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: on; write write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: on; write -b write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write @@ -33,16 +29,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write -b write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write @@ -126,56 +118,56 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write write failed: Input/output error -128 leaked clusters were found on the image. +127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -b write failed: Input/output error -128 leaked clusters were found on the image. +127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write write failed: Input/output error -128 leaked clusters were found on the image. +127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write -b write failed: Input/output error -128 leaked clusters were found on the image. +127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write write failed: No space left on device -128 leaked clusters were found on the image. +127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -b write failed: No space left on device -128 leaked clusters were found on the image. +127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write write failed: No space left on device -128 leaked clusters were found on the image. +127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write -b write failed: No space left on device -128 leaked clusters were found on the image. +127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -186,9 +178,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: off; write @@ -210,9 +200,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: off; write @@ -575,7 +563,6 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 5; imm: off; once: off -qcow2_free_clusters failed: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -586,7 +573,6 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 28; imm: off; once: off -qcow2_free_clusters failed: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -597,7 +583,6 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 5; imm: off; once: off -qcow2_free_clusters failed: Input/output error write failed: Input/output error 96 leaked clusters were found on the image. @@ -610,7 +595,6 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 28; imm: off; once: off -qcow2_free_clusters failed: No space left on device write failed: No space left on device 96 leaked clusters were found on the image. diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache new file mode 100644 index 0000000000..c9d242e9ec --- /dev/null +++ b/tests/qemu-iotests/026.out.nocache @@ -0,0 +1,610 @@ +QA output created by 026 +Errors while writing 128 kB + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_update; errno: 5; imm: off; once: on; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_update; errno: 5; imm: off; once: on; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_update; errno: 5; imm: off; once: off; write +write failed: Input/output error + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_update; errno: 5; imm: off; once: off; write -b +write failed: Input/output error + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_update; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_update; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_update; errno: 28; imm: off; once: off; write +write failed: No space left on device + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_update; errno: 28; imm: off; once: off; write -b +write failed: No space left on device + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_load; errno: 5; imm: off; once: on; write +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: Input/output error +read failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_load; errno: 5; imm: off; once: on; write -b +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: Input/output error +read failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_load; errno: 5; imm: off; once: off; write +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: Input/output error +read failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_load; errno: 5; imm: off; once: off; write -b +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: Input/output error +read failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_load; errno: 28; imm: off; once: on; write +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: No space left on device +read failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_load; errno: 28; imm: off; once: on; write -b +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: No space left on device +read failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_load; errno: 28; imm: off; once: off; write +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: No space left on device +read failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_load; errno: 28; imm: off; once: off; write -b +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: No space left on device +read failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_update; errno: 5; imm: off; once: on; write +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +127 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_update; errno: 5; imm: off; once: on; write -b +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +127 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_update; errno: 5; imm: off; once: off; write +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +127 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_update; errno: 5; imm: off; once: off; write -b +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +127 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_update; errno: 28; imm: off; once: on; write +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +127 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_update; errno: 28; imm: off; once: on; write -b +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +127 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_update; errno: 28; imm: off; once: off; write +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +127 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_update; errno: 28; imm: off; once: off; write -b +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +127 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_alloc.write; errno: 5; imm: off; once: on; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_alloc.write; errno: 5; imm: off; once: off; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_alloc.write; errno: 5; imm: off; once: off; write -b +write failed: Input/output error + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_alloc.write; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_alloc.write; errno: 28; imm: off; once: off; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l2_alloc.write; errno: 28; imm: off; once: off; write -b +write failed: No space left on device + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: write_aio; errno: 5; imm: off; once: on; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: write_aio; errno: 5; imm: off; once: on; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: write_aio; errno: 5; imm: off; once: off; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: write_aio; errno: 5; imm: off; once: off; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: write_aio; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: write_aio; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: write_aio; errno: 28; imm: off; once: off; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: write_aio; errno: 28; imm: off; once: off; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_load; errno: 5; imm: off; once: on; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_load; errno: 5; imm: off; once: on; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_load; errno: 5; imm: off; once: off; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_load; errno: 5; imm: off; once: off; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_load; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_load; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_load; errno: 28; imm: off; once: off; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_load; errno: 28; imm: off; once: off; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_update_part; errno: 5; imm: off; once: on; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_update_part; errno: 5; imm: off; once: on; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_update_part; errno: 5; imm: off; once: off; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_update_part; errno: 5; imm: off; once: off; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_update_part; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_update_part; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_update_part; errno: 28; imm: off; once: off; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_update_part; errno: 28; imm: off; once: off; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc; errno: 5; imm: off; once: on; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc; errno: 5; imm: off; once: on; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc; errno: 5; imm: off; once: off; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc; errno: 5; imm: off; once: off; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc; errno: 28; imm: off; once: off; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc; errno: 28; imm: off; once: off; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: cluster_alloc; errno: 5; imm: off; once: on; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: cluster_alloc; errno: 5; imm: off; once: on; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: cluster_alloc; errno: 5; imm: off; once: off; write +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: cluster_alloc; errno: 5; imm: off; once: off; write -b +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: cluster_alloc; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: cluster_alloc; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: cluster_alloc; errno: 28; imm: off; once: off; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: cluster_alloc; errno: 28; imm: off; once: off; write -b +write failed: No space left on device +No errors were found on the image. + +=== Refcout table growth tests === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write +write failed: No space left on device + +55 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write -b +write failed: No space left on device + +251 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write; errno: 28; imm: off; once: off; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write; errno: 28; imm: off; once: off; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write +write failed: No space left on device + +10 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write -b +write failed: No space left on device + +23 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write +write failed: No space left on device + +10 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write -b +write failed: No space left on device + +23 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write -b +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write +write failed: No space left on device + +10 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write -b +write failed: No space left on device + +23 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +=== L1 growth tests === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.alloc_table; errno: 5; imm: off; once: on +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.alloc_table; errno: 5; imm: off; once: off +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.alloc_table; errno: 28; imm: off; once: on +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.alloc_table; errno: 28; imm: off; once: off +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.write_table; errno: 5; imm: off; once: on +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.write_table; errno: 5; imm: off; once: off +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.write_table; errno: 28; imm: off; once: on +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.write_table; errno: 28; imm: off; once: off +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.activate_table; errno: 5; imm: off; once: on +write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.activate_table; errno: 5; imm: off; once: off +write failed: Input/output error + +96 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.activate_table; errno: 28; imm: off; once: on +write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +Event: l1_grow.activate_table; errno: 28; imm: off; once: off +write failed: No space left on device + +96 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +*** done 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/039.out b/tests/qemu-iotests/039.out index cb510d6716..077fa64cbf 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -12,8 +12,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) incompatible_features 0x1 -ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0 ERROR cluster 5 refcount=0 reference=1 +ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 2 errors were found on the image. Data may be corrupted, or further writes to the image may corrupt it. @@ -24,7 +24,6 @@ read 512/512 bytes at offset 0 incompatible_features 0x1 == Repairing the image file must succeed == -ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0 Repairing cluster 5 refcount=0 reference=1 The following inconsistencies were found and repaired: @@ -44,7 +43,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) incompatible_features 0x1 -ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0 Repairing cluster 5 refcount=0 reference=1 wrote 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/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 new file mode 100755 index 0000000000..2ab8f20e02 --- /dev/null +++ b/tests/qemu-iotests/063 @@ -0,0 +1,97 @@ +#!/bin/bash +# +# test of qemu-img convert -n - convert without creation +# +# Copyright (C) 2009 Red Hat, Inc. +# Copyright (C) 2013 Alex Bligh (alex@alex.org.uk) +# +# 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=alex@alex.org.uk + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_IMG.orig" "$TEST_IMG.raw" "$TEST_IMG.raw2" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +_supported_fmt qcow qcow2 vmdk qed raw +_supported_proto generic +_supported_os Linux + +_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 + 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 + 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 + exit 1 +fi + +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 + 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 + 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" +_make_test_img 2M +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" + +echo "*** done" +rm -f $seq.full +status=0 +exit 0 diff --git a/tests/qemu-iotests/063.out b/tests/qemu-iotests/063.out new file mode 100644 index 0000000000..de1c99afd8 --- /dev/null +++ b/tests/qemu-iotests/063.out @@ -0,0 +1,10 @@ +QA output created by 063 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== Testing conversion with -n fails with no target file == +== Testing conversion with -n succeeds with a target file == +== Testing conversion to raw is the same after conversion with -n == +== Testing conversion back to original format == +No errors were found on the image. +== Testing conversion to a smaller file fails == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152 +*** done diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 74628ae637..f5f328f5f5 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -78,50 +78,50 @@ _wrapup() if $showme then - : + : elif $needwrap then - if [ -f check.time -a -f $tmp.time ] - then - cat check.time $tmp.time \ - | $AWK_PROG ' - { t[$1] = $2 } -END { if (NR > 0) { - for (i in t) print i " " t[i] - } - }' \ - | sort -n >$tmp.out - mv $tmp.out check.time - fi - - if [ -f $tmp.expunged ] - then - notrun=`wc -l <$tmp.expunged | sed -e 's/ *//g'` - try=`expr $try - $notrun` - list=`echo "$list" | sed -f $tmp.expunged` - fi - - echo "" >>check.log - date >>check.log - echo $list | fmt | sed -e 's/^/ /' >>check.log - $interrupt && echo "Interrupted!" >>check.log - - if [ ! -z "$notrun" ] - then - echo "Not run:$notrun" - echo "Not run:$notrun" >>check.log - fi + if [ -f check.time -a -f $tmp.time ] + then + cat check.time $tmp.time \ + | $AWK_PROG ' + { t[$1] = $2 } +END { if (NR > 0) { + for (i in t) print i " " t[i] + } + }' \ + | sort -n >$tmp.out + mv $tmp.out check.time + fi + + if [ -f $tmp.expunged ] + then + notrun=`wc -l <$tmp.expunged | sed -e 's/ *//g'` + try=`expr $try - $notrun` + list=`echo "$list" | sed -f $tmp.expunged` + fi + + echo "" >>check.log + date >>check.log + echo $list | fmt | sed -e 's/^/ /' >>check.log + $interrupt && echo "Interrupted!" >>check.log + + if [ ! -z "$notrun" ] + then + echo "Not run:$notrun" + echo "Not run:$notrun" >>check.log + fi if [ ! -z "$n_bad" -a $n_bad != 0 ] - then - echo "Failures:$bad" - echo "Failed $n_bad of $try tests" - echo "Failures:$bad" | fmt >>check.log - echo "Failed $n_bad of $try tests" >>check.log - else - echo "Passed all $try tests" - echo "Passed all $try tests" >>check.log - fi - needwrap=false + then + echo "Failures:$bad" + echo "Failed $n_bad of $try tests" + echo "Failures:$bad" | fmt >>check.log + echo "Failed $n_bad of $try tests" >>check.log + else + echo "Passed all $try tests" + echo "Passed all $try tests" >>check.log + fi + needwrap=false fi rm -f /tmp/*.out /tmp/*.err /tmp/*.time @@ -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 @@ -185,82 +186,88 @@ do if $showme then - echo - continue - elif [ -f expunged ] && $expunge && egrep "^$seq([ ]|\$)" expunged >/dev/null + echo + continue + elif [ -f expunged ] && $expunge && egrep "^$seq([ ]|\$)" expunged >/dev/null then - echo " - expunged" - rm -f $seq.out.bad - echo "/^$seq\$/d" >>$tmp.expunged + echo " - expunged" + rm -f $seq.out.bad + echo "/^$seq\$/d" >>$tmp.expunged elif [ ! -f $seq ] then - echo " - no such test?" - echo "/^$seq\$/d" >>$tmp.expunged + echo " - no such test?" + echo "/^$seq\$/d" >>$tmp.expunged else - # really going to try and run this one - # - rm -f $seq.out.bad - lasttime=`sed -n -e "/^$seq /s/.* //p" <check.time` - if [ "X$lasttime" != X ]; then - echo -n " ${lasttime}s ..." - else - echo -n " " # prettier output with timestamps. - fi - rm -f core $seq.notrun - - # for hangcheck ... - echo "$seq" >/tmp/check.sts - - start=`_wallclock` - $timestamp && echo -n " ["`date "+%T"`"]" - [ ! -x $seq ] && chmod u+x $seq # ensure we can run it - MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \ - ./$seq >$tmp.out 2>&1 - sts=$? - $timestamp && _timestamp - stop=`_wallclock` - - if [ -f core ] - then - echo -n " [dumped core]" - mv core $seq.core - err=true - fi - - if [ -f $seq.notrun ] - then - $timestamp || echo -n " [not run] " - $timestamp && echo " [not run]" && echo -n " $seq -- " - cat $seq.notrun - notrun="$notrun $seq" - else - if [ $sts -ne 0 ] - then - echo -n " [failed, exit status $sts]" - err=true - fi - if [ ! -f $seq.out ] - then - echo " - no qualified output" - err=true - else - if diff -w $seq.out $tmp.out >/dev/null 2>&1 - then - echo "" - if $err - then - : - else - echo "$seq `expr $stop - $start`" >>$tmp.time - fi - else - echo " - output mismatch (see $seq.out.bad)" - mv $tmp.out $seq.out.bad - $diff -w $seq.out $seq.out.bad - err=true - fi - fi - fi + # really going to try and run this one + # + rm -f $seq.out.bad + lasttime=`sed -n -e "/^$seq /s/.* //p" <check.time` + if [ "X$lasttime" != X ]; then + echo -n " ${lasttime}s ..." + else + echo -n " " # prettier output with timestamps. + fi + rm -f core $seq.notrun + + # for hangcheck ... + echo "$seq" >/tmp/check.sts + + start=`_wallclock` + $timestamp && echo -n " ["`date "+%T"`"]" + [ ! -x $seq ] && chmod u+x $seq # ensure we can run it + MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \ + ./$seq >$tmp.out 2>&1 + sts=$? + $timestamp && _timestamp + stop=`_wallclock` + + if [ -f core ] + then + echo -n " [dumped core]" + mv core $seq.core + err=true + fi + + if [ -f $seq.notrun ] + then + $timestamp || echo -n " [not run] " + $timestamp && echo " [not run]" && echo -n " $seq -- " + cat $seq.notrun + notrun="$notrun $seq" + else + if [ $sts -ne 0 ] + then + echo -n " [failed, exit status $sts]" + err=true + fi + + reference=$seq.out + if (echo $QEMU_IO_OPTIONS | grep -s -- '--nocache' > /dev/null); then + [ -f $seq.out.nocache ] && reference=$seq.out.nocache + fi + + if [ ! -f $reference ] + then + echo " - no qualified output" + err=true + else + if diff -w $reference $tmp.out >/dev/null 2>&1 + then + echo "" + if $err + then + : + else + echo "$seq `expr $stop - $start`" >>$tmp.time + fi + else + echo " - output mismatch (see $seq.out.bad)" + mv $tmp.out $seq.out.bad + $diff -w $reference $seq.out.bad + err=true + fi + fi + fi fi @@ -268,12 +275,12 @@ do # if $err then - bad="$bad $seq" - n_bad=`expr $n_bad + 1` - quick=false + bad="$bad $seq" + n_bad=`expr $n_bad + 1` + quick=false fi [ -f $seq.notrun ] || try=`expr $try + 1` - + seq="after_$seq" done diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 6826ea72fe..fecaf85074 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -54,58 +54,58 @@ do if $group then - # arg after -g - group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ + # arg after -g + group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ s/ .*//p }'` - if [ -z "$group_list" ] - then - echo "Group \"$r\" is empty or not defined?" - exit 1 - fi - [ ! -s $tmp.list ] && touch $tmp.list - for t in $group_list - do - if grep -s "^$t\$" $tmp.list >/dev/null - then - : - else - echo "$t" >>$tmp.list - fi - done - group=false - continue + if [ -z "$group_list" ] + then + echo "Group \"$r\" is empty or not defined?" + exit 1 + fi + [ ! -s $tmp.list ] && touch $tmp.list + for t in $group_list + do + if grep -s "^$t\$" $tmp.list >/dev/null + then + : + else + echo "$t" >>$tmp.list + fi + done + group=false + continue elif $xgroup then - # arg after -x - [ ! -s $tmp.list ] && ls [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] >$tmp.list 2>/dev/null - group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ + # arg after -x + [ ! -s $tmp.list ] && ls [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] >$tmp.list 2>/dev/null + group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ s/ .*//p }'` - if [ -z "$group_list" ] - then - echo "Group \"$r\" is empty or not defined?" - exit 1 - fi - numsed=0 - rm -f $tmp.sed - for t in $group_list - do - if [ $numsed -gt 100 ] - then - sed -f $tmp.sed <$tmp.list >$tmp.tmp - mv $tmp.tmp $tmp.list - numsed=0 - rm -f $tmp.sed - fi - echo "/^$t\$/d" >>$tmp.sed - numsed=`expr $numsed + 1` - done - sed -f $tmp.sed <$tmp.list >$tmp.tmp - mv $tmp.tmp $tmp.list - xgroup=false - continue + if [ -z "$group_list" ] + then + echo "Group \"$r\" is empty or not defined?" + exit 1 + fi + numsed=0 + rm -f $tmp.sed + for t in $group_list + do + if [ $numsed -gt 100 ] + then + sed -f $tmp.sed <$tmp.list >$tmp.tmp + mv $tmp.tmp $tmp.list + numsed=0 + rm -f $tmp.sed + fi + echo "/^$t\$/d" >>$tmp.sed + numsed=`expr $numsed + 1` + done + sed -f $tmp.sed <$tmp.list >$tmp.tmp + mv $tmp.tmp $tmp.list + xgroup=false + continue elif $imgopts then @@ -119,11 +119,11 @@ s/ .*//p case "$r" in - -\? | -h | --help) # usage - echo "Usage: $0 [options] [testlist]"' + -\? | -h | --help) # usage + echo "Usage: $0 [options] [testlist]"' common options - -v verbose + -v verbose check options -raw test raw (default) @@ -138,162 +138,162 @@ check options -sheepdog test sheepdog -nbd test nbd -ssh test ssh - -xdiff graphical mode diff - -nocache use O_DIRECT on backing file - -misalign misalign memory allocations - -n show me, do not run tests + -xdiff graphical mode diff + -nocache use O_DIRECT on backing file + -misalign misalign memory allocations + -n show me, do not run tests -o options -o options to pass to qemu-img create/convert - -T output timestamps - -r randomize test order - + -T output timestamps + -r randomize test order + testlist options - -g group[,group...] include tests from these groups - -x group[,group...] exclude tests from these groups - NNN include test NNN - NNN-NNN include test range (eg. 012-021) + -g group[,group...] include tests from these groups + -x group[,group...] exclude tests from these groups + NNN include test NNN + NNN-NNN include test range (eg. 012-021) ' - exit 0 - ;; - - -raw) - IMGFMT=raw - xpand=false - ;; - - -cow) - IMGFMT=cow - xpand=false - ;; - - -qcow) - IMGFMT=qcow - xpand=false - ;; - - -qcow2) - IMGFMT=qcow2 - xpand=false - ;; - - -qed) - IMGFMT=qed - xpand=false - ;; - - -vdi) - IMGFMT=vdi - xpand=false - ;; - - -vmdk) - IMGFMT=vmdk - xpand=false - ;; - - -vpc) - IMGFMT=vpc - xpand=false - ;; - - -rbd) - IMGPROTO=rbd - xpand=false - ;; - -sheepdog) - IMGPROTO=sheepdog - xpand=false - ;; - -nbd) - IMGPROTO=nbd - xpand=false - ;; + exit 0 + ;; + + -raw) + IMGFMT=raw + xpand=false + ;; + + -cow) + IMGFMT=cow + xpand=false + ;; + + -qcow) + IMGFMT=qcow + xpand=false + ;; + + -qcow2) + IMGFMT=qcow2 + xpand=false + ;; + + -qed) + IMGFMT=qed + xpand=false + ;; + + -vdi) + IMGFMT=vdi + xpand=false + ;; + + -vmdk) + IMGFMT=vmdk + xpand=false + ;; + + -vpc) + IMGFMT=vpc + xpand=false + ;; + + -rbd) + IMGPROTO=rbd + xpand=false + ;; + -sheepdog) + IMGPROTO=sheepdog + xpand=false + ;; + -nbd) + IMGPROTO=nbd + xpand=false + ;; -ssh) IMGPROTO=ssh xpand=false ;; - -nocache) - QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --nocache" - xpand=false - ;; + -nocache) + QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --nocache" + xpand=false + ;; - -misalign) - QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --misalign" - xpand=false - ;; + -misalign) + QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --misalign" + xpand=false + ;; -valgrind) valgrind=true - xpand=false + xpand=false ;; - -g) # -g group ... pick from group file - group=true - xpand=false - ;; - - -xdiff) # graphical diff mode - xpand=false - - if [ ! -z "$DISPLAY" ] - then - which xdiff >/dev/null 2>&1 && diff=xdiff - which gdiff >/dev/null 2>&1 && diff=gdiff - which tkdiff >/dev/null 2>&1 && diff=tkdiff - which xxdiff >/dev/null 2>&1 && diff=xxdiff - fi - ;; - - -n) # show me, don't do it - showme=true - xpand=false - ;; + -g) # -g group ... pick from group file + group=true + xpand=false + ;; + + -xdiff) # graphical diff mode + xpand=false + + if [ ! -z "$DISPLAY" ] + then + which xdiff >/dev/null 2>&1 && diff=xdiff + which gdiff >/dev/null 2>&1 && diff=gdiff + which tkdiff >/dev/null 2>&1 && diff=tkdiff + which xxdiff >/dev/null 2>&1 && diff=xxdiff + fi + ;; + + -n) # show me, don't do it + showme=true + xpand=false + ;; -o) imgopts=true xpand=false ;; - -r) # randomize test order - randomize=true - xpand=false - ;; - - -T) # turn on timestamp output - timestamp=true - xpand=false - ;; - - -v) - verbose=true - xpand=false - ;; - -x) # -x group ... exclude from group file - xgroup=true - xpand=false - ;; - '[0-9][0-9][0-9] [0-9][0-9][0-9][0-9]') - echo "No tests?" - status=1 - exit $status - ;; - - [0-9]*-[0-9]*) - eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'` - ;; - - [0-9]*-) - eval `echo $r | sed -e 's/^/start=/' -e 's/-//'` - end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //'` - if [ -z "$end" ] - then - echo "No tests in range \"$r\"?" - status=1 - exit $status - fi - ;; - - *) - start=$r - end=$r - ;; + -r) # randomize test order + randomize=true + xpand=false + ;; + + -T) # turn on timestamp output + timestamp=true + xpand=false + ;; + + -v) + verbose=true + xpand=false + ;; + -x) # -x group ... exclude from group file + xgroup=true + xpand=false + ;; + '[0-9][0-9][0-9] [0-9][0-9][0-9][0-9]') + echo "No tests?" + status=1 + exit $status + ;; + + [0-9]*-[0-9]*) + eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'` + ;; + + [0-9]*-) + eval `echo $r | sed -e 's/^/start=/' -e 's/-//'` + end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //'` + if [ -z "$end" ] + then + echo "No tests in range \"$r\"?" + status=1 + exit $status + fi + ;; + + *) + start=$r + end=$r + ;; esac @@ -303,26 +303,26 @@ testlist options if $xpand then - have_test_arg=true - $AWK_PROG </dev/null ' -BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ - | while read id - do - if grep -s "^$id " group >/dev/null - then - # in group file ... OK - echo $id >>$tmp.list - else - if [ -f expunged ] && $expunge && egrep "^$id([ ]|\$)" expunged >/dev/null - then - # expunged ... will be reported, but not run, later - echo $id >>$tmp.list - else - # oops - echo "$id - unknown test, ignored" - fi - fi - done + have_test_arg=true + $AWK_PROG </dev/null ' +BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ + | while read id + do + if grep -s "^$id " group >/dev/null + then + # in group file ... OK + echo $id >>$tmp.list + else + if [ -f expunged ] && $expunge && egrep "^$id([ ]|\$)" expunged >/dev/null + then + # expunged ... will be reported, but not run, later + echo $id >>$tmp.list + else + # oops + echo "$id - unknown test, ignored" + fi + fi + done fi done @@ -337,11 +337,11 @@ then else if $have_test_arg then - # had test numbers, but none in group file ... do nothing - touch $tmp.list + # had test numbers, but none in group file ... do nothing + touch $tmp.list else - # no test numbers, do everything from group file - sed -n -e '/^[0-9][0-9][0-9]*/s/[ ].*//p' <group >$tmp.list + # no test numbers, do everything from group file + sed -n -e '/^[0-9][0-9][0-9]*/s/[ ].*//p' <group >$tmp.list fi fi diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index 08a3f100b8..d90a8bca8b 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -19,7 +19,7 @@ # setup and check for config parameters, and in particular # # EMAIL - email of the script runner. -# TEST_DIR - scratch test directory +# TEST_DIR - scratch test directory # # - These can be added to $HOST_CONFIG_DIR (witch default to ./config) # below or a separate local configuration file can be used (using @@ -111,11 +111,11 @@ export QEMU_NBD=$QEMU_NBD_PROG [ -f /etc/qemu-iotest.config ] && . /etc/qemu-iotest.config if [ -z "$TEST_DIR" ]; then - TEST_DIR=`pwd`/scratch + TEST_DIR=`pwd`/scratch fi if [ ! -e "$TEST_DIR" ]; then - mkdir "$TEST_DIR" + mkdir "$TEST_DIR" fi if [ ! -d "$TEST_DIR" ]; then @@ -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.filter b/tests/qemu-iotests/common.filter index 97a31ff0b1..5dfda63e59 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -25,19 +25,19 @@ # Outputs suitable message to stdout if it's not in range. # # A verbose option, -v, may be used as the LAST argument -# -# e.g. +# +# e.g. # foo: 0.0298 = 0.03 +/- 5% -# _within_tolerance "foo" 0.0298 0.03 5% -# +# _within_tolerance "foo" 0.0298 0.03 5% +# # foo: 0.0298 = 0.03 +/- 0.01 # _within_tolerance "foo" 0.0298 0.03 0.01 # # foo: 0.0298 = 0.03 -0.01 +0.002 # _within_tolerance "foo" 0.0298 0.03 0.01 0.002 # -# foo: verbose output of 0.0298 = 0.03 +/- 5% -# _within_tolerance "foo" 0.0298 0.03 5% -v +# foo: verbose output of 0.0298 = 0.03 +/- 5% +# _within_tolerance "foo" 0.0298 0.03 5% -v _within_tolerance() { _name=$1 @@ -51,10 +51,10 @@ _within_tolerance() # maxtol arg is optional # verbose arg is optional if [ $# -ge 5 ] - then + then if [ "$5" = "-v" ] then - _verbose=1 + _verbose=1 else _maxtol=$5 fi @@ -65,18 +65,18 @@ _within_tolerance() fi # find min with or without % - _mintolerance=`echo $_mintol | sed -e 's/%//'` + _mintolerance=`echo $_mintol | sed -e 's/%//'` if [ $_mintol = $_mintolerance ] - then + then _min=`echo "scale=5; $_correct_val-$_mintolerance" | bc` else _min=`echo "scale=5; $_correct_val-$_mintolerance*0.01*$_correct_val" | bc` fi # find max with or without % - _maxtolerance=`echo $_maxtol | sed -e 's/%//'` + _maxtolerance=`echo $_maxtol | sed -e 's/%//'` if [ $_maxtol = $_maxtolerance ] - then + then _max=`echo "scale=5; $_correct_val+$_maxtolerance" | bc` else _max=`echo "scale=5; $_correct_val+$_maxtolerance*0.01*$_correct_val" | bc` @@ -88,7 +88,7 @@ _within_tolerance() cat <<EOF >$tmp.bc.1 scale=5; if ($_min <= $_given_val) 1; -if ($_min > $_given_val) 0; +if ($_min > $_given_val) 0; EOF cat <<EOF >$tmp.bc.2 @@ -102,21 +102,21 @@ EOF rm -f $tmp.bc.[12] - _in_range=`expr $_above_min \& $_below_max` + _in_range=`expr $_above_min \& $_below_max` # fix up min, max precision for output # can vary for 5.3, 6.2 _min=`echo $_min | sed -e 's/0*$//'` # get rid of trailling zeroes _max=`echo $_max | sed -e 's/0*$//'` # get rid of trailling zeroes - if [ $_in_range -eq 1 ] + if [ $_in_range -eq 1 ] then - [ $_verbose -eq 1 ] && echo $_name is in range - return 0 + [ $_verbose -eq 1 ] && echo $_name is in range + return 0 else - [ $_verbose -eq 1 ] && echo $_name has value of $_given_val - [ $_verbose -eq 1 ] && echo $_name is NOT in range $_min .. $_max - return 1 + [ $_verbose -eq 1 ] && echo $_name has value of $_given_val + [ $_verbose -eq 1 ] && echo $_name is NOT in range $_min .. $_max + return 1 fi } @@ -125,7 +125,7 @@ EOF _filter_date() { sed \ - -e 's/[A-Z][a-z][a-z] [A-z][a-z][a-z] *[0-9][0-9]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9][0-9][0-9]$/DATE/' + -e 's/[A-Z][a-z][a-z] [A-z][a-z][a-z] *[0-9][0-9]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9][0-9][0-9]$/DATE/' } # replace occurrences of the actual TEST_DIR value with TEST_DIR diff --git a/tests/qemu-iotests/common.pattern b/tests/qemu-iotests/common.pattern index 85a40eecc0..00e0f605fd 100644 --- a/tests/qemu-iotests/common.pattern +++ b/tests/qemu-iotests/common.pattern @@ -106,8 +106,8 @@ function io_test2() { local num=$3 # Pattern (repeat after 9 clusters): - # used - used - free - used - compressed - compressed - - # free - free - compressed + # used - used - free - used - compressed - compressed - + # free - free - compressed # Write the clusters to be compressed echo === Clusters to be compressed [1] diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 5e077c3573..1b22db04f4 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -20,17 +20,17 @@ dd() { if [ "$HOSTOS" == "Linux" ] - then - command dd --help | grep noxfer > /dev/null 2>&1 - - if [ "$?" -eq 0 ] - then - command dd status=noxfer $@ - else - command dd $@ - fi + then + command dd --help | grep noxfer > /dev/null 2>&1 + + if [ "$?" -eq 0 ] + then + command dd status=noxfer $@ + else + command dd $@ + fi else - command dd $@ + command dd $@ fi } @@ -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" \ @@ -193,8 +209,8 @@ _get_pids_by_name() { if [ $# -ne 1 ] then - echo "Usage: _get_pids_by_name process-name" 1>&2 - exit 1 + echo "Usage: _get_pids_by_name process-name" 1>&2 + exit 1 fi # Algorithm ... all ps(1) variants have a time of the form MM:SS or @@ -206,12 +222,12 @@ _get_pids_by_name() ps $PS_ALL_FLAGS \ | sed -n \ - -e 's/$/ /' \ - -e 's/[ ][ ]*/ /g' \ - -e 's/^ //' \ - -e 's/^[^ ]* //' \ - -e "/[0-9]:[0-9][0-9] *[^ ]*\/$1 /s/ .*//p" \ - -e "/[0-9]:[0-9][0-9] *$1 /s/ .*//p" + -e 's/$/ /' \ + -e 's/[ ][ ]*/ /g' \ + -e 's/^ //' \ + -e 's/^[^ ]* //' \ + -e "/[0-9]:[0-9][0-9] *[^ ]*\/$1 /s/ .*//p" \ + -e "/[0-9]:[0-9][0-9] *$1 /s/ .*//p" } # fqdn for localhost @@ -229,8 +245,8 @@ _need_to_be_root() id=`id | $SED_PROG -e 's/(.*//' -e 's/.*=//'` if [ "$id" -ne 0 ] then - echo "Arrgh ... you need to be root (not uid=$id) to run this test" - exit 1 + echo "Arrgh ... you need to be root (not uid=$id) to run this test" + exit 1 fi } @@ -248,33 +264,33 @@ _need_to_be_root() _do() { if [ $# -eq 1 ]; then - _cmd=$1 + _cmd=$1 elif [ $# -eq 2 ]; then - _note=$1 - _cmd=$2 - echo -n "$_note... " + _note=$1 + _cmd=$2 + echo -n "$_note... " else - echo "Usage: _do [note] cmd" 1>&2 - status=1; exit + echo "Usage: _do [note] cmd" 1>&2 + status=1; exit fi (eval "echo '---' \"$_cmd\"") >>$here/$seq.full (eval "$_cmd") >$tmp._out 2>&1; ret=$? cat $tmp._out >>$here/$seq.full if [ $# -eq 2 ]; then - if [ $ret -eq 0 ]; then - echo "done" - else - echo "fail" - fi + if [ $ret -eq 0 ]; then + echo "done" + else + echo "fail" + fi fi if [ $ret -ne 0 ] \ - && [ "$_do_die_on_error" = "always" \ - -o \( $# -eq 2 -a "$_do_die_on_error" = "message_only" \) ] + && [ "$_do_die_on_error" = "always" \ + -o \( $# -eq 2 -a "$_do_die_on_error" = "message_only" \) ] then - [ $# -ne 2 ] && echo - eval "echo \"$_cmd\" failed \(returned $ret\): see $seq.full" - status=1; exit + [ $# -ne 2 ] && echo + eval "echo \"$_cmd\" failed \(returned $ret\): see $seq.full" + status=1; exit fi return $ret @@ -305,9 +321,9 @@ _fail() _supported_fmt() { for f; do - if [ "$f" = "$IMGFMT" -o "$f" = "generic" ]; then - return - fi + if [ "$f" = "$IMGFMT" -o "$f" = "generic" ]; then + return + fi done _notrun "not suitable for this image format: $IMGFMT" @@ -318,9 +334,9 @@ _supported_fmt() _supported_proto() { for f; do - if [ "$f" = "$IMGPROTO" -o "$f" = "generic" ]; then - return - fi + if [ "$f" = "$IMGPROTO" -o "$f" = "generic" ]; then + return + fi done _notrun "not suitable for this image protocol: $IMGPROTO" @@ -332,10 +348,10 @@ _supported_os() { for h do - if [ "$h" = "$HOSTOS" ] - then - return - fi + if [ "$h" = "$HOSTOS" ] + then + return + fi done _notrun "not suitable for this OS: $HOSTOS" diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index b6962421fa..1ad02e5a2c 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -63,6 +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-aio.c b/tests/test-aio.c index 532a1de3f9..c4fe0fc3b7 100644 --- a/tests/test-aio.c +++ b/tests/test-aio.c @@ -13,6 +13,7 @@ #include <glib.h> #include "block/aio.h" #include "qemu/timer.h" +#include "qemu/sockets.h" AioContext *ctx; @@ -375,7 +376,10 @@ static void test_timer_schedule(void) /* aio_poll will not block to wait for timers to complete unless it has * an fd to wait on. Fixing this breaks other tests. So create a dummy one. */ - g_assert(!pipe2(pipefd, O_NONBLOCK)); + g_assert(!qemu_pipe(pipefd)); + qemu_set_nonblock(pipefd[0]); + qemu_set_nonblock(pipefd[1]); + aio_set_fd_handler(ctx, pipefd[0], dummy_io_handler_read, NULL, NULL); aio_poll(ctx, false); @@ -716,7 +720,10 @@ static void test_source_timer_schedule(void) /* aio_poll will not block to wait for timers to complete unless it has * an fd to wait on. Fixing this breaks other tests. So create a dummy one. */ - g_assert(!pipe2(pipefd, O_NONBLOCK)); + g_assert(!qemu_pipe(pipefd)); + qemu_set_nonblock(pipefd[0]); + qemu_set_nonblock(pipefd[1]); + aio_set_fd_handler(ctx, pipefd[0], dummy_io_handler_read, NULL, NULL); do {} while (g_main_context_iteration(NULL, false)); 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/tests/test-throttle.c b/tests/test-throttle.c new file mode 100644 index 0000000000..760812645b --- /dev/null +++ b/tests/test-throttle.c @@ -0,0 +1,481 @@ +/* + * Throttle infrastructure tests + * + * Copyright Nodalink, SARL. 2013 + * + * Authors: + * BenoƮt Canet <benoit.canet@irqsave.net> + * + * 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 <glib.h> +#include <math.h> +#include "qemu/throttle.h" + +LeakyBucket bkt; +ThrottleConfig cfg; +ThrottleState ts; + +/* usefull function */ +static bool double_cmp(double x, double y) +{ + return fabsl(x - y) < 1e-6; +} + +/* tests for single bucket operations */ +static void test_leak_bucket(void) +{ + /* set initial value */ + bkt.avg = 150; + bkt.max = 15; + bkt.level = 1.5; + + /* leak an op work of time */ + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); + g_assert(bkt.avg == 150); + g_assert(bkt.max == 15); + g_assert(double_cmp(bkt.level, 0.5)); + + /* leak again emptying the bucket */ + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); + g_assert(bkt.avg == 150); + g_assert(bkt.max == 15); + g_assert(double_cmp(bkt.level, 0)); + + /* check that the bucket level won't go lower */ + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); + g_assert(bkt.avg == 150); + g_assert(bkt.max == 15); + g_assert(double_cmp(bkt.level, 0)); +} + +static void test_compute_wait(void) +{ + int64_t wait; + int64_t result; + + /* no operation limit set */ + bkt.avg = 0; + bkt.max = 15; + bkt.level = 1.5; + wait = throttle_compute_wait(&bkt); + g_assert(!wait); + + /* zero delta */ + bkt.avg = 150; + bkt.max = 15; + bkt.level = 15; + wait = throttle_compute_wait(&bkt); + g_assert(!wait); + + /* below zero delta */ + bkt.avg = 150; + bkt.max = 15; + bkt.level = 9; + wait = throttle_compute_wait(&bkt); + g_assert(!wait); + + /* half an operation above max */ + bkt.avg = 150; + bkt.max = 15; + bkt.level = 15.5; + wait = throttle_compute_wait(&bkt); + /* time required to do half an operation */ + result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2; + g_assert(wait == result); +} + +/* functions to test ThrottleState initialization/destroy methods */ +static void read_timer_cb(void *opaque) +{ +} + +static void write_timer_cb(void *opaque) +{ +} + +static void test_init(void) +{ + int i; + + /* fill the structure with crap */ + memset(&ts, 1, sizeof(ts)); + + /* init the structure */ + throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts); + + /* check initialized fields */ + g_assert(ts.clock_type == QEMU_CLOCK_VIRTUAL); + g_assert(ts.timers[0]); + g_assert(ts.timers[1]); + + /* check other fields where cleared */ + g_assert(!ts.previous_leak); + g_assert(!ts.cfg.op_size); + for (i = 0; i < BUCKETS_COUNT; i++) { + g_assert(!ts.cfg.buckets[i].avg); + g_assert(!ts.cfg.buckets[i].max); + g_assert(!ts.cfg.buckets[i].level); + } + + throttle_destroy(&ts); +} + +static void test_destroy(void) +{ + int i; + throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts); + throttle_destroy(&ts); + for (i = 0; i < 2; i++) { + g_assert(!ts.timers[i]); + } +} + +/* function to test throttle_config and throttle_get_config */ +static void test_config_functions(void) +{ + int i; + ThrottleConfig orig_cfg, final_cfg; + + orig_cfg.buckets[THROTTLE_BPS_TOTAL].avg = 153; + orig_cfg.buckets[THROTTLE_BPS_READ].avg = 56; + orig_cfg.buckets[THROTTLE_BPS_WRITE].avg = 1; + + orig_cfg.buckets[THROTTLE_OPS_TOTAL].avg = 150; + orig_cfg.buckets[THROTTLE_OPS_READ].avg = 69; + orig_cfg.buckets[THROTTLE_OPS_WRITE].avg = 23; + + orig_cfg.buckets[THROTTLE_BPS_TOTAL].max = 0; /* should be corrected */ + orig_cfg.buckets[THROTTLE_BPS_READ].max = 1; /* should not be corrected */ + orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120; + + orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150; + orig_cfg.buckets[THROTTLE_OPS_READ].max = 400; + orig_cfg.buckets[THROTTLE_OPS_WRITE].max = 500; + + orig_cfg.buckets[THROTTLE_BPS_TOTAL].level = 45; + orig_cfg.buckets[THROTTLE_BPS_READ].level = 65; + orig_cfg.buckets[THROTTLE_BPS_WRITE].level = 23; + + orig_cfg.buckets[THROTTLE_OPS_TOTAL].level = 1; + orig_cfg.buckets[THROTTLE_OPS_READ].level = 90; + orig_cfg.buckets[THROTTLE_OPS_WRITE].level = 75; + + orig_cfg.op_size = 1; + + throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts); + /* structure reset by throttle_init previous_leak should be null */ + g_assert(!ts.previous_leak); + throttle_config(&ts, &orig_cfg); + + /* has previous leak been initialized by throttle_config ? */ + g_assert(ts.previous_leak); + + /* get back the fixed configuration */ + throttle_get_config(&ts, &final_cfg); + + throttle_destroy(&ts); + + g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153); + g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56); + g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].avg == 1); + + g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].avg == 150); + g_assert(final_cfg.buckets[THROTTLE_OPS_READ].avg == 69); + g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].avg == 23); + + g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].max == 15.3);/* fixed */ + g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max == 1); /* not fixed */ + g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120); + + g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150); + g_assert(final_cfg.buckets[THROTTLE_OPS_READ].max == 400); + g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].max == 500); + + g_assert(final_cfg.op_size == 1); + + /* check bucket have been cleared */ + for (i = 0; i < BUCKETS_COUNT; i++) { + g_assert(!final_cfg.buckets[i].level); + } +} + +/* functions to test is throttle is enabled by a config */ +static void set_cfg_value(bool is_max, int index, int value) +{ + if (is_max) { + cfg.buckets[index].max = value; + } else { + cfg.buckets[index].avg = value; + } +} + +static void test_enabled(void) +{ + int i; + + memset(&cfg, 0, sizeof(cfg)); + g_assert(!throttle_enabled(&cfg)); + + for (i = 0; i < BUCKETS_COUNT; i++) { + memset(&cfg, 0, sizeof(cfg)); + set_cfg_value(false, i, 150); + g_assert(throttle_enabled(&cfg)); + } + + for (i = 0; i < BUCKETS_COUNT; i++) { + memset(&cfg, 0, sizeof(cfg)); + set_cfg_value(false, i, -150); + g_assert(!throttle_enabled(&cfg)); + } +} + +/* tests functions for throttle_conflicting */ + +static void test_conflicts_for_one_set(bool is_max, + int total, + int read, + int write) +{ + memset(&cfg, 0, sizeof(cfg)); + g_assert(!throttle_conflicting(&cfg)); + + set_cfg_value(is_max, total, 1); + set_cfg_value(is_max, read, 1); + g_assert(throttle_conflicting(&cfg)); + + memset(&cfg, 0, sizeof(cfg)); + set_cfg_value(is_max, total, 1); + set_cfg_value(is_max, write, 1); + g_assert(throttle_conflicting(&cfg)); + + memset(&cfg, 0, sizeof(cfg)); + set_cfg_value(is_max, total, 1); + set_cfg_value(is_max, read, 1); + set_cfg_value(is_max, write, 1); + g_assert(throttle_conflicting(&cfg)); + + memset(&cfg, 0, sizeof(cfg)); + set_cfg_value(is_max, total, 1); + g_assert(!throttle_conflicting(&cfg)); + + memset(&cfg, 0, sizeof(cfg)); + set_cfg_value(is_max, read, 1); + set_cfg_value(is_max, write, 1); + g_assert(!throttle_conflicting(&cfg)); +} + +static void test_conflicting_config(void) +{ + /* bps average conflicts */ + test_conflicts_for_one_set(false, + THROTTLE_BPS_TOTAL, + THROTTLE_BPS_READ, + THROTTLE_BPS_WRITE); + + /* ops average conflicts */ + test_conflicts_for_one_set(false, + THROTTLE_OPS_TOTAL, + THROTTLE_OPS_READ, + THROTTLE_OPS_WRITE); + + /* bps average conflicts */ + test_conflicts_for_one_set(true, + THROTTLE_BPS_TOTAL, + THROTTLE_BPS_READ, + THROTTLE_BPS_WRITE); + /* ops average conflicts */ + test_conflicts_for_one_set(true, + THROTTLE_OPS_TOTAL, + THROTTLE_OPS_READ, + THROTTLE_OPS_WRITE); +} +/* functions to test the throttle_is_valid function */ +static void test_is_valid_for_value(int value, bool should_be_valid) +{ + int is_max, index; + for (is_max = 0; is_max < 2; is_max++) { + for (index = 0; index < BUCKETS_COUNT; index++) { + memset(&cfg, 0, sizeof(cfg)); + set_cfg_value(is_max, index, value); + g_assert(throttle_is_valid(&cfg) == should_be_valid); + } + } +} + +static void test_is_valid(void) +{ + /* negative number are invalid */ + test_is_valid_for_value(-1, false); + /* zero are valids */ + test_is_valid_for_value(0, true); + /* positives numers are valids */ + test_is_valid_for_value(1, true); +} + +static void test_have_timer(void) +{ + /* zero the structure */ + memset(&ts, 0, sizeof(ts)); + + /* no timer set shoudl return false */ + g_assert(!throttle_have_timer(&ts)); + + /* init the structure */ + throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts); + + /* timer set by init should return true */ + g_assert(throttle_have_timer(&ts)); + + throttle_destroy(&ts); +} + +static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ + int size, /* size of the operation to do */ + double avg, /* io limit */ + uint64_t op_size, /* ideal size of an io */ + double total_result, + double read_result, + double write_result) +{ + BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL, + THROTTLE_BPS_READ, + THROTTLE_BPS_WRITE, }, + { THROTTLE_OPS_TOTAL, + THROTTLE_OPS_READ, + THROTTLE_OPS_WRITE, } }; + ThrottleConfig cfg; + BucketType index; + int i; + + for (i = 0; i < 3; i++) { + BucketType index = to_test[is_ops][i]; + cfg.buckets[index].avg = avg; + } + + cfg.op_size = op_size; + + throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts); + throttle_config(&ts, &cfg); + + /* account a read */ + throttle_account(&ts, false, size); + /* account a write */ + throttle_account(&ts, true, size); + + /* check total result */ + index = to_test[is_ops][0]; + if (!double_cmp(ts.cfg.buckets[index].level, total_result)) { + return false; + } + + /* check read result */ + index = to_test[is_ops][1]; + if (!double_cmp(ts.cfg.buckets[index].level, read_result)) { + return false; + } + + /* check write result */ + index = to_test[is_ops][2]; + if (!double_cmp(ts.cfg.buckets[index].level, write_result)) { + return false; + } + + throttle_destroy(&ts); + + return true; +} + +static void test_accounting(void) +{ + /* tests for bps */ + + /* op of size 1 */ + g_assert(do_test_accounting(false, + 1 * 512, + 150, + 0, + 1024, + 512, + 512)); + + /* op of size 2 */ + g_assert(do_test_accounting(false, + 2 * 512, + 150, + 0, + 2048, + 1024, + 1024)); + + /* op of size 2 and orthogonal parameter change */ + g_assert(do_test_accounting(false, + 2 * 512, + 150, + 17, + 2048, + 1024, + 1024)); + + + /* tests for ops */ + + /* op of size 1 */ + g_assert(do_test_accounting(true, + 1 * 512, + 150, + 0, + 2, + 1, + 1)); + + /* op of size 2 */ + g_assert(do_test_accounting(true, + 2 * 512, + 150, + 0, + 2, + 1, + 1)); + + /* jumbo op accounting fragmentation : size 64 with op size of 13 units */ + g_assert(do_test_accounting(true, + 64 * 512, + 150, + 13 * 512, + (64.0 * 2) / 13, + (64.0 / 13), + (64.0 / 13))); + + /* same with orthogonal parameters changes */ + g_assert(do_test_accounting(true, + 64 * 512, + 300, + 13 * 512, + (64.0 * 2) / 13, + (64.0 / 13), + (64.0 / 13))); +} + +int main(int argc, char **argv) +{ + init_clocks(); + do {} while (g_main_context_iteration(NULL, false)); + + /* tests in the same order as the header function declarations */ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/throttle/leak_bucket", test_leak_bucket); + g_test_add_func("/throttle/compute_wait", test_compute_wait); + g_test_add_func("/throttle/init", test_init); + g_test_add_func("/throttle/destroy", test_destroy); + g_test_add_func("/throttle/have_timer", test_have_timer); + g_test_add_func("/throttle/config/enabled", test_enabled); + g_test_add_func("/throttle/config/conflicting", test_conflicting_config); + g_test_add_func("/throttle/config/is_valid", test_is_valid); + g_test_add_func("/throttle/config_functions", test_config_functions); + g_test_add_func("/throttle/accounting", test_accounting); + return g_test_run(); +} + diff --git a/trace-events b/trace-events index 8285c5a192..8695e9e5b7 100644 --- a/trace-events +++ b/trace-events @@ -1059,7 +1059,7 @@ qxl_io_destroy_primary_ignored(int qid, const char *mode) "%d %s" qxl_io_log(int qid, const uint8_t *log_buf) "%d %s" qxl_io_read_unexpected(int qid) "%d" qxl_io_unexpected_vga_mode(int qid, uint64_t addr, uint64_t val, const char *desc) "%d 0x%"PRIx64"=%"PRIu64" (%s)" -qxl_io_write(int qid, const char *mode, uint64_t addr, uint64_t val, unsigned size, int async) "%d %s addr=%"PRIu64 " val=%"PRIu64" size=%u async=%d" +qxl_io_write(int qid, const char *mode, uint64_t addr, const char *aname, uint64_t val, unsigned size, int async) "%d %s addr=%"PRIu64 " (%s) val=%"PRIu64" size=%u async=%d" qxl_memslot_add_guest(int qid, uint32_t slot_id, uint64_t guest_start, uint64_t guest_end) "%d %u: guest phys 0x%"PRIx64 " - 0x%" PRIx64 qxl_post_load(int qid, const char *mode) "%d %s" qxl_pre_load(int qid) "%d" @@ -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/ui/spice-core.c b/ui/spice-core.c index 3a2cd7e0c6..33ef83731a 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -511,7 +511,9 @@ SpiceInfo *qmp_query_spice(Error **errp) int port, tls_port; const char *addr; SpiceInfo *info; - char version_string[20]; /* 12 = |255.255.255\0| is the max */ + unsigned int major; + unsigned int minor; + unsigned int micro; info = g_malloc0(sizeof(*info)); @@ -534,11 +536,10 @@ SpiceInfo *qmp_query_spice(Error **errp) info->host = g_strdup(addr ? addr : "0.0.0.0"); info->has_compiled_version = true; - snprintf(version_string, sizeof(version_string), "%d.%d.%d", - (SPICE_SERVER_VERSION & 0xff0000) >> 16, - (SPICE_SERVER_VERSION & 0xff00) >> 8, - SPICE_SERVER_VERSION & 0xff); - info->compiled_version = g_strdup(version_string); + major = (SPICE_SERVER_VERSION & 0xff0000) >> 16; + minor = (SPICE_SERVER_VERSION & 0xff00) >> 8; + micro = SPICE_SERVER_VERSION & 0xff; + info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro); if (port) { info->has_port = true; @@ -640,7 +641,7 @@ void qemu_spice_init(void) char *x509_key_file = NULL, *x509_cert_file = NULL, *x509_cacert_file = NULL; - int port, tls_port, len, addr_flags; + int port, tls_port, addr_flags; spice_image_compression_t compression; spice_wan_compression_t wan_compr; bool seamless_migration; @@ -671,30 +672,29 @@ void qemu_spice_init(void) if (NULL == x509_dir) { x509_dir = "."; } - len = strlen(x509_dir) + 32; str = qemu_opt_get(opts, "x509-key-file"); if (str) { x509_key_file = g_strdup(str); } else { - x509_key_file = g_malloc(len); - snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE); + x509_key_file = g_strdup_printf("%s/%s", x509_dir, + X509_SERVER_KEY_FILE); } str = qemu_opt_get(opts, "x509-cert-file"); if (str) { x509_cert_file = g_strdup(str); } else { - x509_cert_file = g_malloc(len); - snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE); + x509_cert_file = g_strdup_printf("%s/%s", x509_dir, + X509_SERVER_CERT_FILE); } str = qemu_opt_get(opts, "x509-cacert-file"); if (str) { x509_cacert_file = g_strdup(str); } else { - x509_cacert_file = g_malloc(len); - snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE); + x509_cacert_file = g_strdup_printf("%s/%s", x509_dir, + X509_CA_CERT_FILE); } x509_key_password = qemu_opt_get(opts, "x509-key-password"); diff --git a/util/Makefile.objs b/util/Makefile.objs index dc72ab0721..2bb13a2a59 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -11,3 +11,4 @@ util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o uri.o notify.o util-obj-y += qemu-option.o qemu-progress.o util-obj-y += hexdump.o util-obj-y += crc32c.o +util-obj-y += throttle.o diff --git a/util/error.c b/util/error.c index 53b04354ae..ec0faa6176 100644 --- a/util/error.c +++ b/util/error.c @@ -76,6 +76,41 @@ void error_setg_file_open(Error **errp, int os_errno, const char *filename) error_setg_errno(errp, os_errno, "Could not open '%s'", filename); } +#ifdef _WIN32 + +void error_set_win32(Error **errp, int win32_err, ErrorClass err_class, + const char *fmt, ...) +{ + Error *err; + char *msg1; + va_list ap; + + if (errp == NULL) { + return; + } + assert(*errp == NULL); + + err = g_malloc0(sizeof(*err)); + + va_start(ap, fmt); + msg1 = g_strdup_vprintf(fmt, ap); + if (win32_err != 0) { + char *msg2 = g_win32_error_message(win32_err); + err->msg = g_strdup_printf("%s: %s (error: %x)", msg1, msg2, + (unsigned)win32_err); + g_free(msg2); + g_free(msg1); + } else { + err->msg = msg1; + } + va_end(ap); + err->err_class = err_class; + + *errp = err; +} + +#endif + Error *error_copy(const Error *err) { Error *err_new; 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..e00a44c86f 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; @@ -159,6 +157,18 @@ void qemu_set_nonblock(int fd) fcntl(fd, F_SETFL, f | O_NONBLOCK); } +int socket_set_fast_reuse(int fd) +{ + int val = 1, ret; + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const char *)&val, sizeof(val)); + + assert(ret == 0); + + return ret; +} + void qemu_set_cloexec(int fd) { int f; diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 961fbf5e3d..776ccfaaf0 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; } @@ -127,6 +124,16 @@ void qemu_set_nonblock(int fd) qemu_fd_register(fd); } +int socket_set_fast_reuse(int fd) +{ + /* Enabling the reuse of an endpoint that was used by a socket still in + * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows + * fast reuse is the default and SO_REUSEADDR does strange things. So we + * don't have to do anything here. More info can be found at: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */ + return 0; +} + int inet_aton(const char *cp, struct in_addr *ia) { uint32_t addr = inet_addr(cp); diff --git a/util/path.c b/util/path.c index f0c69627c7..623219e4c5 100644 --- a/util/path.c +++ b/util/path.c @@ -39,7 +39,7 @@ static int strneq(const char *s1, unsigned int n, const char *s2) } static struct pathelem *add_entry(struct pathelem *root, const char *name, - unsigned char type); + unsigned type); static struct pathelem *new_entry(const char *root, struct pathelem *parent, @@ -82,7 +82,7 @@ static struct pathelem *add_dir_maybe(struct pathelem *path) } static struct pathelem *add_entry(struct pathelem *root, const char *name, - unsigned char type) + unsigned type) { struct pathelem **e; diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 095716ecdb..6b97dc11f9 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -155,7 +155,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp) continue; } - qemu_setsockopt(slisten, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + socket_set_fast_reuse(slisten); #ifdef IPV6_V6ONLY if (e->ai_family == PF_INET6) { /* listen on both ipv4 and ipv6 */ @@ -274,7 +274,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED); return -1; } - qemu_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + socket_set_fast_reuse(sock); if (connect_state != NULL) { qemu_set_nonblock(sock); } @@ -455,7 +455,7 @@ int inet_dgram_opts(QemuOpts *opts, Error **errp) error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED); goto err; } - qemu_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + socket_set_fast_reuse(sock); /* bind socket */ if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) { diff --git a/util/throttle.c b/util/throttle.c new file mode 100644 index 0000000000..02e6f15587 --- /dev/null +++ b/util/throttle.c @@ -0,0 +1,396 @@ +/* + * QEMU throttling infrastructure + * + * Copyright (C) Nodalink, SARL. 2013 + * + * Author: + * BenoƮt Canet <benoit.canet@irqsave.net> + * + * 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/>. + */ + +#include "qemu/throttle.h" +#include "qemu/timer.h" + +/* This function make a bucket leak + * + * @bkt: the bucket to make leak + * @delta_ns: the time delta + */ +void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta_ns) +{ + double leak; + + /* compute how much to leak */ + leak = (bkt->avg * (double) delta_ns) / NANOSECONDS_PER_SECOND; + + /* make the bucket leak */ + bkt->level = MAX(bkt->level - leak, 0); +} + +/* Calculate the time delta since last leak and make proportionals leaks + * + * @now: the current timestamp in ns + */ +static void throttle_do_leak(ThrottleState *ts, int64_t now) +{ + /* compute the time elapsed since the last leak */ + int64_t delta_ns = now - ts->previous_leak; + int i; + + ts->previous_leak = now; + + if (delta_ns <= 0) { + return; + } + + /* make each bucket leak */ + for (i = 0; i < BUCKETS_COUNT; i++) { + throttle_leak_bucket(&ts->cfg.buckets[i], delta_ns); + } +} + +/* do the real job of computing the time to wait + * + * @limit: the throttling limit + * @extra: the number of operation to delay + * @ret: the time to wait in ns + */ +static int64_t throttle_do_compute_wait(double limit, double extra) +{ + double wait = extra * NANOSECONDS_PER_SECOND; + wait /= limit; + return wait; +} + +/* This function compute the wait time in ns that a leaky bucket should trigger + * + * @bkt: the leaky bucket we operate on + * @ret: the resulting wait time in ns or 0 if the operation can go through + */ +int64_t throttle_compute_wait(LeakyBucket *bkt) +{ + double extra; /* the number of extra units blocking the io */ + + if (!bkt->avg) { + return 0; + } + + extra = bkt->level - bkt->max; + + if (extra <= 0) { + return 0; + } + + return throttle_do_compute_wait(bkt->avg, extra); +} + +/* This function compute the time that must be waited while this IO + * + * @is_write: true if the current IO is a write, false if it's a read + * @ret: time to wait + */ +static int64_t throttle_compute_wait_for(ThrottleState *ts, + bool is_write) +{ + BucketType to_check[2][4] = { {THROTTLE_BPS_TOTAL, + THROTTLE_OPS_TOTAL, + THROTTLE_BPS_READ, + THROTTLE_OPS_READ}, + {THROTTLE_BPS_TOTAL, + THROTTLE_OPS_TOTAL, + THROTTLE_BPS_WRITE, + THROTTLE_OPS_WRITE}, }; + int64_t wait, max_wait = 0; + int i; + + for (i = 0; i < 4; i++) { + BucketType index = to_check[is_write][i]; + wait = throttle_compute_wait(&ts->cfg.buckets[index]); + if (wait > max_wait) { + max_wait = wait; + } + } + + return max_wait; +} + +/* compute the timer for this type of operation + * + * @is_write: the type of operation + * @now: the current clock timestamp + * @next_timestamp: the resulting timer + * @ret: true if a timer must be set + */ +bool throttle_compute_timer(ThrottleState *ts, + bool is_write, + int64_t now, + int64_t *next_timestamp) +{ + int64_t wait; + + /* leak proportionally to the time elapsed */ + throttle_do_leak(ts, now); + + /* compute the wait time if any */ + wait = throttle_compute_wait_for(ts, is_write); + + /* if the code must wait compute when the next timer should fire */ + if (wait) { + *next_timestamp = now + wait; + return true; + } + + /* else no need to wait at all */ + *next_timestamp = now; + return false; +} + +/* To be called first on the ThrottleState */ +void throttle_init(ThrottleState *ts, + QEMUClockType clock_type, + QEMUTimerCB *read_timer_cb, + QEMUTimerCB *write_timer_cb, + void *timer_opaque) +{ + memset(ts, 0, sizeof(ThrottleState)); + + ts->clock_type = clock_type; + ts->timers[0] = timer_new_ns(clock_type, read_timer_cb, timer_opaque); + ts->timers[1] = timer_new_ns(clock_type, write_timer_cb, timer_opaque); +} + +/* destroy a timer */ +static void throttle_timer_destroy(QEMUTimer **timer) +{ + assert(*timer != NULL); + + timer_del(*timer); + timer_free(*timer); + *timer = NULL; +} + +/* To be called last on the ThrottleState */ +void throttle_destroy(ThrottleState *ts) +{ + int i; + + for (i = 0; i < 2; i++) { + throttle_timer_destroy(&ts->timers[i]); + } +} + +/* is any throttling timer configured */ +bool throttle_have_timer(ThrottleState *ts) +{ + if (ts->timers[0]) { + return true; + } + + return false; +} + +/* Does any throttling must be done + * + * @cfg: the throttling configuration to inspect + * @ret: true if throttling must be done else false + */ +bool throttle_enabled(ThrottleConfig *cfg) +{ + int i; + + for (i = 0; i < BUCKETS_COUNT; i++) { + if (cfg->buckets[i].avg > 0) { + return true; + } + } + + return false; +} + +/* return true if any two throttling parameters conflicts + * + * @cfg: the throttling configuration to inspect + * @ret: true if any conflict detected else false + */ +bool throttle_conflicting(ThrottleConfig *cfg) +{ + bool bps_flag, ops_flag; + bool bps_max_flag, ops_max_flag; + + bps_flag = cfg->buckets[THROTTLE_BPS_TOTAL].avg && + (cfg->buckets[THROTTLE_BPS_READ].avg || + cfg->buckets[THROTTLE_BPS_WRITE].avg); + + ops_flag = cfg->buckets[THROTTLE_OPS_TOTAL].avg && + (cfg->buckets[THROTTLE_OPS_READ].avg || + cfg->buckets[THROTTLE_OPS_WRITE].avg); + + bps_max_flag = cfg->buckets[THROTTLE_BPS_TOTAL].max && + (cfg->buckets[THROTTLE_BPS_READ].max || + cfg->buckets[THROTTLE_BPS_WRITE].max); + + ops_max_flag = cfg->buckets[THROTTLE_OPS_TOTAL].max && + (cfg->buckets[THROTTLE_OPS_READ].max || + cfg->buckets[THROTTLE_OPS_WRITE].max); + + return bps_flag || ops_flag || bps_max_flag || ops_max_flag; +} + +/* check if a throttling configuration is valid + * @cfg: the throttling configuration to inspect + * @ret: true if valid else false + */ +bool throttle_is_valid(ThrottleConfig *cfg) +{ + bool invalid = false; + int i; + + for (i = 0; i < BUCKETS_COUNT; i++) { + if (cfg->buckets[i].avg < 0) { + invalid = true; + } + } + + for (i = 0; i < BUCKETS_COUNT; i++) { + if (cfg->buckets[i].max < 0) { + invalid = true; + } + } + + return !invalid; +} + +/* fix bucket parameters */ +static void throttle_fix_bucket(LeakyBucket *bkt) +{ + double min; + + /* zero bucket level */ + bkt->level = 0; + + /* The following is done to cope with the Linux CFQ block scheduler + * which regroup reads and writes by block of 100ms in the guest. + * When they are two process one making reads and one making writes cfq + * make a pattern looking like the following: + * WWWWWWWWWWWRRRRRRRRRRRRRRWWWWWWWWWWWWWwRRRRRRRRRRRRRRRRR + * Having a max burst value of 100ms of the average will help smooth the + * throttling + */ + min = bkt->avg / 10; + if (bkt->avg && !bkt->max) { + bkt->max = min; + } +} + +/* take care of canceling a timer */ +static void throttle_cancel_timer(QEMUTimer *timer) +{ + assert(timer != NULL); + + timer_del(timer); +} + +/* Used to configure the throttle + * + * @ts: the throttle state we are working on + * @cfg: the config to set + */ +void throttle_config(ThrottleState *ts, ThrottleConfig *cfg) +{ + int i; + + ts->cfg = *cfg; + + for (i = 0; i < BUCKETS_COUNT; i++) { + throttle_fix_bucket(&ts->cfg.buckets[i]); + } + + ts->previous_leak = qemu_clock_get_ns(ts->clock_type); + + for (i = 0; i < 2; i++) { + throttle_cancel_timer(ts->timers[i]); + } +} + +/* used to get config + * + * @ts: the throttle state we are working on + * @cfg: the config to write + */ +void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg) +{ + *cfg = ts->cfg; +} + + +/* Schedule the read or write timer if needed + * + * NOTE: this function is not unit tested due to it's usage of timer_mod + * + * @is_write: the type of operation (read/write) + * @ret: true if the timer has been scheduled else false + */ +bool throttle_schedule_timer(ThrottleState *ts, bool is_write) +{ + int64_t now = qemu_clock_get_ns(ts->clock_type); + int64_t next_timestamp; + bool must_wait; + + must_wait = throttle_compute_timer(ts, + is_write, + now, + &next_timestamp); + + /* request not throttled */ + if (!must_wait) { + return false; + } + + /* request throttled and timer pending -> do nothing */ + if (timer_pending(ts->timers[is_write])) { + return true; + } + + /* request throttled and timer not pending -> arm timer */ + timer_mod(ts->timers[is_write], next_timestamp); + return true; +} + +/* do the accounting for this operation + * + * @is_write: the type of operation (read/write) + * @size: the size of the operation + */ +void throttle_account(ThrottleState *ts, bool is_write, uint64_t size) +{ + double units = 1.0; + + /* if cfg.op_size is defined and smaller than size we compute unit count */ + if (ts->cfg.op_size && size > ts->cfg.op_size) { + units = (double) size / ts->cfg.op_size; + } + + ts->cfg.buckets[THROTTLE_BPS_TOTAL].level += size; + ts->cfg.buckets[THROTTLE_OPS_TOTAL].level += units; + + if (is_write) { + ts->cfg.buckets[THROTTLE_BPS_WRITE].level += size; + ts->cfg.buckets[THROTTLE_OPS_WRITE].level += units; + } else { + ts->cfg.buckets[THROTTLE_BPS_READ].level += size; + ts->cfg.buckets[THROTTLE_OPS_READ].level += units; + } +} + @@ -254,6 +254,7 @@ uint64_t node_mem[MAX_NODES]; unsigned long *node_cpumask[MAX_NODES]; uint8_t qemu_uuid[16]; +bool qemu_uuid_set; static QEMUBootSetHandler *boot_set_handler; static void *boot_set_opaque; @@ -843,45 +844,6 @@ static int nb_hcis; static int cur_hci; static struct HCIInfo *hci_table[MAX_NICS]; -static struct bt_vlan_s { - struct bt_scatternet_s net; - int id; - struct bt_vlan_s *next; -} *first_bt_vlan; - -/* find or alloc a new bluetooth "VLAN" */ -static struct bt_scatternet_s *qemu_find_bt_vlan(int id) -{ - struct bt_vlan_s **pvlan, *vlan; - for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) { - if (vlan->id == id) - return &vlan->net; - } - vlan = g_malloc0(sizeof(struct bt_vlan_s)); - vlan->id = id; - pvlan = &first_bt_vlan; - while (*pvlan != NULL) - pvlan = &(*pvlan)->next; - *pvlan = vlan; - return &vlan->net; -} - -static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len) -{ -} - -static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr) -{ - return -ENOTSUP; -} - -static struct HCIInfo null_hci = { - .cmd_send = null_hci_send, - .sco_send = null_hci_send, - .acl_send = null_hci_send, - .bdaddr_set = null_hci_addr_set, -}; - struct HCIInfo *qemu_next_hci(void) { if (cur_hci == nb_hcis) @@ -890,36 +852,6 @@ struct HCIInfo *qemu_next_hci(void) return hci_table[cur_hci++]; } -static struct HCIInfo *hci_init(const char *str) -{ - char *endp; - struct bt_scatternet_s *vlan = 0; - - if (!strcmp(str, "null")) - /* null */ - return &null_hci; - else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':')) - /* host[:hciN] */ - return bt_host_hci(str[4] ? str + 5 : "hci0"); - else if (!strncmp(str, "hci", 3)) { - /* hci[,vlan=n] */ - if (str[3]) { - if (!strncmp(str + 3, ",vlan=", 6)) { - vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0)); - if (*endp) - vlan = 0; - } - } else - vlan = qemu_find_bt_vlan(0); - if (vlan) - return bt_new_hci(vlan); - } - - fprintf(stderr, "qemu: Unknown bluetooth HCI `%s'.\n", str); - - return 0; -} - static int bt_hci_parse(const char *str) { struct HCIInfo *hci; @@ -1526,8 +1458,10 @@ static void configure_msg(QemuOpts *opts) static int usb_device_add(const char *devname) { - const char *p; USBDevice *dev = NULL; +#ifndef CONFIG_LINUX + const char *p; +#endif if (!usb_enabled(false)) { return -1; @@ -1543,15 +1477,8 @@ static int usb_device_add(const char *devname) /* only the linux version is qdev-ified, usb-bsd still needs this */ if (strstart(devname, "host:", &p)) { dev = usb_host_device_open(usb_bus_find(-1), p); - } else -#endif - if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) { - dev = usb_bt_init(usb_bus_find(-1), - devname[2] ? hci_init(p) - : bt_new_hci(qemu_find_bt_vlan(0))); - } else { - return -1; } +#endif if (!dev) return -1; @@ -1792,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) @@ -1849,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) @@ -1970,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(); } @@ -2063,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); } @@ -2899,7 +2825,7 @@ int main(int argc, char **argv, char **envp) const char *icount_option = NULL; const char *initrd_filename; const char *kernel_filename, *kernel_cmdline; - const char *boot_order = NULL; + const char *boot_order; DisplayState *ds; int cyls, heads, secs, translation; QemuOpts *hda_opts = NULL, *opts, *machine_opts; @@ -3565,7 +3491,8 @@ int main(int argc, char **argv, char **envp) do_acpitable_option(opts); break; case QEMU_OPTION_smbios: - do_smbios_option(optarg); + opts = qemu_opts_parse(qemu_find_opts("smbios"), optarg, 0); + do_smbios_option(opts); break; case QEMU_OPTION_enable_kvm: olist = qemu_find_opts("machine"); @@ -3661,6 +3588,7 @@ int main(int argc, char **argv, char **envp) " Wrong format.\n"); exit(1); } + qemu_uuid_set = true; break; case QEMU_OPTION_option_rom: if (nb_option_roms >= MAX_OPTION_ROMS) { @@ -4122,9 +4050,7 @@ int main(int argc, char **argv, char **envp) initrd_filename = qemu_opt_get(machine_opts, "initrd"); kernel_cmdline = qemu_opt_get(machine_opts, "append"); - if (!boot_order) { - boot_order = machine->default_boot_order; - } + boot_order = machine->default_boot_order; opts = qemu_opts_find(qemu_find_opts("boot-opts"), NULL); if (opts) { char *normal_boot_order; @@ -98,6 +98,7 @@ typedef struct XenIOState { Notifier exit; Notifier suspend; + Notifier wakeup; } XenIOState; /* Xen specific function for piix pci */ @@ -154,7 +155,7 @@ qemu_irq *xen_interrupt_controller_init(void) /* Memory Ops */ -static void xen_ram_init(ram_addr_t ram_size) +static void xen_ram_init(ram_addr_t ram_size, MemoryRegion **ram_memory_p) { MemoryRegion *sysmem = get_system_memory(); ram_addr_t below_4g_mem_size, above_4g_mem_size = 0; @@ -168,6 +169,7 @@ static void xen_ram_init(ram_addr_t ram_size) block_len += HVM_BELOW_4G_MMIO_LENGTH; } memory_region_init_ram(&ram_memory, NULL, "xen.ram", block_len); + *ram_memory_p = &ram_memory; vmstate_register_ram_global(&ram_memory); if (ram_size >= HVM_BELOW_4G_RAM_END) { @@ -612,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"); } @@ -947,7 +949,7 @@ static void xenstore_record_dm_state(struct xs_handle *xs, const char *state) exit(1); } - snprintf(path, sizeof (path), "/local/domain/0/device-model/%u/state", xen_domid); + snprintf(path, sizeof (path), "device-model/%u/state", xen_domid); if (!xs_write(xs, XBT_NULL, path, state, strlen(state))) { fprintf(stderr, "error recording dm state\n"); exit(1); @@ -1059,7 +1061,12 @@ static void xen_read_physmap(XenIOState *state) free(entries); } -int xen_hvm_init(void) +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; unsigned long ioreq_pfn; @@ -1088,6 +1095,9 @@ int xen_hvm_init(void) 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, @@ -1105,10 +1115,10 @@ int xen_hvm_init(void) 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) { @@ -1134,7 +1144,7 @@ int xen_hvm_init(void) /* Init RAM management */ xen_map_cache_init(xen_phys_offset_to_gaddr, state); - xen_ram_init(ram_size); + xen_ram_init(ram_size, ram_memory); qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); diff --git a/xen-stub.c b/xen-stub.c index 47c8e73e0f..ad189a6df8 100644 --- a/xen-stub.c +++ b/xen-stub.c @@ -64,7 +64,7 @@ void xen_modified_memory(ram_addr_t start, ram_addr_t length) { } -int xen_hvm_init(void) +int xen_hvm_init(MemoryRegion **ram_memory) { return 0; } |