diff options
347 files changed, 12573 insertions, 4306 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 21803ca00d..efdec47319 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -327,6 +327,7 @@ L: xen-devel@lists.xenproject.org S: Supported F: xen-* F: */xen* +F: hw/9pfs/xen-9p-backend.c F: hw/char/xen_console.c F: hw/display/xenfb.c F: hw/net/xen_nic.c @@ -645,7 +646,6 @@ F: hw/ppc/ppc440_bamboo.c e500 M: Alexander Graf <agraf@suse.de> -M: Scott Wood <scottwood@freescale.com> L: qemu-ppc@nongnu.org S: Supported F: hw/ppc/e500.[hc] @@ -656,7 +656,6 @@ F: pc-bios/u-boot.e500 mpc8544ds M: Alexander Graf <agraf@suse.de> -M: Scott Wood <scottwood@freescale.com> L: qemu-ppc@nongnu.org S: Supported F: hw/ppc/mpc8544ds.c @@ -933,7 +932,6 @@ F: include/hw/ppc/ppc4xx.h ppce500 M: Alexander Graf <agraf@suse.de> -M: Scott Wood <scottwood@freescale.com> L: qemu-ppc@nongnu.org S: Supported F: hw/ppc/e500* @@ -1817,8 +1815,8 @@ S: Supported F: tests/image-fuzzer/ Replication -M: Wen Congyang <wency@cn.fujitsu.com> -M: Changlong Xie <xiecl.fnst@cn.fujitsu.com> +M: Wen Congyang <wencongyang2@huawei.com> +M: Xie Changlong <xiechanglong.d@gmail.com> S: Supported F: replication* F: block/replication.c @@ -346,7 +346,7 @@ dtc/%: mkdir -p $@ $(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(chardev-obj-y) \ - $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) $(trace-obj-y) + $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) # Only keep -O and -g cflags @@ -366,11 +366,11 @@ Makefile: $(version-obj-y) # Build libraries libqemustub.a: $(stub-obj-y) -libqemuutil.a: $(util-obj-y) +libqemuutil.a: $(util-obj-y) $(trace-obj-y) ###################################################################### -COMMON_LDADDS = $(trace-obj-y) libqemuutil.a libqemustub.a +COMMON_LDADDS = libqemuutil.a libqemustub.a qemu-img.o: qemu-img-cmds.h diff --git a/Makefile.target b/Makefile.target index 7df2b8c149..465a633367 100644 --- a/Makefile.target +++ b/Makefile.target @@ -149,12 +149,6 @@ obj-y += dump.o obj-y += migration/ram.o migration/savevm.o LIBS := $(libs_softmmu) $(LIBS) -# xen support -obj-$(CONFIG_XEN) += xen-common.o -obj-$(CONFIG_XEN_I386) += xen-hvm.o xen-mapcache.o -obj-$(call lnot,$(CONFIG_XEN)) += xen-common-stub.o -obj-$(call lnot,$(CONFIG_XEN_I386)) += xen-hvm-stub.o - # Hardware support ifeq ($(TARGET_NAME), sparc64) obj-y += hw/sparc64/ @@ -188,8 +182,7 @@ dummy := $(call unnest-vars,.., \ qom-obj-y \ io-obj-y \ common-obj-y \ - common-obj-m \ - trace-obj-y) + common-obj-m) target-obj-y := $(target-obj-y-save) all-obj-y += $(common-obj-y) all-obj-y += $(target-obj-y) @@ -201,7 +194,7 @@ all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) $(QEMU_PROG_BUILD): config-devices.mak -COMMON_LDADDS = $(trace-obj-y) ../libqemuutil.a ../libqemustub.a +COMMON_LDADDS = ../libqemuutil.a ../libqemustub.a # build either PROG or PROGW $(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) @@ -1 +1 @@ -2.9.0 +2.9.50 diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 42efb2f28a..fc4ef46d11 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -51,7 +51,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) #ifndef CONFIG_LINUX error_setg(errp, "-mem-path not supported on this host"); #else - if (!memory_region_size(&backend->mr)) { + if (!host_memory_backend_mr_inited(backend)) { gchar *path; backend->force_prealloc = mem_prealloc; path = object_get_canonical_path(OBJECT(backend)); @@ -76,7 +76,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp) HostMemoryBackend *backend = MEMORY_BACKEND(o); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); - if (memory_region_size(&backend->mr)) { + if (host_memory_backend_mr_inited(backend)) { error_setg(errp, "cannot change property value"); return; } @@ -96,7 +96,7 @@ static void file_memory_backend_set_share(Object *o, bool value, Error **errp) HostMemoryBackend *backend = MEMORY_BACKEND(o); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); - if (memory_region_size(&backend->mr)) { + if (host_memory_backend_mr_inited(backend)) { error_setg(errp, "cannot change property value"); return; } diff --git a/backends/hostmem.c b/backends/hostmem.c index 89feb9ed75..4606b73849 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -45,7 +45,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, Error *local_err = NULL; uint64_t value; - if (memory_region_size(&backend->mr)) { + if (host_memory_backend_mr_inited(backend)) { error_setg(&local_err, "cannot change property value"); goto out; } @@ -146,7 +146,7 @@ static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); - if (!memory_region_size(&backend->mr)) { + if (!host_memory_backend_mr_inited(backend)) { backend->merge = value; return; } @@ -172,7 +172,7 @@ static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); - if (!memory_region_size(&backend->mr)) { + if (!host_memory_backend_mr_inited(backend)) { backend->dump = value; return; } @@ -208,7 +208,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value, } } - if (!memory_region_size(&backend->mr)) { + if (!host_memory_backend_mr_inited(backend)) { backend->prealloc = value; return; } @@ -237,10 +237,19 @@ static void host_memory_backend_init(Object *obj) backend->prealloc = mem_prealloc; } +bool host_memory_backend_mr_inited(HostMemoryBackend *backend) +{ + /* + * NOTE: We forbid zero-length memory backend, so here zero means + * "we haven't inited the backend memory region yet". + */ + return memory_region_size(&backend->mr) != 0; +} + MemoryRegion * host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) { - return memory_region_size(&backend->mr) ? &backend->mr : NULL; + return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; } void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped) @@ -192,6 +192,43 @@ void path_combine(char *dest, int dest_size, } } +bool bdrv_is_read_only(BlockDriverState *bs) +{ + return bs->read_only; +} + +int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) +{ + /* Do not set read_only if copy_on_read is enabled */ + if (bs->copy_on_read && read_only) { + error_setg(errp, "Can't set node '%s' to r/o with copy-on-read enabled", + bdrv_get_device_or_node_name(bs)); + return -EINVAL; + } + + /* Do not clear read_only if it is prohibited */ + if (!read_only && !(bs->open_flags & BDRV_O_ALLOW_RDWR)) { + error_setg(errp, "Node '%s' is read only", + bdrv_get_device_or_node_name(bs)); + return -EPERM; + } + + return 0; +} + +int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) +{ + int ret = 0; + + ret = bdrv_can_set_read_only(bs, read_only, errp); + if (ret < 0) { + return ret; + } + + bs->read_only = read_only; + return 0; +} + void bdrv_get_full_backing_filename_from_filename(const char *backed, const char *backing, char *dest, size_t sz, @@ -1167,7 +1204,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, filename = qdict_get_try_str(options, "filename"); } - if (drv->bdrv_needs_filename && !filename) { + if (drv->bdrv_needs_filename && (!filename || !filename[0])) { error_setg(errp, "The '%s' block driver requires a file name", drv->format_name); ret = -EINVAL; @@ -2752,6 +2789,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, BlockDriver *drv; QemuOpts *opts; const char *value; + bool read_only; assert(reopen_state != NULL); assert(reopen_state->bs->drv != NULL); @@ -2780,12 +2818,13 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, qdict_put(reopen_state->options, "driver", qstring_from_str(value)); } - /* if we are to stay read-only, do not allow permission change - * to r/w */ - if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) && - reopen_state->flags & BDRV_O_RDWR) { - error_setg(errp, "Node '%s' is read only", - bdrv_get_device_or_node_name(reopen_state->bs)); + /* If we are to stay read-only, do not allow permission change + * to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is + * not set, or if the BDS still has copy_on_read enabled */ + read_only = !(reopen_state->flags & BDRV_O_RDWR); + ret = bdrv_can_set_read_only(reopen_state->bs, read_only, &local_err); + if (local_err) { + error_propagate(errp, local_err); goto error; } @@ -3268,26 +3307,30 @@ exit: /** * Truncate file to 'offset' bytes (needed only for file protocols) */ -int bdrv_truncate(BdrvChild *child, int64_t offset) +int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp) { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; int ret; - /* FIXME: Some format block drivers use this function instead of implicitly - * growing their file by writing beyond its end. - * See bdrv_aligned_pwritev() for an explanation why we currently - * cannot assert this permission in that case. */ - // assert(child->perm & BLK_PERM_RESIZE); + assert(child->perm & BLK_PERM_RESIZE); - if (!drv) + if (!drv) { + error_setg(errp, "No medium inserted"); return -ENOMEDIUM; - if (!drv->bdrv_truncate) + } + if (!drv->bdrv_truncate) { + error_setg(errp, "Image format driver does not support resize"); return -ENOTSUP; - if (bs->read_only) + } + if (bs->read_only) { + error_setg(errp, "Image is read-only"); return -EACCES; + } + + assert(!(bs->open_flags & BDRV_O_INACTIVE)); - ret = drv->bdrv_truncate(bs, offset); + ret = drv->bdrv_truncate(bs, offset, errp); if (ret == 0) { ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); bdrv_dirty_bitmap_truncate(bs); @@ -3355,11 +3398,6 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) *nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors; } -bool bdrv_is_read_only(BlockDriverState *bs) -{ - return bs->read_only; -} - bool bdrv_is_sg(BlockDriverState *bs) { return bs->sg; @@ -4161,8 +4199,8 @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs) void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, - char *options, uint64_t img_size, int flags, - Error **errp, bool quiet) + char *options, uint64_t img_size, int flags, bool quiet, + Error **errp) { QemuOptsList *create_opts = NULL; QemuOpts *opts = NULL; diff --git a/block/Makefile.objs b/block/Makefile.objs index de96f8ee80..ea955302c8 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -19,6 +19,7 @@ block-obj-$(CONFIG_LIBNFS) += nfs.o block-obj-$(CONFIG_CURL) += curl.o block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o +block-obj-$(CONFIG_VXHS) += vxhs.o block-obj-$(CONFIG_LIBSSH2) += ssh.o block-obj-y += accounting.o dirty-bitmap.o block-obj-y += write-threshold.o @@ -38,6 +39,7 @@ rbd.o-cflags := $(RBD_CFLAGS) rbd.o-libs := $(RBD_LIBS) gluster.o-cflags := $(GLUSTERFS_CFLAGS) gluster.o-libs := $(GLUSTERFS_LIBS) +vxhs.o-libs := $(VXHS_LIBS) ssh.o-cflags := $(LIBSSH2_CFLAGS) ssh.o-libs := $(LIBSSH2_LIBS) block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o diff --git a/block/blkdebug.c b/block/blkdebug.c index 67e8024e36..d2a7561c4c 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -389,14 +389,12 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, } else if (align) { error_setg(errp, "Invalid alignment"); ret = -EINVAL; - goto fail_unref; + goto out; } ret = 0; goto out; -fail_unref: - bdrv_unref_child(bs, bs->file); out: if (ret < 0) { g_free(s->config_file); @@ -661,9 +659,9 @@ static int64_t blkdebug_getlength(BlockDriverState *bs) return bdrv_getlength(bs->file->bs); } -static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) +static int blkdebug_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { - return bdrv_truncate(bs->file, offset); + return bdrv_truncate(bs->file, offset, errp); } static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) diff --git a/block/blkreplay.c b/block/blkreplay.c index e1102119fb..6aa5fd4156 100755 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -37,9 +37,6 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, ret = 0; fail: - if (ret < 0) { - bdrv_unref_child(bs, bs->file); - } return ret; } diff --git a/block/blkverify.c b/block/blkverify.c index 9a1e21c6ad..af23281669 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -142,9 +142,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, ret = 0; fail: - if (ret < 0) { - bdrv_unref_child(bs, bs->file); - } qemu_opts_del(opts); return ret; } diff --git a/block/block-backend.c b/block/block-backend.c index 7405024e08..f5bf13eec9 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -420,7 +420,7 @@ void monitor_remove_blk(BlockBackend *blk) * Return @blk's name, a non-null string. * Returns an empty string iff @blk is not referenced by the monitor. */ -const char *blk_name(BlockBackend *blk) +const char *blk_name(const BlockBackend *blk) { return blk->name ?: ""; } @@ -1746,13 +1746,14 @@ int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, BDRV_REQ_WRITE_COMPRESSED); } -int blk_truncate(BlockBackend *blk, int64_t offset) +int blk_truncate(BlockBackend *blk, int64_t offset, Error **errp) { if (!blk_is_available(blk)) { + error_setg(errp, "No medium inserted"); return -ENOMEDIUM; } - return bdrv_truncate(blk->root, offset); + return bdrv_truncate(blk->root, offset, errp); } static void blk_pdiscard_entry(void *opaque) diff --git a/block/bochs.c b/block/bochs.c index 516da56c3b..a759b6eff0 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -110,7 +110,10 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - bs->read_only = true; /* no write support yet */ + ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ + if (ret < 0) { + return ret; + } ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); if (ret < 0) { diff --git a/block/cloop.c b/block/cloop.c index a6c7b9dbe6..d6597fcf78 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -72,7 +72,10 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - bs->read_only = true; + ret = bdrv_set_read_only(bs, true, errp); + if (ret < 0) { + return ret; + } /* read header */ ret = bdrv_pread(bs->file, 128, &s->block_size, 4); diff --git a/block/commit.c b/block/commit.c index 91d2c344f6..76a0d98c6f 100644 --- a/block/commit.c +++ b/block/commit.c @@ -151,7 +151,7 @@ static void coroutine_fn commit_run(void *opaque) } if (base_len < s->common.len) { - ret = blk_truncate(s->base, s->common.len); + ret = blk_truncate(s->base, s->common.len, NULL); if (ret) { goto out; } @@ -511,8 +511,9 @@ int bdrv_commit(BlockDriverState *bs) * grow the backing file image if possible. If not possible, * we must return an error */ if (length > backing_length) { - ret = blk_truncate(backing, length); + ret = blk_truncate(backing, length, &local_err); if (ret < 0) { + error_report_err(local_err); goto ro_cleanup; } } diff --git a/block/crypto.c b/block/crypto.c index 4a2038888d..6828180840 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -56,11 +56,11 @@ static int block_crypto_probe_generic(QCryptoBlockFormat format, static ssize_t block_crypto_read_func(QCryptoBlock *block, + void *opaque, size_t offset, uint8_t *buf, size_t buflen, - Error **errp, - void *opaque) + Error **errp) { BlockDriverState *bs = opaque; ssize_t ret; @@ -83,11 +83,11 @@ struct BlockCryptoCreateData { static ssize_t block_crypto_write_func(QCryptoBlock *block, + void *opaque, size_t offset, const uint8_t *buf, size_t buflen, - Error **errp, - void *opaque) + Error **errp) { struct BlockCryptoCreateData *data = opaque; ssize_t ret; @@ -102,9 +102,9 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block, static ssize_t block_crypto_init_func(QCryptoBlock *block, + void *opaque, size_t headerlen, - Error **errp, - void *opaque) + Error **errp) { struct BlockCryptoCreateData *data = opaque; int ret; @@ -381,7 +381,8 @@ static int block_crypto_create_generic(QCryptoBlockFormat format, return ret; } -static int block_crypto_truncate(BlockDriverState *bs, int64_t offset) +static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, + Error **errp) { BlockCrypto *crypto = bs->opaque; size_t payload_offset = @@ -389,7 +390,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset) offset += payload_offset; - return bdrv_truncate(bs->file, offset); + return bdrv_truncate(bs->file, offset, errp); } static void block_crypto_close(BlockDriverState *bs) diff --git a/block/dmg.c b/block/dmg.c index a7d25fc47b..900ae5a678 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -419,8 +419,12 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } + ret = bdrv_set_read_only(bs, true, errp); + if (ret < 0) { + return ret; + } + block_module_load_one("dmg-bz2"); - bs->read_only = true; s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; diff --git a/block/file-posix.c b/block/file-posix.c index 0c4896876e..1941fb6749 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -25,8 +25,6 @@ #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "qemu/timer.h" -#include "qemu/log.h" #include "block/block_int.h" #include "qemu/module.h" #include "trace.h" @@ -1409,24 +1407,31 @@ static void raw_close(BlockDriverState *bs) } } -static int raw_truncate(BlockDriverState *bs, int64_t offset) +static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { BDRVRawState *s = bs->opaque; struct stat st; + int ret; if (fstat(s->fd, &st)) { - return -errno; + ret = -errno; + error_setg_errno(errp, -ret, "Failed to fstat() the file"); + return ret; } if (S_ISREG(st.st_mode)) { if (ftruncate(s->fd, offset) < 0) { - return -errno; + ret = -errno; + error_setg_errno(errp, -ret, "Failed to resize the file"); + return ret; } } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { - if (offset > raw_getlength(bs)) { - return -EINVAL; - } + if (offset > raw_getlength(bs)) { + error_setg(errp, "Cannot grow device files"); + return -EINVAL; + } } else { + error_setg(errp, "Resizing this file is not supported"); return -ENOTSUP; } diff --git a/block/file-win32.c b/block/file-win32.c index 800fabdd72..7872e00a21 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/cutils.h" -#include "qemu/timer.h" #include "block/block_int.h" #include "qemu/module.h" #include "block/raw-aio.h" @@ -461,7 +460,7 @@ static void raw_close(BlockDriverState *bs) } } -static int raw_truncate(BlockDriverState *bs, int64_t offset) +static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { BDRVRawState *s = bs->opaque; LONG low, high; @@ -476,11 +475,11 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset) */ dwPtrLow = SetFilePointer(s->hfile, low, &high, FILE_BEGIN); if (dwPtrLow == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - fprintf(stderr, "SetFilePointer error: %lu\n", GetLastError()); + error_setg_win32(errp, GetLastError(), "SetFilePointer error"); return -EIO; } if (SetEndOfFile(s->hfile) == 0) { - fprintf(stderr, "SetEndOfFile error: %lu\n", GetLastError()); + error_setg_win32(errp, GetLastError(), "SetEndOfFile error"); return -EIO; } return 0; diff --git a/block/gluster.c b/block/gluster.c index cf29b5f9a4..1d4e2f7c52 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1092,14 +1092,17 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, return acb.ret; } -static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset) +static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, + Error **errp) { int ret; BDRVGlusterState *s = bs->opaque; ret = glfs_ftruncate(s->fd, offset); if (ret < 0) { - return -errno; + ret = -errno; + error_setg_errno(errp, -ret, "Failed to truncate file"); + return ret; } return 0; diff --git a/block/io.c b/block/io.c index a7142e00e8..40bd94f323 100644 --- a/block/io.c +++ b/block/io.c @@ -1362,16 +1362,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, assert(!waited || !req->serialising); assert(req->overlap_offset <= offset); assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); - /* FIXME: Block migration uses the BlockBackend of the guest device at a - * point when it has not yet taken write permissions. This will be - * fixed by a future patch, but for now we have to bypass this - * assertion for block migration to work. */ - // assert(child->perm & BLK_PERM_WRITE); - /* FIXME: Because of the above, we also cannot guarantee that all format - * BDS take the BLK_PERM_RESIZE permission on their file BDS, since - * they are not obligated to do so if they do not have any parent - * that has taken the permission to write to them. */ - // assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); + assert(child->perm & BLK_PERM_WRITE); + assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); @@ -1452,7 +1444,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, int ret = 0; head_padding_bytes = offset & (align - 1); - tail_padding_bytes = align - ((offset + bytes) & (align - 1)); + tail_padding_bytes = (align - (offset + bytes)) & (align - 1); assert(flags & BDRV_REQ_ZERO_WRITE); @@ -2308,7 +2300,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) bdrv_inc_in_flight(bs); - if (!bs || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs) || + if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs) || bdrv_is_sg(bs)) { goto early_exit; } diff --git a/block/iscsi.c b/block/iscsi.c index 42fb0b019c..5daa201181 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2059,22 +2059,24 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state) } } -static int iscsi_truncate(BlockDriverState *bs, int64_t offset) +static int iscsi_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { IscsiLun *iscsilun = bs->opaque; Error *local_err = NULL; if (iscsilun->type != TYPE_DISK) { + error_setg(errp, "Cannot resize non-disk iSCSI devices"); return -ENOTSUP; } iscsi_readcapacity_sync(iscsilun, &local_err); if (local_err != NULL) { - error_free(local_err); + error_propagate(errp, local_err); return -EIO; } if (offset > iscsi_getlength(bs)) { + error_setg(errp, "Cannot grow iSCSI devices"); return -EINVAL; } diff --git a/block/mirror.c b/block/mirror.c index 164438f422..e86f8f8ad7 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -724,7 +724,7 @@ static void coroutine_fn mirror_run(void *opaque) } if (s->bdev_length > base_length) { - ret = blk_truncate(s->target, s->bdev_length); + ret = blk_truncate(s->target, s->bdev_length, NULL); if (ret < 0) { goto immediate_exit; } @@ -1112,10 +1112,11 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, bool unmap, BlockCompletionFunc *cb, - void *opaque, Error **errp, + void *opaque, const BlockJobDriver *driver, bool is_none_mode, BlockDriverState *base, - bool auto_complete, const char *filter_node_name) + bool auto_complete, const char *filter_node_name, + Error **errp) { MirrorBlockJob *s; BlockDriverState *mirror_top_bs; @@ -1280,17 +1281,17 @@ void mirror_start(const char *job_id, BlockDriverState *bs, base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces, speed, granularity, buf_size, backing_mode, - on_source_error, on_target_error, unmap, NULL, NULL, errp, + on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, is_none_mode, base, false, - filter_node_name); + filter_node_name, errp); } void commit_active_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, int creation_flags, int64_t speed, BlockdevOnError on_error, const char *filter_node_name, - BlockCompletionFunc *cb, void *opaque, Error **errp, - bool auto_complete) + BlockCompletionFunc *cb, void *opaque, + bool auto_complete, Error **errp) { int orig_base_flags; Error *local_err = NULL; @@ -1303,9 +1304,9 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN, - on_error, on_error, true, cb, opaque, &local_err, + on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, - filter_node_name); + filter_node_name, &local_err); if (local_err) { error_propagate(errp, local_err); goto error_restore_flags; diff --git a/block/nfs.c b/block/nfs.c index 0816678307..76572ae546 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -497,7 +497,7 @@ out: static int64_t nfs_client_open(NFSClient *client, QDict *options, - int flags, Error **errp, int open_flags) + int flags, int open_flags, Error **errp) { int ret = -EINVAL; QemuOpts *opts = NULL; @@ -663,7 +663,7 @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, ret = nfs_client_open(client, options, (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, - errp, bs->open_flags); + bs->open_flags, errp); if (ret < 0) { return ret; } @@ -705,7 +705,7 @@ static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp) goto out; } - ret = nfs_client_open(client, options, O_CREAT, errp, 0); + ret = nfs_client_open(client, options, O_CREAT, 0, errp); if (ret < 0) { goto out; } @@ -764,10 +764,18 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) return (task.ret < 0 ? task.ret : st.st_blocks * 512); } -static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) +static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { NFSClient *client = bs->opaque; - return nfs_ftruncate(client->context, client->fh, offset); + int ret; + + ret = nfs_ftruncate(client->context, client->fh, offset); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to truncate file"); + return ret; + } + + return 0; } /* Note that this will not re-establish a connection with the NFS server diff --git a/block/parallels.c b/block/parallels.c index 90acf79687..8be46a7d48 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -223,7 +223,8 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, space << BDRV_SECTOR_BITS, 0); } else { ret = bdrv_truncate(bs->file, - (s->data_end + space) << BDRV_SECTOR_BITS); + (s->data_end + space) << BDRV_SECTOR_BITS, + NULL); } if (ret < 0) { return ret; @@ -456,8 +457,10 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, size - res->image_end_offset); res->leaks += count; if (fix & BDRV_FIX_LEAKS) { - ret = bdrv_truncate(bs->file, res->image_end_offset); + Error *local_err = NULL; + ret = bdrv_truncate(bs->file, res->image_end_offset, &local_err); if (ret < 0) { + error_report_err(local_err); res->check_errors++; return ret; } @@ -504,7 +507,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) blk_set_allow_write_beyond_eof(file, true); - ret = blk_truncate(file, 0); + ret = blk_truncate(file, 0, errp); if (ret < 0) { goto exit; } @@ -696,7 +699,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, } if (!(flags & BDRV_O_RESIZE) || !bdrv_has_zero_init(bs->file->bs) || - bdrv_truncate(bs->file, bdrv_getlength(bs->file->bs)) != 0) { + bdrv_truncate(bs->file, bdrv_getlength(bs->file->bs), NULL) != 0) { s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; } @@ -739,7 +742,7 @@ static void parallels_close(BlockDriverState *bs) } if (bs->open_flags & BDRV_O_RDWR) { - bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS); + bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS, NULL); } g_free(s->bat_dirty_bmap); diff --git a/block/qcow.c b/block/qcow.c index 9d6ac83959..5d147b962e 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -473,7 +473,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, /* round to cluster size */ cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); - bdrv_truncate(bs->file, cluster_offset + s->cluster_size); + bdrv_truncate(bs->file, cluster_offset + s->cluster_size, NULL); /* if encrypted, we must initialize the cluster content which won't be written */ if (bs->encrypted && @@ -833,7 +833,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) blk_set_allow_write_beyond_eof(qcow_blk, true); - ret = blk_truncate(qcow_blk, 0); + ret = blk_truncate(qcow_blk, 0, errp); if (ret < 0) { goto exit; } @@ -916,7 +916,7 @@ static int qcow_make_empty(BlockDriverState *bs) if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0) return -1; - ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); + ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length, NULL); if (ret < 0) return ret; diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 9e96f64c8b..4efca7ebdb 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1728,14 +1728,17 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, if (fix & BDRV_FIX_ERRORS) { int64_t new_nb_clusters; + Error *local_err = NULL; if (offset > INT64_MAX - s->cluster_size) { ret = -EINVAL; goto resize_fail; } - ret = bdrv_truncate(bs->file, offset + s->cluster_size); + ret = bdrv_truncate(bs->file, offset + s->cluster_size, + &local_err); if (ret < 0) { + error_report_err(local_err); goto resize_fail; } size = bdrv_getlength(bs->file->bs); diff --git a/block/qcow2.c b/block/qcow2.c index 6a92d2ef3f..5c1573c999 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2294,9 +2294,9 @@ 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 = blk_truncate(blk, total_size); + ret = blk_truncate(blk, total_size, errp); if (ret < 0) { - error_setg_errno(errp, -ret, "Could not resize image"); + error_prepend(errp, "Could not resize image: "); goto out; } @@ -2515,7 +2515,12 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, if (!QEMU_IS_ALIGNED(offset | count, s->cluster_size)) { assert(count < s->cluster_size); - return -ENOTSUP; + /* Ignore partial clusters, except for the special case of the + * complete partial cluster at the end of an unaligned file */ + if (!QEMU_IS_ALIGNED(offset, s->cluster_size) || + offset + count != bs->total_sectors * BDRV_SECTOR_SIZE) { + return -ENOTSUP; + } } qemu_co_mutex_lock(&s->lock); @@ -2525,32 +2530,33 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, return ret; } -static int qcow2_truncate(BlockDriverState *bs, int64_t offset) +static int qcow2_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { BDRVQcow2State *s = bs->opaque; int64_t new_l1_size; int ret; if (offset & 511) { - error_report("The new size must be a multiple of 512"); + error_setg(errp, "The new size must be a multiple of 512"); return -EINVAL; } /* cannot proceed if image has snapshots */ if (s->nb_snapshots) { - error_report("Can't resize an image which has snapshots"); + error_setg(errp, "Can't resize an image which has snapshots"); return -ENOTSUP; } /* shrinking is currently not supported */ if (offset < bs->total_sectors * 512) { - error_report("qcow2 doesn't support shrinking images yet"); + error_setg(errp, "qcow2 doesn't support shrinking images yet"); return -ENOTSUP; } new_l1_size = size_to_l1(s, offset); ret = qcow2_grow_l1_table(bs, new_l1_size, true); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to grow the L1 table"); return ret; } @@ -2559,6 +2565,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset) ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), &offset, sizeof(uint64_t)); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to update the image size"); return ret; } @@ -2584,7 +2591,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, /* align end of file to a sector boundary to ease reading with sector based I/Os */ cluster_offset = bdrv_getlength(bs->file->bs); - return bdrv_truncate(bs->file, cluster_offset); + return bdrv_truncate(bs->file, cluster_offset, NULL); } buf = qemu_blockalign(bs, s->cluster_size); @@ -2674,6 +2681,7 @@ fail: static int make_completely_empty(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; + Error *local_err = NULL; int ret, l1_clusters; int64_t offset; uint64_t *new_reftable = NULL; @@ -2798,8 +2806,10 @@ static int make_completely_empty(BlockDriverState *bs) goto fail; } - ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size); + ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size, + &local_err); if (ret < 0) { + error_report_err(local_err); goto fail; } @@ -3273,9 +3283,10 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, return ret; } - ret = blk_truncate(blk, new_size); + ret = blk_truncate(blk, new_size, &local_err); blk_unref(blk); if (ret < 0) { + error_report_err(local_err); return ret; } } diff --git a/block/qed.c b/block/qed.c index 5ec7fd83f2..fd76817cbb 100644 --- a/block/qed.c +++ b/block/qed.c @@ -635,7 +635,7 @@ static int qed_create(const char *filename, uint32_t cluster_size, blk_set_allow_write_beyond_eof(blk, true); /* File must start empty and grow, check truncate is supported */ - ret = blk_truncate(blk, 0); + ret = blk_truncate(blk, 0, errp); if (ret < 0) { goto out; } @@ -1518,7 +1518,7 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, return cb.ret; } -static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset) +static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { BDRVQEDState *s = bs->opaque; uint64_t old_image_size; @@ -1526,11 +1526,12 @@ static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset) if (!qed_is_image_size_valid(offset, s->header.cluster_size, s->header.table_size)) { + error_setg(errp, "Invalid image size specified"); return -EINVAL; } - /* Shrinking is currently not supported */ if ((uint64_t)offset < s->header.image_size) { + error_setg(errp, "Shrinking images is currently not supported"); return -ENOTSUP; } @@ -1539,6 +1540,7 @@ static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset) ret = qed_write_header_sync(s); if (ret < 0) { s->header.image_size = old_image_size; + error_setg_errno(errp, -ret, "Failed to update the image size"); } return ret; } diff --git a/block/raw-format.c b/block/raw-format.c index 86fbc657eb..36e65036f0 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -327,21 +327,23 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) } } -static int raw_truncate(BlockDriverState *bs, int64_t offset) +static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { BDRVRawState *s = bs->opaque; if (s->has_size) { + error_setg(errp, "Cannot resize fixed-size raw disks"); return -ENOTSUP; } if (INT64_MAX - offset < s->offset) { + error_setg(errp, "Disk size too large for the chosen offset"); return -EINVAL; } s->size = offset; offset += s->offset; - return bdrv_truncate(bs->file, offset); + return bdrv_truncate(bs->file, offset, errp); } static int raw_media_changed(BlockDriverState *bs) diff --git a/block/rbd.c b/block/rbd.c index 1ceeeb5a60..fbf30591d1 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -94,7 +94,7 @@ typedef struct BDRVRBDState { rados_t cluster; rados_ioctx_t io_ctx; rbd_image_t image; - char *name; + char *image_name; char *snap; } BDRVRBDState; @@ -350,7 +350,7 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) int64_t bytes = 0; int64_t objsize; int obj_order = 0; - const char *pool, *name, *conf, *clientname, *keypairs; + const char *pool, *image_name, *conf, *user, *keypairs; const char *secretid; rados_t cluster; rados_ioctx_t io_ctx; @@ -393,11 +393,11 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) */ pool = qdict_get_try_str(options, "pool"); conf = qdict_get_try_str(options, "conf"); - clientname = qdict_get_try_str(options, "user"); - name = qdict_get_try_str(options, "image"); + user = qdict_get_try_str(options, "user"); + image_name = qdict_get_try_str(options, "image"); keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); - ret = rados_create(&cluster, clientname); + ret = rados_create(&cluster, user); if (ret < 0) { error_setg_errno(errp, -ret, "error initializing"); goto exit; @@ -434,7 +434,7 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) goto shutdown; } - ret = rbd_create(io_ctx, name, bytes, &obj_order); + ret = rbd_create(io_ctx, image_name, bytes, &obj_order); if (ret < 0) { error_setg_errno(errp, -ret, "error rbd create"); } @@ -540,7 +540,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVRBDState *s = bs->opaque; - const char *pool, *snap, *conf, *clientname, *name, *keypairs; + const char *pool, *snap, *conf, *user, *image_name, *keypairs; const char *secretid; QemuOpts *opts; Error *local_err = NULL; @@ -567,24 +567,24 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, pool = qemu_opt_get(opts, "pool"); conf = qemu_opt_get(opts, "conf"); snap = qemu_opt_get(opts, "snapshot"); - clientname = qemu_opt_get(opts, "user"); - name = qemu_opt_get(opts, "image"); + user = qemu_opt_get(opts, "user"); + image_name = qemu_opt_get(opts, "image"); keypairs = qemu_opt_get(opts, "=keyvalue-pairs"); - if (!pool || !name) { + if (!pool || !image_name) { error_setg(errp, "Parameters 'pool' and 'image' are required"); r = -EINVAL; goto failed_opts; } - r = rados_create(&s->cluster, clientname); + r = rados_create(&s->cluster, user); if (r < 0) { error_setg_errno(errp, -r, "error initializing"); goto failed_opts; } s->snap = g_strdup(snap); - s->name = g_strdup(name); + s->image_name = g_strdup(image_name); /* try default location when conf=NULL, but ignore failure */ r = rados_conf_read_file(s->cluster, conf); @@ -635,13 +635,23 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, goto failed_shutdown; } - r = rbd_open(s->io_ctx, s->name, &s->image, s->snap); + /* rbd_open is always r/w */ + r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap); if (r < 0) { - error_setg_errno(errp, -r, "error reading header from %s", s->name); + error_setg_errno(errp, -r, "error reading header from %s", + s->image_name); goto failed_open; } - bs->read_only = (s->snap != NULL); + /* If we are using an rbd snapshot, we must be r/o, otherwise + * leave as-is */ + if (s->snap != NULL) { + r = bdrv_set_read_only(bs, true, &local_err); + if (r < 0) { + error_propagate(errp, local_err); + goto failed_open; + } + } qemu_opts_del(opts); return 0; @@ -651,13 +661,33 @@ failed_open: failed_shutdown: rados_shutdown(s->cluster); g_free(s->snap); - g_free(s->name); + g_free(s->image_name); failed_opts: qemu_opts_del(opts); g_free(mon_host); return r; } + +/* Since RBD is currently always opened R/W via the API, + * we just need to check if we are using a snapshot or not, in + * order to determine if we will allow it to be R/W */ +static int qemu_rbd_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + BDRVRBDState *s = state->bs->opaque; + int ret = 0; + + if (s->snap && state->flags & BDRV_O_RDWR) { + error_setg(errp, + "Cannot change node '%s' to r/w when using RBD snapshot", + bdrv_get_device_or_node_name(state->bs)); + ret = -EINVAL; + } + + return ret; +} + static void qemu_rbd_close(BlockDriverState *bs) { BDRVRBDState *s = bs->opaque; @@ -665,7 +695,7 @@ static void qemu_rbd_close(BlockDriverState *bs) rbd_close(s->image); rados_ioctx_destroy(s->io_ctx); g_free(s->snap); - g_free(s->name); + g_free(s->image_name); rados_shutdown(s->cluster); } @@ -886,13 +916,14 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs) return info.size; } -static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset) +static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { BDRVRBDState *s = bs->opaque; int r; r = rbd_resize(s->image, offset); if (r < 0) { + error_setg_errno(errp, -r, "Failed to resize file"); return r; } @@ -1064,6 +1095,7 @@ static BlockDriver bdrv_rbd = { .bdrv_parse_filename = qemu_rbd_parse_filename, .bdrv_file_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, + .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, .bdrv_create = qemu_rbd_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_get_info = qemu_rbd_getinfo, diff --git a/block/replication.c b/block/replication.c index bf3c395eb4..d300c15475 100644 --- a/block/replication.c +++ b/block/replication.c @@ -656,7 +656,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) s->replication_state = BLOCK_REPLICATION_FAILOVER; commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, - NULL, replication_done, bs, errp, true); + NULL, replication_done, bs, true, errp); break; default: aio_context_release(aio_context); diff --git a/block/sheepdog.c b/block/sheepdog.c index fb9203e9be..fe8fd923d5 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -595,7 +595,7 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) { int fd; - fd = socket_connect(s->addr, errp, NULL, NULL); + fd = socket_connect(s->addr, NULL, NULL, errp); if (s->addr->type == SOCKET_ADDRESS_KIND_INET && fd >= 0) { int ret = socket_set_nodelay(fd); @@ -2159,9 +2159,8 @@ static int64_t sd_getlength(BlockDriverState *bs) return s->inode.vdi_size; } -static int sd_truncate(BlockDriverState *bs, int64_t offset) +static int sd_truncate(BlockDriverState *bs, int64_t offset, Error **errp) { - Error *local_err = NULL; BDRVSheepdogState *s = bs->opaque; int ret, fd; unsigned int datalen; @@ -2169,16 +2168,15 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; if (offset < s->inode.vdi_size) { - error_report("shrinking is not supported"); + error_setg(errp, "shrinking is not supported"); return -EINVAL; } else if (offset > max_vdi_size) { - error_report("too big image size"); + error_setg(errp, "too big image size"); return -EINVAL; } - fd = connect_to_sdog(s, &local_err); + fd = connect_to_sdog(s, errp); if (fd < 0) { - error_report_err(local_err); return fd; } @@ -2191,7 +2189,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) close(fd); if (ret < 0) { - error_report("failed to update an inode."); + error_setg_errno(errp, -ret, "failed to update an inode"); } return ret; @@ -2456,7 +2454,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, BDRVSheepdogState *s = bs->opaque; if (offset > s->inode.vdi_size) { - ret = sd_truncate(bs, offset); + ret = sd_truncate(bs, offset, NULL); if (ret < 0) { return ret; } diff --git a/block/ssh.c b/block/ssh.c index 471ba8a260..df09f6c5ba 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -681,7 +681,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, } /* Open the socket and connect. */ - s->sock = inet_connect_saddr(s->inet, errp, NULL, NULL); + s->sock = inet_connect_saddr(s->inet, NULL, NULL, errp); if (s->sock < 0) { ret = -EIO; goto err; diff --git a/block/trace-events b/block/trace-events index 0bc5c0adf1..9a71c7fb04 100644 --- a/block/trace-events +++ b/block/trace-events @@ -110,3 +110,20 @@ qed_aio_write_data(void *s, void *acb, int ret, uint64_t offset, size_t len) "s qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64 qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64 qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu" + +# block/vxhs.c +vxhs_iio_callback(int error) "ctx is NULL: error %d" +vxhs_iio_callback_chnfail(int err, int error) "QNIO channel failed, no i/o %d, %d" +vxhs_iio_callback_unknwn(int opcode, int err) "unexpected opcode %d, errno %d" +vxhs_aio_rw_invalid(int req) "Invalid I/O request iodir %d" +vxhs_aio_rw_ioerr(char *guid, int iodir, uint64_t size, uint64_t off, void *acb, int ret, int err) "IO ERROR (vDisk %s) FOR : Read/Write = %d size = %"PRIu64" offset = %"PRIu64" ACB = %p. Error = %d, errno = %d" +vxhs_get_vdisk_stat_err(char *guid, int ret, int err) "vDisk (%s) stat ioctl failed, ret = %d, errno = %d" +vxhs_get_vdisk_stat(char *vdisk_guid, uint64_t vdisk_size) "vDisk %s stat ioctl returned size %"PRIu64 +vxhs_complete_aio(void *acb, uint64_t ret) "aio failed acb %p ret %"PRIu64 +vxhs_parse_uri_filename(const char *filename) "URI passed via bdrv_parse_filename %s" +vxhs_open_vdiskid(const char *vdisk_id) "Opening vdisk-id %s" +vxhs_open_hostinfo(char *of_vsa_addr, int port) "Adding host %s:%d to BDRVVXHSState" +vxhs_open_iio_open(const char *host) "Failed to connect to storage agent on host %s" +vxhs_parse_uri_hostinfo(char *host, int port) "Host: IP %s, Port %d" +vxhs_close(char *vdisk_guid) "Closing vdisk %s" +vxhs_get_creds(const char *cacert, const char *client_key, const char *client_cert) "cacert %s, client_key %s, client_cert %s" diff --git a/block/vdi.c b/block/vdi.c index 9b4f70e977..d12d9cdc79 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -832,9 +832,9 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) } if (image_type == VDI_TYPE_STATIC) { - ret = blk_truncate(blk, offset + blocks * block_size); + ret = blk_truncate(blk, offset + blocks * block_size, errp); if (ret < 0) { - error_setg(errp, "Failed to statically allocate %s", filename); + error_prepend(errp, "Failed to statically allocate %s", filename); goto exit; } } diff --git a/block/vhdx-log.c b/block/vhdx-log.c index 67a91c0de5..3f4c2aa095 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -548,7 +548,7 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, if (new_file_size % (1024*1024)) { /* round up to nearest 1MB boundary */ new_file_size = ((new_file_size >> 20) + 1) << 20; - bdrv_truncate(bs->file, new_file_size); + bdrv_truncate(bs->file, new_file_size, NULL); } } qemu_vfree(desc_entries); diff --git a/block/vhdx.c b/block/vhdx.c index 052a753159..e8fe3fb5e9 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1171,7 +1171,7 @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, /* per the spec, the address for a block is in units of 1MB */ *new_offset = ROUND_UP(*new_offset, 1024 * 1024); - return bdrv_truncate(bs->file, *new_offset + s->block_size); + return bdrv_truncate(bs->file, *new_offset + s->block_size, NULL); } /* @@ -1586,7 +1586,7 @@ exit: static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, uint64_t image_size, VHDXImageType type, bool use_zero_blocks, uint64_t file_offset, - uint32_t length) + uint32_t length, Error **errp) { int ret = 0; uint64_t data_file_offset; @@ -1607,16 +1607,17 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, if (type == VHDX_TYPE_DYNAMIC) { /* All zeroes, so we can just extend the file - the end of the BAT * is the furthest thing we have written yet */ - ret = blk_truncate(blk, data_file_offset); + ret = blk_truncate(blk, data_file_offset, errp); if (ret < 0) { goto exit; } } else if (type == VHDX_TYPE_FIXED) { - ret = blk_truncate(blk, data_file_offset + image_size); + ret = blk_truncate(blk, data_file_offset + image_size, errp); if (ret < 0) { goto exit; } } else { + error_setg(errp, "Unsupported image type"); ret = -ENOTSUP; goto exit; } @@ -1627,6 +1628,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, /* for a fixed file, the default BAT entry is not zero */ s->bat = g_try_malloc0(length); if (length && s->bat == NULL) { + error_setg(errp, "Failed to allocate memory for the BAT"); ret = -ENOMEM; goto exit; } @@ -1646,6 +1648,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, } ret = blk_pwrite(blk, file_offset, s->bat, length, 0); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to write the BAT"); goto exit; } } @@ -1671,7 +1674,8 @@ static int vhdx_create_new_region_table(BlockBackend *blk, uint32_t log_size, bool use_zero_blocks, VHDXImageType type, - uint64_t *metadata_offset) + uint64_t *metadata_offset, + Error **errp) { int ret = 0; uint32_t offset = 0; @@ -1740,7 +1744,7 @@ static int vhdx_create_new_region_table(BlockBackend *blk, /* The region table gives us the data we need to create the BAT, * so do that now */ ret = vhdx_create_bat(blk, s, image_size, type, use_zero_blocks, - bat_file_offset, bat_length); + bat_file_offset, bat_length, errp); if (ret < 0) { goto exit; } @@ -1749,12 +1753,14 @@ static int vhdx_create_new_region_table(BlockBackend *blk, ret = blk_pwrite(blk, VHDX_REGION_TABLE_OFFSET, buffer, VHDX_HEADER_BLOCK_SIZE, 0); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to write first region table"); goto exit; } ret = blk_pwrite(blk, VHDX_REGION_TABLE2_OFFSET, buffer, VHDX_HEADER_BLOCK_SIZE, 0); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to write second region table"); goto exit; } @@ -1825,6 +1831,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) ret = -ENOTSUP; goto exit; } else { + error_setg(errp, "Invalid subformat '%s'", type); ret = -EINVAL; goto exit; } @@ -1879,12 +1886,14 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature), 0); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to write file signature"); goto delete_and_exit; } if (creator) { ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature), creator, creator_items * sizeof(gunichar2), 0); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to write creator field"); goto delete_and_exit; } } @@ -1893,13 +1902,14 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) /* Creates (B),(C) */ ret = vhdx_create_new_headers(blk, image_size, log_size); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to write image headers"); goto delete_and_exit; } /* Creates (D),(E),(G) explicitly. (F) created as by-product */ ret = vhdx_create_new_region_table(blk, image_size, block_size, 512, log_size, use_zero_blocks, image_type, - &metadata_offset); + &metadata_offset, errp); if (ret < 0) { goto delete_and_exit; } @@ -1908,6 +1918,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) ret = vhdx_create_new_metadata(blk, image_size, block_size, 512, metadata_offset, image_type); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to initialize metadata"); goto delete_and_exit; } diff --git a/block/vmdk.c b/block/vmdk.c index a9bd22bf93..c61b9cc8e0 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1714,10 +1714,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, blk_set_allow_write_beyond_eof(blk, true); if (flat) { - ret = blk_truncate(blk, filesize); - if (ret < 0) { - error_setg_errno(errp, -ret, "Could not truncate file"); - } + ret = blk_truncate(blk, filesize, errp); goto exit; } magic = cpu_to_be32(VMDK4_MAGIC); @@ -1780,9 +1777,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, goto exit; } - ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9); + ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9, errp); if (ret < 0) { - error_setg_errno(errp, -ret, "Could not truncate file"); goto exit; } @@ -2090,10 +2086,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) /* bdrv_pwrite write padding zeros to align to sector, we don't need that * for description file */ if (desc_offset == 0) { - ret = blk_truncate(new_blk, desc_len); - if (ret < 0) { - error_setg_errno(errp, -ret, "Could not truncate file"); - } + ret = blk_truncate(new_blk, desc_len, errp); } exit: if (new_blk) { diff --git a/block/vpc.c b/block/vpc.c index f591d4be38..ecfee77149 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -851,20 +851,21 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, } static int create_fixed_disk(BlockBackend *blk, uint8_t *buf, - int64_t total_size) + int64_t total_size, Error **errp) { int ret; /* Add footer to total size */ total_size += HEADER_SIZE; - ret = blk_truncate(blk, total_size); + ret = blk_truncate(blk, total_size, errp); if (ret < 0) { return ret; } ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE, 0); if (ret < 0) { + error_setg_errno(errp, -ret, "Unable to write VHD header"); return ret; } @@ -996,11 +997,11 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) if (disk_type == VHD_DYNAMIC) { ret = create_dynamic_disk(blk, buf, total_sectors); + if (ret < 0) { + error_setg(errp, "Unable to create or write VHD header"); + } } else { - ret = create_fixed_disk(blk, buf, total_size); - } - if (ret < 0) { - error_setg(errp, "Unable to create or write VHD header"); + ret = create_fixed_disk(blk, buf, total_size, errp); } out: diff --git a/block/vvfat.c b/block/vvfat.c index af5153d27d..b509d55642 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1156,8 +1156,6 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->current_cluster=0xffffffff; - /* read only is the default for safety */ - bs->read_only = true; s->qcow = NULL; s->qcow_filename = NULL; s->fat2 = NULL; @@ -1169,11 +1167,24 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1); if (qemu_opt_get_bool(opts, "rw", false)) { - ret = enable_write_target(bs, errp); + if (!bdrv_is_read_only(bs)) { + ret = enable_write_target(bs, errp); + if (ret < 0) { + goto fail; + } + } else { + ret = -EPERM; + error_setg(errp, + "Unable to set VVFAT to 'rw' when drive is read-only"); + goto fail; + } + } else { + /* read only is the default for safety */ + ret = bdrv_set_read_only(bs, true, &local_err); if (ret < 0) { + error_propagate(errp, local_err); goto fail; } - bs->read_only = false; } bs->total_sectors = cyls * heads * secs; diff --git a/block/vxhs.c b/block/vxhs.c new file mode 100644 index 0000000000..9ffe9d3814 --- /dev/null +++ b/block/vxhs.c @@ -0,0 +1,575 @@ +/* + * QEMU Block driver for Veritas HyperScale (VxHS) + * + * Copyright (c) 2017 Veritas Technologies LLC. + * + * 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 "qemu/osdep.h" +#include <qnio/qnio_api.h> +#include <sys/param.h> +#include "block/block_int.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" +#include "trace.h" +#include "qemu/uri.h" +#include "qapi/error.h" +#include "qemu/uuid.h" +#include "crypto/tlscredsx509.h" + +#define VXHS_OPT_FILENAME "filename" +#define VXHS_OPT_VDISK_ID "vdisk-id" +#define VXHS_OPT_SERVER "server" +#define VXHS_OPT_HOST "host" +#define VXHS_OPT_PORT "port" + +/* Only accessed under QEMU global mutex */ +static uint32_t vxhs_ref; + +typedef enum { + VDISK_AIO_READ, + VDISK_AIO_WRITE, +} VDISKAIOCmd; + +/* + * HyperScale AIO callbacks structure + */ +typedef struct VXHSAIOCB { + BlockAIOCB common; + int err; +} VXHSAIOCB; + +typedef struct VXHSvDiskHostsInfo { + void *dev_handle; /* Device handle */ + char *host; /* Host name or IP */ + int port; /* Host's port number */ +} VXHSvDiskHostsInfo; + +/* + * Structure per vDisk maintained for state + */ +typedef struct BDRVVXHSState { + VXHSvDiskHostsInfo vdisk_hostinfo; /* Per host info */ + char *vdisk_guid; + char *tlscredsid; /* tlscredsid */ +} BDRVVXHSState; + +static void vxhs_complete_aio_bh(void *opaque) +{ + VXHSAIOCB *acb = opaque; + BlockCompletionFunc *cb = acb->common.cb; + void *cb_opaque = acb->common.opaque; + int ret = 0; + + if (acb->err != 0) { + trace_vxhs_complete_aio(acb, acb->err); + ret = (-EIO); + } + + qemu_aio_unref(acb); + cb(cb_opaque, ret); +} + +/* + * Called from a libqnio thread + */ +static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error) +{ + VXHSAIOCB *acb = NULL; + + switch (opcode) { + case IRP_READ_REQUEST: + case IRP_WRITE_REQUEST: + + /* + * ctx is VXHSAIOCB* + * ctx is NULL if error is QNIOERROR_CHANNEL_HUP + */ + if (ctx) { + acb = ctx; + } else { + trace_vxhs_iio_callback(error); + goto out; + } + + if (error) { + if (!acb->err) { + acb->err = error; + } + trace_vxhs_iio_callback(error); + } + + aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs), + vxhs_complete_aio_bh, acb); + break; + + default: + if (error == QNIOERROR_HUP) { + /* + * Channel failed, spontaneous notification, + * not in response to I/O + */ + trace_vxhs_iio_callback_chnfail(error, errno); + } else { + trace_vxhs_iio_callback_unknwn(opcode, error); + } + break; + } +out: + return; +} + +static QemuOptsList runtime_opts = { + .name = "vxhs", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = VXHS_OPT_FILENAME, + .type = QEMU_OPT_STRING, + .help = "URI to the Veritas HyperScale image", + }, + { + .name = VXHS_OPT_VDISK_ID, + .type = QEMU_OPT_STRING, + .help = "UUID of the VxHS vdisk", + }, + { + .name = "tls-creds", + .type = QEMU_OPT_STRING, + .help = "ID of the TLS/SSL credentials to use", + }, + { /* end of list */ } + }, +}; + +static QemuOptsList runtime_tcp_opts = { + .name = "vxhs_tcp", + .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head), + .desc = { + { + .name = VXHS_OPT_HOST, + .type = QEMU_OPT_STRING, + .help = "host address (ipv4 addresses)", + }, + { + .name = VXHS_OPT_PORT, + .type = QEMU_OPT_NUMBER, + .help = "port number on which VxHSD is listening (default 9999)", + .def_value_str = "9999" + }, + { /* end of list */ } + }, +}; + +/* + * Parse incoming URI and populate *options with the host + * and device information + */ +static int vxhs_parse_uri(const char *filename, QDict *options) +{ + URI *uri = NULL; + char *port; + int ret = 0; + + trace_vxhs_parse_uri_filename(filename); + uri = uri_parse(filename); + if (!uri || !uri->server || !uri->path) { + uri_free(uri); + return -EINVAL; + } + + qdict_put(options, VXHS_OPT_SERVER".host", qstring_from_str(uri->server)); + + if (uri->port) { + port = g_strdup_printf("%d", uri->port); + qdict_put(options, VXHS_OPT_SERVER".port", qstring_from_str(port)); + g_free(port); + } + + qdict_put(options, "vdisk-id", qstring_from_str(uri->path)); + + trace_vxhs_parse_uri_hostinfo(uri->server, uri->port); + uri_free(uri); + + return ret; +} + +static void vxhs_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + if (qdict_haskey(options, "vdisk-id") || qdict_haskey(options, "server")) { + error_setg(errp, "vdisk-id/server and a file name may not be specified " + "at the same time"); + return; + } + + if (strstr(filename, "://")) { + int ret = vxhs_parse_uri(filename, options); + if (ret < 0) { + error_setg(errp, "Invalid URI. URI should be of the form " + " vxhs://<host_ip>:<port>/<vdisk-id>"); + } + } +} + +static int vxhs_init_and_ref(void) +{ + if (vxhs_ref++ == 0) { + if (iio_init(QNIO_VERSION, vxhs_iio_callback)) { + return -ENODEV; + } + } + return 0; +} + +static void vxhs_unref(void) +{ + if (--vxhs_ref == 0) { + iio_fini(); + } +} + +static void vxhs_get_tls_creds(const char *id, char **cacert, + char **key, char **cert, Error **errp) +{ + Object *obj; + QCryptoTLSCreds *creds; + QCryptoTLSCredsX509 *creds_x509; + + obj = object_resolve_path_component( + object_get_objects_root(), id); + + if (!obj) { + error_setg(errp, "No TLS credentials with id '%s'", + id); + return; + } + + creds_x509 = (QCryptoTLSCredsX509 *) + object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS_X509); + + if (!creds_x509) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + id); + return; + } + + creds = &creds_x509->parent_obj; + + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, + "Expecting TLS credentials with a client endpoint"); + return; + } + + /* + * Get the cacert, client_cert and client_key file names. + */ + if (!creds->dir) { + error_setg(errp, "TLS object missing 'dir' property value"); + return; + } + + *cacert = g_strdup_printf("%s/%s", creds->dir, + QCRYPTO_TLS_CREDS_X509_CA_CERT); + *cert = g_strdup_printf("%s/%s", creds->dir, + QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + *key = g_strdup_printf("%s/%s", creds->dir, + QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); +} + +static int vxhs_open(BlockDriverState *bs, QDict *options, + int bdrv_flags, Error **errp) +{ + BDRVVXHSState *s = bs->opaque; + void *dev_handlep; + QDict *backing_options = NULL; + QemuOpts *opts = NULL; + QemuOpts *tcp_opts = NULL; + char *of_vsa_addr = NULL; + Error *local_err = NULL; + const char *vdisk_id_opt; + const char *server_host_opt; + int ret = 0; + char *cacert = NULL; + char *client_key = NULL; + char *client_cert = NULL; + + ret = vxhs_init_and_ref(); + if (ret < 0) { + ret = -EINVAL; + goto out; + } + + /* Create opts info from runtime_opts and runtime_tcp_opts list */ + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + tcp_opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort); + + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + ret = -EINVAL; + goto out; + } + + /* vdisk-id is the disk UUID */ + vdisk_id_opt = qemu_opt_get(opts, VXHS_OPT_VDISK_ID); + if (!vdisk_id_opt) { + error_setg(&local_err, QERR_MISSING_PARAMETER, VXHS_OPT_VDISK_ID); + ret = -EINVAL; + goto out; + } + + /* vdisk-id may contain a leading '/' */ + if (strlen(vdisk_id_opt) > UUID_FMT_LEN + 1) { + error_setg(&local_err, "vdisk-id cannot be more than %d characters", + UUID_FMT_LEN); + ret = -EINVAL; + goto out; + } + + s->vdisk_guid = g_strdup(vdisk_id_opt); + trace_vxhs_open_vdiskid(vdisk_id_opt); + + /* get the 'server.' arguments */ + qdict_extract_subqdict(options, &backing_options, VXHS_OPT_SERVER"."); + + qemu_opts_absorb_qdict(tcp_opts, backing_options, &local_err); + if (local_err != NULL) { + ret = -EINVAL; + goto out; + } + + server_host_opt = qemu_opt_get(tcp_opts, VXHS_OPT_HOST); + if (!server_host_opt) { + error_setg(&local_err, QERR_MISSING_PARAMETER, + VXHS_OPT_SERVER"."VXHS_OPT_HOST); + ret = -EINVAL; + goto out; + } + + if (strlen(server_host_opt) > MAXHOSTNAMELEN) { + error_setg(&local_err, "server.host cannot be more than %d characters", + MAXHOSTNAMELEN); + ret = -EINVAL; + goto out; + } + + /* check if we got tls-creds via the --object argument */ + s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); + if (s->tlscredsid) { + vxhs_get_tls_creds(s->tlscredsid, &cacert, &client_key, + &client_cert, &local_err); + if (local_err != NULL) { + ret = -EINVAL; + goto out; + } + trace_vxhs_get_creds(cacert, client_key, client_cert); + } + + s->vdisk_hostinfo.host = g_strdup(server_host_opt); + s->vdisk_hostinfo.port = g_ascii_strtoll(qemu_opt_get(tcp_opts, + VXHS_OPT_PORT), + NULL, 0); + + trace_vxhs_open_hostinfo(s->vdisk_hostinfo.host, + s->vdisk_hostinfo.port); + + of_vsa_addr = g_strdup_printf("of://%s:%d", + s->vdisk_hostinfo.host, + s->vdisk_hostinfo.port); + + /* + * Open qnio channel to storage agent if not opened before + */ + dev_handlep = iio_open(of_vsa_addr, s->vdisk_guid, 0, + cacert, client_key, client_cert); + if (dev_handlep == NULL) { + trace_vxhs_open_iio_open(of_vsa_addr); + ret = -ENODEV; + goto out; + } + s->vdisk_hostinfo.dev_handle = dev_handlep; + +out: + g_free(of_vsa_addr); + QDECREF(backing_options); + qemu_opts_del(tcp_opts); + qemu_opts_del(opts); + g_free(cacert); + g_free(client_key); + g_free(client_cert); + + if (ret < 0) { + vxhs_unref(); + error_propagate(errp, local_err); + g_free(s->vdisk_hostinfo.host); + g_free(s->vdisk_guid); + g_free(s->tlscredsid); + s->vdisk_guid = NULL; + } + + return ret; +} + +static const AIOCBInfo vxhs_aiocb_info = { + .aiocb_size = sizeof(VXHSAIOCB) +}; + +/* + * This allocates QEMU-VXHS callback for each IO + * and is passed to QNIO. When QNIO completes the work, + * it will be passed back through the callback. + */ +static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, + VDISKAIOCmd iodir) +{ + VXHSAIOCB *acb = NULL; + BDRVVXHSState *s = bs->opaque; + size_t size; + uint64_t offset; + int iio_flags = 0; + int ret = 0; + void *dev_handle = s->vdisk_hostinfo.dev_handle; + + offset = sector_num * BDRV_SECTOR_SIZE; + size = nb_sectors * BDRV_SECTOR_SIZE; + acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque); + + /* + * Initialize VXHSAIOCB. + */ + acb->err = 0; + + iio_flags = IIO_FLAG_ASYNC; + + switch (iodir) { + case VDISK_AIO_WRITE: + ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov, + offset, (uint64_t)size, iio_flags); + break; + case VDISK_AIO_READ: + ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov, + offset, (uint64_t)size, iio_flags); + break; + default: + trace_vxhs_aio_rw_invalid(iodir); + goto errout; + } + + if (ret != 0) { + trace_vxhs_aio_rw_ioerr(s->vdisk_guid, iodir, size, offset, + acb, ret, errno); + goto errout; + } + return &acb->common; + +errout: + qemu_aio_unref(acb); + return NULL; +} + +static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb, + opaque, VDISK_AIO_READ); +} + +static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, + cb, opaque, VDISK_AIO_WRITE); +} + +static void vxhs_close(BlockDriverState *bs) +{ + BDRVVXHSState *s = bs->opaque; + + trace_vxhs_close(s->vdisk_guid); + + g_free(s->vdisk_guid); + s->vdisk_guid = NULL; + + /* + * Close vDisk device + */ + if (s->vdisk_hostinfo.dev_handle) { + iio_close(s->vdisk_hostinfo.dev_handle); + s->vdisk_hostinfo.dev_handle = NULL; + } + + vxhs_unref(); + + /* + * Free the dynamically allocated host string etc + */ + g_free(s->vdisk_hostinfo.host); + g_free(s->tlscredsid); + s->tlscredsid = NULL; + s->vdisk_hostinfo.host = NULL; + s->vdisk_hostinfo.port = 0; +} + +static int64_t vxhs_get_vdisk_stat(BDRVVXHSState *s) +{ + int64_t vdisk_size = -1; + int ret = 0; + void *dev_handle = s->vdisk_hostinfo.dev_handle; + + ret = iio_ioctl(dev_handle, IOR_VDISK_STAT, &vdisk_size, 0); + if (ret < 0) { + trace_vxhs_get_vdisk_stat_err(s->vdisk_guid, ret, errno); + return -EIO; + } + + trace_vxhs_get_vdisk_stat(s->vdisk_guid, vdisk_size); + return vdisk_size; +} + +/* + * Returns the size of vDisk in bytes. This is required + * by QEMU block upper block layer so that it is visible + * to guest. + */ +static int64_t vxhs_getlength(BlockDriverState *bs) +{ + BDRVVXHSState *s = bs->opaque; + int64_t vdisk_size; + + vdisk_size = vxhs_get_vdisk_stat(s); + if (vdisk_size < 0) { + return -EIO; + } + + return vdisk_size; +} + +static BlockDriver bdrv_vxhs = { + .format_name = "vxhs", + .protocol_name = "vxhs", + .instance_size = sizeof(BDRVVXHSState), + .bdrv_file_open = vxhs_open, + .bdrv_parse_filename = vxhs_parse_filename, + .bdrv_close = vxhs_close, + .bdrv_getlength = vxhs_getlength, + .bdrv_aio_readv = vxhs_aio_readv, + .bdrv_aio_writev = vxhs_aio_writev, +}; + +static void bdrv_vxhs_init(void) +{ + bdrv_register(&bdrv_vxhs); +} + +block_init(bdrv_vxhs_init); diff --git a/blockdev.c b/blockdev.c index 4927914ce3..4d8cdedd54 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1728,7 +1728,7 @@ static void external_snapshot_prepare(BlkActionState *common, bdrv_img_create(new_image_file, format, state->old_bs->filename, state->old_bs->drv->format_name, - NULL, size, flags, &local_err, false); + NULL, size, flags, false, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -2930,26 +2930,7 @@ void qmp_block_resize(bool has_device, const char *device, /* complete all in-flight operations before resizing the device */ bdrv_drain_all(); - ret = blk_truncate(blk, size); - switch (ret) { - case 0: - break; - case -ENOMEDIUM: - error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); - break; - case -ENOTSUP: - error_setg(errp, QERR_UNSUPPORTED); - break; - case -EACCES: - error_setg(errp, "Device '%s' is read only", device); - break; - case -EBUSY: - error_setg(errp, QERR_DEVICE_IN_USE, device); - break; - default: - error_setg_errno(errp, -ret, "Could not resize"); - break; - } + ret = blk_truncate(blk, size, errp); out: blk_unref(blk); @@ -3142,7 +3123,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, } commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, BLOCK_JOB_DEFAULT, speed, on_error, - filter_node_name, NULL, NULL, &local_err, false); + filter_node_name, NULL, NULL, false, &local_err); } else { BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { @@ -3237,10 +3218,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, if (source) { bdrv_img_create(backup->target, backup->format, source->filename, source->drv->format_name, NULL, - size, flags, &local_err, false); + size, flags, false, &local_err); } else { bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL, - size, flags, &local_err, false); + size, flags, false, &local_err); } } @@ -3531,7 +3512,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) /* create new image w/o backing file */ assert(format); bdrv_img_create(arg->target, format, - NULL, NULL, NULL, size, flags, &local_err, false); + NULL, NULL, NULL, size, flags, false, &local_err); } else { switch (arg->mode) { case NEW_IMAGE_MODE_EXISTING: @@ -3541,7 +3522,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) bdrv_img_create(arg->target, format, source->filename, source->drv->format_name, - NULL, size, flags, &local_err, false); + NULL, size, flags, false, &local_err); break; default: abort(); @@ -320,6 +320,7 @@ numa="" tcmalloc="no" jemalloc="no" replication="yes" +vxhs="" supported_cpu="no" supported_os="no" @@ -742,7 +743,7 @@ if test "$mingw32" = "yes" ; then sysconfdir="\${prefix}" local_statedir= confsuffix="" - libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi -lnetapi32 $libs_qga" + libs_qga="-lws2_32 -lwinmm -lpowrprof -lwtsapi32 -liphlpapi -lnetapi32 $libs_qga" fi werror="" @@ -1183,6 +1184,10 @@ for opt do ;; --enable-replication) replication="yes" ;; + --disable-vxhs) vxhs="no" + ;; + --enable-vxhs) vxhs="yes" + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1191,21 +1196,6 @@ for opt do esac done -if ! has $python; then - error_exit "Python not found. Use --python=/path/to/python" -fi - -# Note that if the Python conditional here evaluates True we will exit -# with status 1 which is a shell 'false' value. -if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6) or sys.version_info >= (3,))'; then - error_exit "Cannot use '$python', Python 2.6 or later is required." \ - "Note that Python 3 or later is not yet supported." \ - "Use --python=/path/to/python to specify a supported Python." -fi - -# Suppress writing compiled files -python="$python -B" - case "$cpu" in ppc) CPU_CFLAGS="-m32" @@ -1280,6 +1270,9 @@ for config in $mak_wilds; do default_target_list="${default_target_list} $(basename "$config" .mak)" done +# Enumerate public trace backends for --help output +trace_backend_list=$(echo $(grep -le '^PUBLIC = True$' "$source_path"/scripts/tracetool/backend/*.py | sed -e 's/^.*\/\(.*\)\.py$/\1/')) + if test x"$show_help" = x"yes" ; then cat << EOF @@ -1333,7 +1326,7 @@ Advanced options (experts only): set block driver read-only whitelist (affects only QEMU, not qemu-img) --enable-trace-backends=B Set trace backend - Available backends: $($python $source_path/scripts/tracetool.py --list-backends) + Available backends: $trace_backend_list --with-trace-file=NAME Full PATH,NAME of file to store traces Default:trace-<pid> --disable-slirp disable SLIRP userspace network connectivity @@ -1427,12 +1420,28 @@ disabled with --disable-FEATURE, default is enabled if available: xfsctl xfsctl support qom-cast-debug cast debugging support tools build qemu-io, qemu-nbd and qemu-image tools + vxhs Veritas HyperScale vDisk backend support NOTE: The object files are built at the place where configure is launched EOF exit 0 fi +if ! has $python; then + error_exit "Python not found. Use --python=/path/to/python" +fi + +# Note that if the Python conditional here evaluates True we will exit +# with status 1 which is a shell 'false' value. +if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6) or sys.version_info >= (3,))'; then + error_exit "Cannot use '$python', Python 2.6 or later is required." \ + "Note that Python 3 or later is not yet supported." \ + "Use --python=/path/to/python to specify a supported Python." +fi + +# Suppress writing compiled files +python="$python -B" + # Now we have handled --enable-tcg-interpreter and know we're not just # printing the help message, bail out if the host CPU isn't supported. if test "$ARCH" = "unknown"; then @@ -1988,30 +1997,65 @@ fi # xen probe if test "$xen" != "no" ; then - xen_libs="-lxenstore -lxenctrl -lxenguest" - xen_stable_libs="-lxenforeignmemory -lxengnttab -lxenevtchn" + # Check whether Xen library path is specified via --extra-ldflags to avoid + # overriding this setting with pkg-config output. If not, try pkg-config + # to obtain all needed flags. + + if ! echo $EXTRA_LDFLAGS | grep tools/libxc > /dev/null && \ + $pkg_config --exists xencontrol ; then + xen_ctrl_version="$(printf '%d%02d%02d' \ + $($pkg_config --modversion xencontrol | sed 's/\./ /g') )" + xen=yes + xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab" + xen_pc="$xen_pc xenevtchn xendevicemodel" + QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)" + libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu" + LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS" + else - # First we test whether Xen headers and libraries are available. - # If no, we are done and there is no Xen support. - # If yes, more tests are run to detect the Xen version. + xen_libs="-lxenstore -lxenctrl -lxenguest" + xen_stable_libs="-lxencall -lxenforeignmemory -lxengnttab -lxenevtchn" - # Xen (any) - cat > $TMPC <<EOF + # First we test whether Xen headers and libraries are available. + # If no, we are done and there is no Xen support. + # If yes, more tests are run to detect the Xen version. + + # Xen (any) + cat > $TMPC <<EOF #include <xenctrl.h> int main(void) { return 0; } EOF - if ! compile_prog "" "$xen_libs" ; then - # Xen not found - if test "$xen" = "yes" ; then - feature_not_found "xen" "Install xen devel" - fi - xen=no + if ! compile_prog "" "$xen_libs" ; then + # Xen not found + if test "$xen" = "yes" ; then + feature_not_found "xen" "Install xen devel" + fi + xen=no + + # Xen unstable + elif + cat > $TMPC <<EOF && +#undef XC_WANT_COMPAT_DEVICEMODEL_API +#define __XEN_TOOLS__ +#include <xendevicemodel.h> +int main(void) { + xendevicemodel_handle *xd; - # Xen unstable - elif - cat > $TMPC <<EOF && + xd = xendevicemodel_open(0, 0); + xendevicemodel_close(xd); + + return 0; +} +EOF + compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs" + then + xen_stable_libs="-lxendevicemodel $xen_stable_libs" + xen_ctrl_version=40900 + xen=yes + elif + cat > $TMPC <<EOF && /* * If we have stable libs the we don't want the libxc compat * layers, regardless of what CFLAGS we may have been given. @@ -2061,12 +2105,12 @@ int main(void) { return 0; } EOF - compile_prog "" "$xen_libs $xen_stable_libs" - then - xen_ctrl_version=480 - xen=yes - elif - cat > $TMPC <<EOF && + compile_prog "" "$xen_libs $xen_stable_libs" + then + xen_ctrl_version=40800 + xen=yes + elif + cat > $TMPC <<EOF && /* * If we have stable libs the we don't want the libxc compat * layers, regardless of what CFLAGS we may have been given. @@ -2112,12 +2156,12 @@ int main(void) { return 0; } EOF - compile_prog "" "$xen_libs $xen_stable_libs" - then - xen_ctrl_version=471 - xen=yes - elif - cat > $TMPC <<EOF && + compile_prog "" "$xen_libs $xen_stable_libs" + then + xen_ctrl_version=40701 + xen=yes + elif + cat > $TMPC <<EOF && #include <xenctrl.h> #include <stdint.h> int main(void) { @@ -2127,14 +2171,14 @@ int main(void) { return 0; } EOF - compile_prog "" "$xen_libs" - then - xen_ctrl_version=470 - xen=yes - - # Xen 4.6 - elif - cat > $TMPC <<EOF && + compile_prog "" "$xen_libs" + then + xen_ctrl_version=40700 + xen=yes + + # Xen 4.6 + elif + cat > $TMPC <<EOF && #include <xenctrl.h> #include <xenstore.h> #include <stdint.h> @@ -2155,14 +2199,14 @@ int main(void) { return 0; } EOF - compile_prog "" "$xen_libs" - then - xen_ctrl_version=460 - xen=yes - - # Xen 4.5 - elif - cat > $TMPC <<EOF && + compile_prog "" "$xen_libs" + then + xen_ctrl_version=40600 + xen=yes + + # Xen 4.5 + elif + cat > $TMPC <<EOF && #include <xenctrl.h> #include <xenstore.h> #include <stdint.h> @@ -2182,13 +2226,13 @@ int main(void) { return 0; } EOF - compile_prog "" "$xen_libs" - then - xen_ctrl_version=450 - xen=yes + compile_prog "" "$xen_libs" + then + xen_ctrl_version=40500 + xen=yes - elif - cat > $TMPC <<EOF && + elif + cat > $TMPC <<EOF && #include <xenctrl.h> #include <xenstore.h> #include <stdint.h> @@ -2207,24 +2251,25 @@ int main(void) { return 0; } EOF - compile_prog "" "$xen_libs" - then - xen_ctrl_version=420 - xen=yes + compile_prog "" "$xen_libs" + then + xen_ctrl_version=40200 + xen=yes - else - if test "$xen" = "yes" ; then - feature_not_found "xen (unsupported version)" \ - "Install a supported xen (xen 4.2 or newer)" + else + if test "$xen" = "yes" ; then + feature_not_found "xen (unsupported version)" \ + "Install a supported xen (xen 4.2 or newer)" + fi + xen=no fi - xen=no - fi - if test "$xen" = yes; then - if test $xen_ctrl_version -ge 471 ; then - libs_softmmu="$xen_stable_libs $libs_softmmu" + if test "$xen" = yes; then + if test $xen_ctrl_version -ge 40701 ; then + libs_softmmu="$xen_stable_libs $libs_softmmu" + fi + libs_softmmu="$xen_libs $libs_softmmu" fi - libs_softmmu="$xen_libs $libs_softmmu" fi fi @@ -4781,6 +4826,33 @@ if compile_prog "" "" ; then fi ########################################## +# Veritas HyperScale block driver VxHS +# Check if libvxhs is installed + +if test "$vxhs" != "no" ; then + cat > $TMPC <<EOF +#include <stdint.h> +#include <qnio/qnio_api.h> + +void *vxhs_callback; + +int main(void) { + iio_init(QNIO_VERSION, vxhs_callback); + return 0; +} +EOF + vxhs_libs="-lvxhs -lssl" + if compile_prog "" "$vxhs_libs" ; then + vxhs=yes + else + if test "$vxhs" = "yes" ; then + feature_not_found "vxhs block device" "Install libvxhs See github" + fi + vxhs=no + fi +fi + +########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -5146,6 +5218,7 @@ echo "tcmalloc support $tcmalloc" echo "jemalloc support $jemalloc" echo "avx2 optimization $avx2_opt" echo "replication support $replication" +echo "VxHS block device $vxhs" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -5785,6 +5858,11 @@ if test "$pthread_setname_np" = "yes" ; then echo "CONFIG_PTHREAD_SETNAME_NP=y" >> $config_host_mak fi +if test "$vxhs" = "yes" ; then + echo "CONFIG_VXHS=y" >> $config_host_mak + echo "VXHS_LIBS=$vxhs_libs" >> $config_host_mak +fi + if test "$tcg_interpreter" = "yes"; then QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" elif test "$ARCH" = "sparc64" ; then diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 4530f8241c..d5a31bbaeb 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -473,10 +473,10 @@ qcrypto_block_luks_load_key(QCryptoBlock *block, * then encrypted. */ rv = readfunc(block, + opaque, slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, splitkey, splitkeylen, - errp, - opaque); + errp); if (rv < 0) { goto cleanup; } @@ -676,11 +676,10 @@ qcrypto_block_luks_open(QCryptoBlock *block, /* Read the entire LUKS header, minus the key material from * the underlying device */ - rv = readfunc(block, 0, + rv = readfunc(block, opaque, 0, (uint8_t *)&luks->header, sizeof(luks->header), - errp, - opaque); + errp); if (rv < 0) { ret = rv; goto fail; @@ -1246,7 +1245,7 @@ qcrypto_block_luks_create(QCryptoBlock *block, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; /* Reserve header space to match payload offset */ - initfunc(block, block->payload_offset, &local_err, opaque); + initfunc(block, opaque, block->payload_offset, &local_err); if (local_err) { error_propagate(errp, local_err); goto error; @@ -1268,11 +1267,10 @@ qcrypto_block_luks_create(QCryptoBlock *block, /* Write out the partition header and key slot headers */ - writefunc(block, 0, + writefunc(block, opaque, 0, (const uint8_t *)&luks->header, sizeof(luks->header), - &local_err, - opaque); + &local_err); /* Delay checking local_err until we've byte-swapped */ @@ -1297,12 +1295,11 @@ qcrypto_block_luks_create(QCryptoBlock *block, /* Write out the master key material, starting at the * sector immediately following the partition header. */ - if (writefunc(block, + if (writefunc(block, opaque, luks->header.key_slots[0].key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, splitkey, splitkeylen, - errp, - opaque) != splitkeylen) { + errp) != splitkeylen) { goto error; } diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 1e3bd2b8ca..78d7af03a2 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -29,6 +29,7 @@ CONFIG_LAN9118=y CONFIG_SMC91C111=y CONFIG_ALLWINNER_EMAC=y CONFIG_IMX_FEC=y +CONFIG_FTGMAC100=y CONFIG_DS1338=y CONFIG_PFLASH_CFI01=y CONFIG_PFLASH_CFI02=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 029e95202a..d2ab2f6655 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -39,7 +39,6 @@ CONFIG_TPM_TIS=$(CONFIG_TPM) CONFIG_MC146818RTC=y CONFIG_PCI_PIIX=y CONFIG_WDT_IB700=y -CONFIG_XEN_I386=$(CONFIG_XEN) CONFIG_ISA_DEBUG=y CONFIG_ISA_TESTDEV=y CONFIG_VMPORT=y diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 09c1d45633..1f1cd85b12 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -45,6 +45,7 @@ CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y CONFIG_LIBDECNUMBER=y +CONFIG_SM501=y # For PReP CONFIG_SERIAL_ISA=y CONFIG_MC146818RTC=y diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 05c83356e1..46c9599321 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -6,6 +6,10 @@ include usb.mak CONFIG_VIRTIO_VGA=y CONFIG_ESCC=y CONFIG_M48T59=y +CONFIG_IPMI=y +CONFIG_IPMI_LOCAL=y +CONFIG_IPMI_EXTERN=y +CONFIG_ISA_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y @@ -47,6 +51,7 @@ CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y CONFIG_LIBDECNUMBER=y +CONFIG_SM501=y # For pSeries CONFIG_XICS=$(CONFIG_PSERIES) CONFIG_XICS_SPAPR=$(CONFIG_PSERIES) diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index 7f56004cda..94340de356 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -15,3 +15,4 @@ CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_LIBDECNUMBER=y +CONFIG_SM501=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index d1d7432f74..9bde2f1c4b 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -39,7 +39,6 @@ CONFIG_TPM_TIS=$(CONFIG_TPM) CONFIG_MC146818RTC=y CONFIG_PCI_PIIX=y CONFIG_WDT_IB700=y -CONFIG_XEN_I386=$(CONFIG_XEN) CONFIG_ISA_DEBUG=y CONFIG_ISA_TESTDEV=y CONFIG_VMPORT=y @@ -223,6 +223,12 @@ struct CPUAddressSpace { MemoryListener tcg_as_listener; }; +struct DirtyBitmapSnapshot { + ram_addr_t start; + ram_addr_t end; + unsigned long dirty[]; +}; + #endif #if !defined(CONFIG_USER_ONLY) @@ -1061,6 +1067,75 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, return dirty; } +DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty + (ram_addr_t start, ram_addr_t length, unsigned client) +{ + DirtyMemoryBlocks *blocks; + unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL); + ram_addr_t first = QEMU_ALIGN_DOWN(start, align); + ram_addr_t last = QEMU_ALIGN_UP(start + length, align); + DirtyBitmapSnapshot *snap; + unsigned long page, end, dest; + + snap = g_malloc0(sizeof(*snap) + + ((last - first) >> (TARGET_PAGE_BITS + 3))); + snap->start = first; + snap->end = last; + + page = first >> TARGET_PAGE_BITS; + end = last >> TARGET_PAGE_BITS; + dest = 0; + + rcu_read_lock(); + + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + + while (page < end) { + unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; + unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; + unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset); + + assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL))); + assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL))); + offset >>= BITS_PER_LEVEL; + + bitmap_copy_and_clear_atomic(snap->dirty + dest, + blocks->blocks[idx] + offset, + num); + page += num; + dest += num >> BITS_PER_LEVEL; + } + + rcu_read_unlock(); + + if (tcg_enabled()) { + tlb_reset_dirty_range_all(start, length); + } + + return snap; +} + +bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, + ram_addr_t start, + ram_addr_t length) +{ + unsigned long page, end; + + assert(start >= snap->start); + assert(start + length <= snap->end); + + end = TARGET_PAGE_ALIGN(start + length - snap->start) >> TARGET_PAGE_BITS; + page = (start - snap->start) >> TARGET_PAGE_BITS; + + while (page < end) { + if (test_bit(page, snap->dirty)) { + return true; + } + page++; + } + return false; +} + /* Called from RCU critical section */ hwaddr memory_region_section_get_iotlb(CPUState *cpu, MemoryRegionSection *section, @@ -1528,7 +1603,7 @@ static ram_addr_t find_ram_offset(ram_addr_t size) return offset; } -ram_addr_t last_ram_offset(void) +unsigned long last_ram_page(void) { RAMBlock *block; ram_addr_t last = 0; @@ -1538,7 +1613,7 @@ ram_addr_t last_ram_offset(void) last = MAX(last, block->offset + block->max_length); } rcu_read_unlock(); - return last; + return last >> TARGET_PAGE_BITS; } static void qemu_ram_setup_dump(void *addr, ram_addr_t size) @@ -1727,7 +1802,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) ram_addr_t old_ram_size, new_ram_size; Error *err = NULL; - old_ram_size = last_ram_offset() >> TARGET_PAGE_BITS; + old_ram_size = last_ram_page(); qemu_mutex_lock_ramlist(); new_block->offset = find_ram_offset(new_block->max_length); @@ -1758,7 +1833,6 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) new_ram_size = MAX(old_ram_size, (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS); if (new_ram_size > old_ram_size) { - migration_bitmap_extend(old_ram_size, new_ram_size); dirty_memory_extend(old_ram_size, new_ram_size); } /* Keep the list sorted from biggest to smallest block. Unlike QTAILQ, @@ -3307,9 +3381,9 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, * Allows code that needs to deal with migration bitmaps etc to still be built * target independent. */ -size_t qemu_target_page_bits(void) +size_t qemu_target_page_size(void) { - return TARGET_PAGE_BITS; + return TARGET_PAGE_SIZE; } #endif diff --git a/hmp-commands.hx b/hmp-commands.hx index 88192817b2..0aca984261 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -526,6 +526,38 @@ Dump 80 16 bit values at the start of the video memory. ETEXI { + .name = "gpa2hva", + .args_type = "addr:l", + .params = "addr", + .help = "print the host virtual address corresponding to a guest physical address", + .cmd = hmp_gpa2hva, + }, + +STEXI +@item gpa2hva @var{addr} +@findex gpa2hva +Print the host virtual address at which the guest's physical address @var{addr} +is mapped. +ETEXI + +#ifdef CONFIG_LINUX + { + .name = "gpa2hpa", + .args_type = "addr:l", + .params = "addr", + .help = "print the host physical address corresponding to a guest physical address", + .cmd = hmp_gpa2hpa, + }, +#endif + +STEXI +@item gpa2hpa @var{addr} +@findex gpa2hpa +Print the host physical address at which the guest's physical address @var{addr} +is mapped. +ETEXI + + { .name = "p|print", .args_type = "fmt:/,val:l", .params = "/fmt expr", @@ -215,6 +215,9 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->ram->normal_bytes >> 10); monitor_printf(mon, "dirty sync count: %" PRIu64 "\n", info->ram->dirty_sync_count); + monitor_printf(mon, "page size: %" PRIu64 " kbytes\n", + info->ram->page_size >> 10); + if (info->ram->dirty_pages_rate) { monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", info->ram->dirty_pages_rate); @@ -265,13 +268,11 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict) caps = qmp_query_migrate_capabilities(NULL); if (caps) { - monitor_printf(mon, "capabilities: "); for (cap = caps; cap; cap = cap->next) { - monitor_printf(mon, "%s: %s ", + monitor_printf(mon, "%s: %s\n", MigrationCapability_lookup[cap->value->capability], cap->value->state ? "on" : "off"); } - monitor_printf(mon, "\n"); } qapi_free_MigrationCapabilityStatusList(caps); @@ -284,46 +285,44 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) params = qmp_query_migrate_parameters(NULL); if (params) { - monitor_printf(mon, "parameters:"); assert(params->has_compress_level); - monitor_printf(mon, " %s: %" PRId64, + monitor_printf(mon, "%s: %" PRId64 "\n", MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_LEVEL], params->compress_level); assert(params->has_compress_threads); - monitor_printf(mon, " %s: %" PRId64, + monitor_printf(mon, "%s: %" PRId64 "\n", MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_THREADS], params->compress_threads); assert(params->has_decompress_threads); - monitor_printf(mon, " %s: %" PRId64, + monitor_printf(mon, "%s: %" PRId64 "\n", MigrationParameter_lookup[MIGRATION_PARAMETER_DECOMPRESS_THREADS], params->decompress_threads); assert(params->has_cpu_throttle_initial); - monitor_printf(mon, " %s: %" PRId64, + monitor_printf(mon, "%s: %" PRId64 "\n", MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL], params->cpu_throttle_initial); assert(params->has_cpu_throttle_increment); - monitor_printf(mon, " %s: %" PRId64, + monitor_printf(mon, "%s: %" PRId64 "\n", MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT], params->cpu_throttle_increment); - monitor_printf(mon, " %s: '%s'", + monitor_printf(mon, "%s: '%s'\n", MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_CREDS], params->has_tls_creds ? params->tls_creds : ""); - monitor_printf(mon, " %s: '%s'", + monitor_printf(mon, "%s: '%s'\n", MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_HOSTNAME], params->has_tls_hostname ? params->tls_hostname : ""); assert(params->has_max_bandwidth); - monitor_printf(mon, " %s: %" PRId64 " bytes/second", + monitor_printf(mon, "%s: %" PRId64 " bytes/second\n", MigrationParameter_lookup[MIGRATION_PARAMETER_MAX_BANDWIDTH], params->max_bandwidth); assert(params->has_downtime_limit); - monitor_printf(mon, " %s: %" PRId64 " milliseconds", + monitor_printf(mon, "%s: %" PRId64 " milliseconds\n", MigrationParameter_lookup[MIGRATION_PARAMETER_DOWNTIME_LIMIT], params->downtime_limit); assert(params->has_x_checkpoint_delay); - monitor_printf(mon, " %s: %" PRId64, + monitor_printf(mon, "%s: %" PRId64 "\n", MigrationParameter_lookup[MIGRATION_PARAMETER_X_CHECKPOINT_DELAY], params->x_checkpoint_delay); - monitor_printf(mon, "\n"); } qapi_free_MigrationParameters(params); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index b7e836251e..5312d8a424 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -119,6 +119,12 @@ static inline char *rpath(FsContext *ctx, const char *path) typedef struct V9fsPDU V9fsPDU; struct V9fsState; +typedef struct { + uint32_t size_le; + uint8_t id; + uint16_t tag_le; +} QEMU_PACKED P9MsgHeader; + struct V9fsPDU { uint32_t size; diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs index 32197e6671..cab5e942ed 100644 --- a/hw/9pfs/Makefile.objs +++ b/hw/9pfs/Makefile.objs @@ -5,5 +5,6 @@ common-obj-y += coth.o cofs.o codir.o cofile.o common-obj-y += coxattr.o 9p-synth.o common-obj-$(CONFIG_OPEN_BY_HANDLE) += 9p-handle.o common-obj-y += 9p-proxy.o +common-obj-$(CONFIG_XEN) += xen-9p-backend.o obj-y += virtio-9p-device.o diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 27a4a32f5c..3782f43702 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -46,11 +46,7 @@ static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) VirtQueueElement *elem; while ((pdu = pdu_alloc(s))) { - struct { - uint32_t size_le; - uint8_t id; - uint16_t tag_le; - } QEMU_PACKED out; + P9MsgHeader out; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c new file mode 100644 index 0000000000..9c7f41af99 --- /dev/null +++ b/hw/9pfs/xen-9p-backend.c @@ -0,0 +1,440 @@ +/* + * Xen 9p backend + * + * Copyright Aporeto 2017 + * + * Authors: + * Stefano Stabellini <stefano@aporeto.com> + * + */ + +#include "qemu/osdep.h" + +#include "hw/hw.h" +#include "hw/9pfs/9p.h" +#include "hw/xen/xen_backend.h" +#include "hw/9pfs/xen-9pfs.h" +#include "qemu/config-file.h" +#include "fsdev/qemu-fsdev.h" + +#define VERSIONS "1" +#define MAX_RINGS 8 +#define MAX_RING_ORDER 8 + +typedef struct Xen9pfsRing { + struct Xen9pfsDev *priv; + + int ref; + xenevtchn_handle *evtchndev; + int evtchn; + int local_port; + int ring_order; + struct xen_9pfs_data_intf *intf; + unsigned char *data; + struct xen_9pfs_data ring; + + struct iovec *sg; + QEMUBH *bh; + + /* local copies, so that we can read/write PDU data directly from + * the ring */ + RING_IDX out_cons, out_size, in_cons; + bool inprogress; +} Xen9pfsRing; + +typedef struct Xen9pfsDev { + struct XenDevice xendev; /* must be first */ + V9fsState state; + char *path; + char *security_model; + char *tag; + char *id; + + int num_rings; + Xen9pfsRing *rings; +} Xen9pfsDev; + +static void xen_9pfs_in_sg(Xen9pfsRing *ring, + struct iovec *in_sg, + int *num, + uint32_t idx, + uint32_t size) +{ + RING_IDX cons, prod, masked_prod, masked_cons; + + cons = ring->intf->in_cons; + prod = ring->intf->in_prod; + xen_rmb(); + masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order)); + masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order)); + + if (masked_prod < masked_cons) { + in_sg[0].iov_base = ring->ring.in + masked_prod; + in_sg[0].iov_len = masked_cons - masked_prod; + *num = 1; + } else { + in_sg[0].iov_base = ring->ring.in + masked_prod; + in_sg[0].iov_len = XEN_FLEX_RING_SIZE(ring->ring_order) - masked_prod; + in_sg[1].iov_base = ring->ring.in; + in_sg[1].iov_len = masked_cons; + *num = 2; + } +} + +static void xen_9pfs_out_sg(Xen9pfsRing *ring, + struct iovec *out_sg, + int *num, + uint32_t idx) +{ + RING_IDX cons, prod, masked_prod, masked_cons; + + cons = ring->intf->out_cons; + prod = ring->intf->out_prod; + xen_rmb(); + masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order)); + masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order)); + + if (masked_cons < masked_prod) { + out_sg[0].iov_base = ring->ring.out + masked_cons; + out_sg[0].iov_len = ring->out_size; + *num = 1; + } else { + if (ring->out_size > + (XEN_FLEX_RING_SIZE(ring->ring_order) - masked_cons)) { + out_sg[0].iov_base = ring->ring.out + masked_cons; + out_sg[0].iov_len = XEN_FLEX_RING_SIZE(ring->ring_order) - + masked_cons; + out_sg[1].iov_base = ring->ring.out; + out_sg[1].iov_len = ring->out_size - + (XEN_FLEX_RING_SIZE(ring->ring_order) - + masked_cons); + *num = 2; + } else { + out_sg[0].iov_base = ring->ring.out + masked_cons; + out_sg[0].iov_len = ring->out_size; + *num = 1; + } + } +} + +static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, + size_t offset, + const char *fmt, + va_list ap) +{ + Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); + struct iovec in_sg[2]; + int num; + + xen_9pfs_in_sg(&xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings], + in_sg, &num, pdu->idx, ROUND_UP(offset + 128, 512)); + return v9fs_iov_vmarshal(in_sg, num, offset, 0, fmt, ap); +} + +static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, + size_t offset, + const char *fmt, + va_list ap) +{ + Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); + struct iovec out_sg[2]; + int num; + + xen_9pfs_out_sg(&xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings], + out_sg, &num, pdu->idx); + return v9fs_iov_vunmarshal(out_sg, num, offset, 0, fmt, ap); +} + +static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov) +{ + Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); + Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; + int num; + + g_free(ring->sg); + + ring->sg = g_malloc0(sizeof(*ring->sg) * 2); + xen_9pfs_out_sg(ring, ring->sg, &num, pdu->idx); + *piov = ring->sg; + *pniov = num; +} + +static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size) +{ + Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); + Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; + int num; + + g_free(ring->sg); + + ring->sg = g_malloc0(sizeof(*ring->sg) * 2); + xen_9pfs_in_sg(ring, ring->sg, &num, pdu->idx, size); + *piov = ring->sg; + *pniov = num; +} + +static void xen_9pfs_push_and_notify(V9fsPDU *pdu) +{ + RING_IDX prod; + Xen9pfsDev *priv = container_of(pdu->s, Xen9pfsDev, state); + Xen9pfsRing *ring = &priv->rings[pdu->tag % priv->num_rings]; + + g_free(ring->sg); + ring->sg = NULL; + + ring->intf->out_cons = ring->out_cons; + xen_wmb(); + + prod = ring->intf->in_prod; + xen_rmb(); + ring->intf->in_prod = prod + pdu->size; + xen_wmb(); + + ring->inprogress = false; + xenevtchn_notify(ring->evtchndev, ring->local_port); + + qemu_bh_schedule(ring->bh); +} + +static const struct V9fsTransport xen_9p_transport = { + .pdu_vmarshal = xen_9pfs_pdu_vmarshal, + .pdu_vunmarshal = xen_9pfs_pdu_vunmarshal, + .init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu, + .init_out_iov_from_pdu = xen_9pfs_init_out_iov_from_pdu, + .push_and_notify = xen_9pfs_push_and_notify, +}; + +static int xen_9pfs_init(struct XenDevice *xendev) +{ + return 0; +} + +static int xen_9pfs_receive(Xen9pfsRing *ring) +{ + P9MsgHeader h; + RING_IDX cons, prod, masked_prod, masked_cons; + V9fsPDU *pdu; + + if (ring->inprogress) { + return 0; + } + + cons = ring->intf->out_cons; + prod = ring->intf->out_prod; + xen_rmb(); + + if (xen_9pfs_queued(prod, cons, XEN_FLEX_RING_SIZE(ring->ring_order)) < + sizeof(h)) { + return 0; + } + ring->inprogress = true; + + masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order)); + masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order)); + + xen_9pfs_read_packet((uint8_t *) &h, ring->ring.out, sizeof(h), + masked_prod, &masked_cons, + XEN_FLEX_RING_SIZE(ring->ring_order)); + + /* cannot fail, because we only handle one request per ring at a time */ + pdu = pdu_alloc(&ring->priv->state); + pdu->size = le32_to_cpu(h.size_le); + pdu->id = h.id; + pdu->tag = le32_to_cpu(h.tag_le); + ring->out_size = le32_to_cpu(h.size_le); + ring->out_cons = cons + le32_to_cpu(h.size_le); + + qemu_co_queue_init(&pdu->complete); + pdu_submit(pdu); + + return 0; +} + +static void xen_9pfs_bh(void *opaque) +{ + Xen9pfsRing *ring = opaque; + xen_9pfs_receive(ring); +} + +static void xen_9pfs_evtchn_event(void *opaque) +{ + Xen9pfsRing *ring = opaque; + evtchn_port_t port; + + port = xenevtchn_pending(ring->evtchndev); + xenevtchn_unmask(ring->evtchndev, port); + + qemu_bh_schedule(ring->bh); +} + +static int xen_9pfs_free(struct XenDevice *xendev) +{ + int i; + Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); + + g_free(xen_9pdev->id); + g_free(xen_9pdev->tag); + g_free(xen_9pdev->path); + g_free(xen_9pdev->security_model); + + for (i = 0; i < xen_9pdev->num_rings; i++) { + if (xen_9pdev->rings[i].data != NULL) { + xengnttab_unmap(xen_9pdev->xendev.gnttabdev, + xen_9pdev->rings[i].data, + (1 << xen_9pdev->rings[i].ring_order)); + } + if (xen_9pdev->rings[i].intf != NULL) { + xengnttab_unmap(xen_9pdev->xendev.gnttabdev, + xen_9pdev->rings[i].intf, + 1); + } + if (xen_9pdev->rings[i].evtchndev > 0) { + qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), + NULL, NULL, NULL); + xenevtchn_unbind(xen_9pdev->rings[i].evtchndev, + xen_9pdev->rings[i].local_port); + } + if (xen_9pdev->rings[i].bh != NULL) { + qemu_bh_delete(xen_9pdev->rings[i].bh); + } + } + g_free(xen_9pdev->rings); + return 0; +} + +static int xen_9pfs_connect(struct XenDevice *xendev) +{ + int i; + Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); + V9fsState *s = &xen_9pdev->state; + QemuOpts *fsdev; + + if (xenstore_read_fe_int(&xen_9pdev->xendev, "num-rings", + &xen_9pdev->num_rings) == -1 || + xen_9pdev->num_rings > MAX_RINGS || xen_9pdev->num_rings < 1) { + return -1; + } + + xen_9pdev->rings = g_malloc0(xen_9pdev->num_rings * sizeof(Xen9pfsRing)); + for (i = 0; i < xen_9pdev->num_rings; i++) { + char *str; + int ring_order; + + xen_9pdev->rings[i].priv = xen_9pdev; + xen_9pdev->rings[i].evtchn = -1; + xen_9pdev->rings[i].local_port = -1; + + str = g_strdup_printf("ring-ref%u", i); + if (xenstore_read_fe_int(&xen_9pdev->xendev, str, + &xen_9pdev->rings[i].ref) == -1) { + goto out; + } + g_free(str); + str = g_strdup_printf("event-channel-%u", i); + if (xenstore_read_fe_int(&xen_9pdev->xendev, str, + &xen_9pdev->rings[i].evtchn) == -1) { + goto out; + } + g_free(str); + + xen_9pdev->rings[i].intf = xengnttab_map_grant_ref( + xen_9pdev->xendev.gnttabdev, + xen_9pdev->xendev.dom, + xen_9pdev->rings[i].ref, + PROT_READ | PROT_WRITE); + if (!xen_9pdev->rings[i].intf) { + goto out; + } + ring_order = xen_9pdev->rings[i].intf->ring_order; + if (ring_order > MAX_RING_ORDER) { + goto out; + } + xen_9pdev->rings[i].ring_order = ring_order; + xen_9pdev->rings[i].data = xengnttab_map_domain_grant_refs( + xen_9pdev->xendev.gnttabdev, + (1 << ring_order), + xen_9pdev->xendev.dom, + xen_9pdev->rings[i].intf->ref, + PROT_READ | PROT_WRITE); + if (!xen_9pdev->rings[i].data) { + goto out; + } + xen_9pdev->rings[i].ring.in = xen_9pdev->rings[i].data; + xen_9pdev->rings[i].ring.out = xen_9pdev->rings[i].data + + XEN_FLEX_RING_SIZE(ring_order); + + xen_9pdev->rings[i].bh = qemu_bh_new(xen_9pfs_bh, &xen_9pdev->rings[i]); + xen_9pdev->rings[i].out_cons = 0; + xen_9pdev->rings[i].out_size = 0; + xen_9pdev->rings[i].inprogress = false; + + + xen_9pdev->rings[i].evtchndev = xenevtchn_open(NULL, 0); + if (xen_9pdev->rings[i].evtchndev == NULL) { + goto out; + } + fcntl(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), F_SETFD, FD_CLOEXEC); + xen_9pdev->rings[i].local_port = xenevtchn_bind_interdomain + (xen_9pdev->rings[i].evtchndev, + xendev->dom, + xen_9pdev->rings[i].evtchn); + if (xen_9pdev->rings[i].local_port == -1) { + xen_pv_printf(xendev, 0, + "xenevtchn_bind_interdomain failed port=%d\n", + xen_9pdev->rings[i].evtchn); + goto out; + } + xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); + qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), + xen_9pfs_evtchn_event, NULL, &xen_9pdev->rings[i]); + } + + xen_9pdev->security_model = xenstore_read_be_str(xendev, "security_model"); + xen_9pdev->path = xenstore_read_be_str(xendev, "path"); + xen_9pdev->id = s->fsconf.fsdev_id = + g_strdup_printf("xen9p%d", xendev->dev); + xen_9pdev->tag = s->fsconf.tag = xenstore_read_fe_str(xendev, "tag"); + v9fs_register_transport(s, &xen_9p_transport); + fsdev = qemu_opts_create(qemu_find_opts("fsdev"), + s->fsconf.tag, + 1, NULL); + qemu_opt_set(fsdev, "fsdriver", "local", NULL); + qemu_opt_set(fsdev, "path", xen_9pdev->path, NULL); + qemu_opt_set(fsdev, "security_model", xen_9pdev->security_model, NULL); + qemu_opts_set_id(fsdev, s->fsconf.fsdev_id); + qemu_fsdev_add(fsdev); + v9fs_device_realize_common(s, NULL); + + return 0; + +out: + xen_9pfs_free(xendev); + return -1; +} + +static void xen_9pfs_alloc(struct XenDevice *xendev) +{ + xenstore_write_be_str(xendev, "versions", VERSIONS); + xenstore_write_be_int(xendev, "max-rings", MAX_RINGS); + xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER); +} + +static void xen_9pfs_disconnect(struct XenDevice *xendev) +{ + /* Dynamic hotplug of PV filesystems at runtime is not supported. */ +} + +struct XenDevOps xen_9pfs_ops = { + .size = sizeof(Xen9pfsDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .alloc = xen_9pfs_alloc, + .init = xen_9pfs_init, + .initialise = xen_9pfs_connect, + .disconnect = xen_9pfs_disconnect, + .free = xen_9pfs_free, +}; diff --git a/hw/9pfs/xen-9pfs.h b/hw/9pfs/xen-9pfs.h new file mode 100644 index 0000000000..2d6ef7828c --- /dev/null +++ b/hw/9pfs/xen-9pfs.h @@ -0,0 +1,21 @@ +/* + * Xen 9p backend + * + * Copyright Aporeto 2017 + * + * Authors: + * Stefano Stabellini <stefano@aporeto.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 <xen/io/protocols.h> +#include "hw/xen/io/ring.h" + +/* + * Do not merge into xen-9p-backend.c: clang doesn't allow unused static + * inline functions in c files. + */ +DEFINE_XEN_FLEX_RING_AND_INTF(xen_9pfs); diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index ca15d1c8cc..f62a9a3541 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -118,12 +118,6 @@ static void aw_a10_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = aw_a10_realize; - - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo aw_a10_type_info = { diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 571e4f097b..4937e2bc83 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -19,6 +19,7 @@ #include "hw/char/serial.h" #include "qemu/log.h" #include "hw/i2c/aspeed_i2c.h" +#include "net/net.h" #define ASPEED_SOC_UART_5_BASE 0x00184000 #define ASPEED_SOC_IOMEM_SIZE 0x00200000 @@ -33,6 +34,8 @@ #define ASPEED_SOC_TIMER_BASE 0x1E782000 #define ASPEED_SOC_WDT_BASE 0x1E785000 #define ASPEED_SOC_I2C_BASE 0x1E78A000 +#define ASPEED_SOC_ETH1_BASE 0x1E660000 +#define ASPEED_SOC_ETH2_BASE 0x1E680000 static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, }; @@ -175,6 +178,10 @@ static void aspeed_soc_init(Object *obj) object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ASPEED_WDT); object_property_add_child(obj, "wdt", OBJECT(&s->wdt), NULL); qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default()); + + object_initialize(&s->ftgmac100, sizeof(s->ftgmac100), TYPE_FTGMAC100); + object_property_add_child(obj, "ftgmac100", OBJECT(&s->ftgmac100), NULL); + qdev_set_parent_bus(DEVICE(&s->ftgmac100), sysbus_get_default()); } static void aspeed_soc_realize(DeviceState *dev, Error **errp) @@ -299,6 +306,20 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, ASPEED_SOC_WDT_BASE); + + /* Net */ + qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]); + object_property_set_bool(OBJECT(&s->ftgmac100), true, "aspeed", &err); + object_property_set_bool(OBJECT(&s->ftgmac100), true, "realized", + &local_err); + error_propagate(&err, local_err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100), 0, ASPEED_SOC_ETH1_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100), 0, + qdev_get_gpio_in(DEVICE(&s->vic), 2)); } static void aspeed_soc_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 8451190a19..8c43291112 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -160,12 +160,6 @@ static void bcm2836_class_init(ObjectClass *oc, void *data) dc->props = bcm2836_props; dc->realize = bcm2836_realize; - - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo bcm2836_type_info = { diff --git a/hw/arm/boot.c b/hw/arm/boot.c index ff621e4b6a..c2720c8046 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -31,6 +31,9 @@ #define KERNEL_LOAD_ADDR 0x00010000 #define KERNEL64_LOAD_ADDR 0x00080000 +#define ARM64_TEXT_OFFSET_OFFSET 8 +#define ARM64_MAGIC_OFFSET 56 + typedef enum { FIXUP_NONE = 0, /* do nothing */ FIXUP_TERMINATOR, /* end of insns */ @@ -768,6 +771,49 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, return ret; } +static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, + hwaddr *entry) +{ + hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR; + uint8_t *buffer; + int size; + + /* On aarch64, it's the bootloader's job to uncompress the kernel. */ + size = load_image_gzipped_buffer(filename, LOAD_IMAGE_MAX_GUNZIP_BYTES, + &buffer); + + if (size < 0) { + gsize len; + + /* Load as raw file otherwise */ + if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) { + return -1; + } + size = len; + } + + /* check the arm64 magic header value -- very old kernels may not have it */ + if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) { + uint64_t hdrvals[2]; + + /* The arm64 Image header has text_offset and image_size fields at 8 and + * 16 bytes into the Image header, respectively. The text_offset field + * is only valid if the image_size is non-zero. + */ + memcpy(&hdrvals, buffer + ARM64_TEXT_OFFSET_OFFSET, sizeof(hdrvals)); + if (hdrvals[1] != 0) { + kernel_load_offset = le64_to_cpu(hdrvals[0]); + } + } + + *entry = mem_base + kernel_load_offset; + rom_add_blob_fixed(filename, buffer, size, *entry); + + g_free(buffer); + + return size; +} + static void arm_load_kernel_notify(Notifier *notifier, void *data) { CPUState *cs; @@ -776,7 +822,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) int is_linux = 0; uint64_t elf_entry, elf_low_addr, elf_high_addr; int elf_machine; - hwaddr entry, kernel_load_offset; + hwaddr entry; static const ARMInsnFixup *primary_loader; ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, notifier, notifier); @@ -841,14 +887,12 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { primary_loader = bootloader_aarch64; - kernel_load_offset = KERNEL64_LOAD_ADDR; elf_machine = EM_AARCH64; } else { primary_loader = bootloader; if (!info->write_board_setup) { primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET; } - kernel_load_offset = KERNEL_LOAD_ADDR; elf_machine = EM_ARM; } @@ -900,17 +944,15 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) kernel_size = load_uimage(info->kernel_filename, &entry, NULL, &is_linux, NULL, NULL); } - /* On aarch64, it's the bootloader's job to uncompress the kernel. */ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) { - entry = info->loader_start + kernel_load_offset; - kernel_size = load_image_gzipped(info->kernel_filename, entry, - info->ram_size - kernel_load_offset); + kernel_size = load_aarch64_image(info->kernel_filename, + info->loader_start, &entry); is_linux = 1; - } - if (kernel_size < 0) { - entry = info->loader_start + kernel_load_offset; + } else if (kernel_size < 0) { + /* 32-bit ARM */ + entry = info->loader_start + KERNEL_LOAD_ADDR; kernel_size = load_image_targphys(info->kernel_filename, entry, - info->ram_size - kernel_load_offset); + info->ram_size - KERNEL_LOAD_ADDR); is_linux = 1; } if (kernel_size < 0) { diff --git a/hw/arm/digic.c b/hw/arm/digic.c index d60ea395f4..94f32637f0 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -101,12 +101,6 @@ static void digic_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = digic_realize; - - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo digic_type_info = { diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 1d2b50cc4e..960f27e45a 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -32,6 +32,7 @@ #include "hw/arm/arm.h" #include "hw/loader.h" #include "hw/arm/exynos4210.h" +#include "hw/sd/sd.h" #include "hw/usb/hcd-ehci.h" #define EXYNOS4210_CHIPID_ADDR 0x10000000 @@ -72,6 +73,13 @@ #define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000 #define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000 +/* SD/MMC host controllers */ +#define EXYNOS4210_SDHCI_CAPABILITIES 0x05E80080 +#define EXYNOS4210_SDHCI_BASE_ADDR 0x12510000 +#define EXYNOS4210_SDHCI_ADDR(n) (EXYNOS4210_SDHCI_BASE_ADDR + \ + 0x00010000 * (n)) +#define EXYNOS4210_SDHCI_NUMBER 4 + /* PMU SFR base address */ #define EXYNOS4210_PMU_BASE_ADDR 0x10020000 @@ -382,6 +390,27 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, EXYNOS4210_UART3_FIFO_SIZE, 3, NULL, s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]); + /*** SD/MMC host controllers ***/ + for (n = 0; n < EXYNOS4210_SDHCI_NUMBER; n++) { + DeviceState *carddev; + BlockBackend *blk; + DriveInfo *di; + + dev = qdev_create(NULL, "generic-sdhci"); + qdev_prop_set_uint32(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES); + qdev_init_nofail(dev); + + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, EXYNOS4210_SDHCI_ADDR(n)); + sysbus_connect_irq(busdev, 0, s->irq_table[exynos4210_get_irq(29, n)]); + + di = drive_get(IF_SD, 0, n); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_abort); + qdev_init_nofail(carddev); + } + /*** Display controller (FIMD) ***/ sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR, s->irq_table[exynos4210_get_irq(11, 0)], diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 0efa194054..4853c31802 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu-common.h" #include "cpu.h" #include "sysemu/sysemu.h" @@ -101,9 +102,9 @@ static Exynos4210State *exynos4_boards_init_common(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) { - fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus" - " value.\n", - mc->name, EXYNOS4210_NCPUS); + error_report("%s board supports only %d CPU cores, ignoring smp_cpus" + " value", + mc->name, EXYNOS4210_NCPUS); } exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type]; diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 2126f73ca0..9056f27bf8 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -290,11 +290,6 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data) dc->realize = fsl_imx25_realize; - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; dc->desc = "i.MX25 SOC"; } diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index dd1c713ae3..d7e2d832b2 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -262,11 +262,6 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data) dc->realize = fsl_imx31_realize; - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; dc->desc = "i.MX31 SOC"; } diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 76dd8a48ca..6969e734ad 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -442,11 +442,6 @@ static void fsl_imx6_class_init(ObjectClass *oc, void *data) dc->realize = fsl_imx6_realize; - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; dc->desc = "i.MX6 SOC"; } diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index cfee3929d9..eea551dc16 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -755,19 +755,18 @@ static void pxa2xx_ssp_reset(DeviceState *d) s->rx_start = s->rx_level = 0; } -static int pxa2xx_ssp_init(SysBusDevice *sbd) +static void pxa2xx_ssp_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - PXA2xxSSPState *s = PXA2XX_SSP(dev); - + DeviceState *dev = DEVICE(obj); + PXA2xxSSPState *s = PXA2XX_SSP(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_ssp_ops, s, "pxa2xx-ssp", 0x1000); sysbus_init_mmio(sbd, &s->iomem); s->bus = ssi_create_bus(dev, "ssi"); - return 0; } /* Real-Time Clock */ @@ -2321,10 +2320,8 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = pxa2xx_ssp_init; dc->reset = pxa2xx_ssp_reset; dc->vmsd = &vmstate_pxa2xx_ssp; } @@ -2333,6 +2330,7 @@ static const TypeInfo pxa2xx_ssp_info = { .name = TYPE_PXA2XX_SSP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxSSPState), + .instance_init = pxa2xx_ssp_init, .class_init = pxa2xx_ssp_class_init, }; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 9edcd49740..ea7a8094e1 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -108,7 +108,10 @@ static void gptm_reload(gptm_state *s, int n, int reset) } else if (s->mode[n] == 0xa) { /* PWM mode. Not implemented. */ } else { - hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]); + qemu_log_mask(LOG_UNIMP, + "GPTM: 16-bit timer mode unimplemented: 0x%x\n", + s->mode[n]); + return; } s->tick[n] = tick; timer_mod(s->timer[n], tick); @@ -149,7 +152,9 @@ static void gptm_tick(void *opaque) } else if (s->mode[n] == 0xa) { /* PWM mode. Not implemented. */ } else { - hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]); + qemu_log_mask(LOG_UNIMP, + "GPTM: 16-bit timer mode unimplemented: 0x%x\n", + s->mode[n]); } gptm_update_irq(s); } @@ -286,7 +291,8 @@ static void gptm_write(void *opaque, hwaddr offset, s->match_prescale[0] = value; break; default: - hw_error("gptm_write: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "GPTM: read at bad offset 0x%x\n", (int)offset); } gptm_update_irq(s); } @@ -425,7 +431,10 @@ static int ssys_board_class(const ssys_state *s) } /* for unknown classes, fall through */ default: - hw_error("ssys_board_class: Unknown class 0x%08x\n", did0); + /* This can only happen if the hardwired constant did0 value + * in this board's stellaris_board_info struct is wrong. + */ + g_assert_not_reached(); } } @@ -479,8 +488,7 @@ static uint64_t ssys_read(void *opaque, hwaddr offset, case DID0_CLASS_SANDSTORM: return pllcfg_sandstorm[xtal]; default: - hw_error("ssys_read: Unhandled class for PLLCFG read.\n"); - return 0; + g_assert_not_reached(); } } case 0x070: /* RCC2 */ @@ -512,7 +520,8 @@ static uint64_t ssys_read(void *opaque, hwaddr offset, case 0x1e4: /* USER1 */ return s->user1; default: - hw_error("ssys_read: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "SSYS: read at bad offset 0x%x\n", (int)offset); return 0; } } @@ -614,7 +623,8 @@ static void ssys_write(void *opaque, hwaddr offset, s->ldoarst = value; break; default: - hw_error("ssys_write: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "SSYS: write at bad offset 0x%x\n", (int)offset); } ssys_update(s); } @@ -748,7 +758,8 @@ static uint64_t stellaris_i2c_read(void *opaque, hwaddr offset, case 0x20: /* MCR */ return s->mcr; default: - hw_error("strllaris_i2c_read: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "stellaris_i2c: read at bad offset 0x%x\n", (int)offset); return 0; } } @@ -823,17 +834,18 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset, s->mris &= ~value; break; case 0x20: /* MCR */ - if (value & 1) - hw_error( - "stellaris_i2c_write: Loopback not implemented\n"); - if (value & 0x20) - hw_error( - "stellaris_i2c_write: Slave mode not implemented\n"); + if (value & 1) { + qemu_log_mask(LOG_UNIMP, "stellaris_i2c: Loopback not implemented"); + } + if (value & 0x20) { + qemu_log_mask(LOG_UNIMP, + "stellaris_i2c: Slave mode not implemented"); + } s->mcr = value & 0x31; break; default: - hw_error("stellaris_i2c_write: Bad offset 0x%x\n", - (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "stellaris_i2c: write at bad offset 0x%x\n", (int)offset); } stellaris_i2c_update(s); } @@ -1057,8 +1069,8 @@ static uint64_t stellaris_adc_read(void *opaque, hwaddr offset, case 0x30: /* SAC */ return s->sac; default: - hw_error("strllaris_adc_read: Bad offset 0x%x\n", - (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "stellaris_adc: read at bad offset 0x%x\n", (int)offset); return 0; } } @@ -1078,8 +1090,9 @@ static void stellaris_adc_write(void *opaque, hwaddr offset, return; case 0x04: /* SSCTL */ if (value != 6) { - hw_error("ADC: Unimplemented sequence %" PRIx64 "\n", - value); + qemu_log_mask(LOG_UNIMP, + "ADC: Unimplemented sequence %" PRIx64 "\n", + value); } s->ssctl[n] = value; return; @@ -1110,13 +1123,14 @@ static void stellaris_adc_write(void *opaque, hwaddr offset, s->sspri = value; break; case 0x28: /* PSSI */ - hw_error("Not implemented: ADC sample initiate\n"); + qemu_log_mask(LOG_UNIMP, "ADC: sample initiate unimplemented"); break; case 0x30: /* SAC */ s->sac = value; break; default: - hw_error("stellaris_adc_write: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "stellaris_adc: write at bad offset 0x%x\n", (int)offset); } stellaris_adc_update(s); } diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index bc4e66b862..64f52f80a5 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -30,6 +30,8 @@ #define ARM_PHYS_TIMER_PPI 30 #define ARM_VIRT_TIMER_PPI 27 +#define GEM_REVISION 0x40070106 + #define GIC_BASE_ADDR 0xf9000000 #define GIC_DIST_ADDR 0xf9010000 #define GIC_CPU_ADDR 0xf9020000 @@ -334,8 +336,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) qemu_check_nic_model(nd, TYPE_CADENCE_GEM); qdev_set_nic_properties(DEVICE(&s->gem[i]), nd); } + object_property_set_int(OBJECT(&s->gem[i]), GEM_REVISION, "revision", + &error_abort); object_property_set_int(OBJECT(&s->gem[i]), 2, "num-priority-queues", - &error_abort); + &error_abort); object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -439,12 +443,6 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data) dc->props = xlnx_zynqmp_props; dc->realize = xlnx_zynqmp_realize; - - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo xlnx_zynqmp_type_info = { diff --git a/hw/block/fdc.c b/hw/block/fdc.c index a328693d15..2e629b398b 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -2521,8 +2521,8 @@ static void fdctrl_result_timer(void *opaque) } /* Init functions */ -static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp, - DeviceState *fdc_dev) +static void fdctrl_connect_drives(FDCtrl *fdctrl, DeviceState *fdc_dev, + Error **errp) { unsigned int i; FDrive *drive; @@ -2675,7 +2675,7 @@ static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl, } floppy_bus_create(fdctrl, &fdctrl->bus, dev); - fdctrl_connect_drives(fdctrl, errp, dev); + fdctrl_connect_drives(fdctrl, dev, errp); } static const MemoryRegionPortio fdc_portio_list[] = { diff --git a/hw/block/xen_blkif.h b/hw/block/xen_blkif.h index 3300b6fc0a..3e6e1ea365 100644 --- a/hw/block/xen_blkif.h +++ b/hw/block/xen_blkif.h @@ -1,7 +1,7 @@ #ifndef XEN_BLKIF_H #define XEN_BLKIF_H -#include <xen/io/ring.h> +#include "hw/xen/io/ring.h" #include <xen/io/blkif.h> #include <xen/io/protocols.h> diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 456a2d5694..27df0486d9 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -492,7 +492,7 @@ static int ioreq_map(struct ioreq *ioreq) return 0; } -#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 480 +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 static void ioreq_free_copy_buffers(struct ioreq *ioreq) { diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index b75f28d473..bff706ab3a 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -102,7 +102,7 @@ typedef struct Exynos4210UartReg { uint32_t reset_value; } Exynos4210UartReg; -static Exynos4210UartReg exynos4210_uart_regs[] = { +static const Exynos4210UartReg exynos4210_uart_regs[] = { {"ULCON", ULCON, 0x00000000}, {"UCON", UCON, 0x00003000}, {"UFCON", UFCON, 0x00000000}, @@ -220,7 +220,7 @@ static uint8_t fifo_retrieve(Exynos4210UartFIFO *q) return ret; } -static int fifo_elements_number(Exynos4210UartFIFO *q) +static int fifo_elements_number(const Exynos4210UartFIFO *q) { if (q->sp < q->rp) { return q->size - q->rp + q->sp; @@ -229,7 +229,7 @@ static int fifo_elements_number(Exynos4210UartFIFO *q) return q->sp - q->rp; } -static int fifo_empty_elements_number(Exynos4210UartFIFO *q) +static int fifo_empty_elements_number(const Exynos4210UartFIFO *q) { return q->size - fifo_elements_number(q); } @@ -245,7 +245,7 @@ static void fifo_reset(Exynos4210UartFIFO *q) q->rp = 0; } -static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s) +static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(const Exynos4210UartState *s) { uint32_t level = 0; uint32_t reg; diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index 27c8369b57..864832db34 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -40,6 +40,12 @@ static void machine_none_init(MachineState *mch) memory_region_allocate_system_memory(ram, NULL, "ram", mch->ram_size); memory_region_add_subregion(get_system_memory(), 0, ram); } + + if (mch->kernel_filename) { + error_report("The -kernel parameter is not supported " + "(use the generic 'loader' device instead)."); + exit(1); + } } static void machine_none_machine_init(MachineClass *mc) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index e885e650fb..79c2014135 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -409,7 +409,7 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name, if (value) { ref = blk_name(value); if (!*ref) { - BlockDriverState *bs = blk_bs(value); + const BlockDriverState *bs = blk_bs(value); if (bs) { ref = bdrv_get_node_name(bs); } diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 6ab4265eb4..fa3617db2d 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1010,7 +1010,8 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value) object_property_set_str(OBJECT(dev), value, name, &error_abort); } -void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value) +void qdev_prop_set_macaddr(DeviceState *dev, const char *name, + const uint8_t *value) { char str[2 * 6 + 5 + 1]; snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 1e7fb33246..02b632f6b3 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -39,9 +39,9 @@ #include "qapi-event.h" #include "migration/migration.h" -int qdev_hotplug = 0; +bool qdev_hotplug = false; static bool qdev_hot_added = false; -static bool qdev_hot_removed = false; +bool qdev_hot_removed = false; const VMStateDescription *qdev_get_vmsd(DeviceState *dev) { @@ -271,40 +271,6 @@ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) return hotplug_ctrl; } -void qdev_unplug(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - HotplugHandler *hotplug_ctrl; - HotplugHandlerClass *hdc; - - if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); - return; - } - - if (!dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, - object_get_typename(OBJECT(dev))); - return; - } - - qdev_hot_removed = true; - - hotplug_ctrl = qdev_get_hotplug_handler(dev); - /* hotpluggable device MUST have HotplugHandler, if it doesn't - * then something is very wrong with it */ - g_assert(hotplug_ctrl); - - /* If device supports async unplug just request it to be done, - * otherwise just remove it synchronously */ - hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl); - if (hdc->unplug_request) { - hotplug_handler_unplug_request(hotplug_ctrl, dev, errp); - } else { - hotplug_handler_unplug(hotplug_ctrl, dev, errp); - } -} - static int qdev_reset_one(DeviceState *dev, void *opaque) { device_reset(dev); @@ -385,7 +351,7 @@ void qdev_machine_creation_done(void) * ok, initial machine setup is done, starting from now we can * only create hotpluggable devices */ - qdev_hotplug = 1; + qdev_hotplug = true; } bool qdev_machine_modified(void) @@ -1037,13 +1003,6 @@ static bool device_get_hotplugged(Object *obj, Error **err) return dev->hotplugged; } -static void device_set_hotplugged(Object *obj, bool value, Error **err) -{ - DeviceState *dev = DEVICE(obj); - - dev->hotplugged = value; -} - static void device_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); @@ -1063,7 +1022,7 @@ static void device_initfn(Object *obj) object_property_add_bool(obj, "hotpluggable", device_get_hotpluggable, NULL, NULL); object_property_add_bool(obj, "hotplugged", - device_get_hotplugged, device_set_hotplugged, + device_get_hotplugged, NULL, &error_abort); class = object_get_class(OBJECT(dev)); diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 1174220394..03d9197f71 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -26,7 +26,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" -#include "cpu.h" #include "qemu/error-report.h" #include "ui/console.h" #include "hw/sysbus.h" @@ -114,8 +113,8 @@ static void cg3_update_display(void *opaque) for (y = 0; y < height; y++) { int update = s->full_update; - page = (y * width) & TARGET_PAGE_MASK; - update |= memory_region_get_dirty(&s->vram_mem, page, page + width, + page = y * width; + update |= memory_region_get_dirty(&s->vram_mem, page, width, DIRTY_MEMORY_VGA); if (update) { if (y_start < 0) { @@ -148,8 +147,7 @@ static void cg3_update_display(void *opaque) } if (page_max >= page_min) { memory_region_reset_dirty(&s->vram_mem, - page_min, page_max - page_min + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); + page_min, page_max - page_min, DIRTY_MEMORY_VGA); } /* vsync interrupt? */ if (s->regs[0] & CG3_CR_ENABLE_INTS) { @@ -305,8 +303,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp) vmstate_register_ram_global(&s->rom); fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, CG3_ROM_FILE); if (fcode_filename) { - ret = load_image_targphys(fcode_filename, s->prom_addr, - FCODE_MAX_ROM_SIZE); + ret = load_image_mr(fcode_filename, &s->rom); g_free(fcode_filename); if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { error_report("cg3: could not load prom '%s'", CG3_ROM_FILE); @@ -371,7 +368,6 @@ static Property cg3_properties[] = { DEFINE_PROP_UINT16("width", CG3State, width, -1), DEFINE_PROP_UINT16("height", CG3State, height, -1), DEFINE_PROP_UINT16("depth", CG3State, depth, -1), - DEFINE_PROP_UINT64("prom-addr", CG3State, prom_addr, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index e5be713406..fd0b2bec65 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1263,6 +1263,7 @@ static void exynos4210_fimd_update(void *opaque) Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; DisplaySurface *surface; Exynos4210fimdWindow *w; + DirtyBitmapSnapshot *snap; int i, line; hwaddr fb_line_addr, inc_size; int scrn_height; @@ -1291,10 +1292,12 @@ static void exynos4210_fimd_update(void *opaque) memory_region_sync_dirty_bitmap(w->mem_section.mr); host_fb_addr = w->host_fb_addr; fb_line_addr = w->mem_section.offset_within_region; + snap = memory_region_snapshot_and_clear_dirty(w->mem_section.mr, + fb_line_addr, inc_size * scrn_height, DIRTY_MEMORY_VGA); for (line = 0; line < scrn_height; line++) { - is_dirty = memory_region_get_dirty(w->mem_section.mr, - fb_line_addr, scrn_width, DIRTY_MEMORY_VGA); + is_dirty = memory_region_snapshot_get_dirty(w->mem_section.mr, + snap, fb_line_addr, scrn_width); if (s->invalidate || is_dirty) { if (first_line == -1) { @@ -1309,9 +1312,7 @@ static void exynos4210_fimd_update(void *opaque) fb_line_addr += inc_size; is_dirty = false; } - memory_region_reset_dirty(w->mem_section.mr, - w->mem_section.offset_within_region, - w->fb_len, DIRTY_MEMORY_VGA); + g_free(snap); blend = true; } } diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c index 25aa46c8c7..d7310d25f2 100644 --- a/hw/display/framebuffer.c +++ b/hw/display/framebuffer.c @@ -67,7 +67,7 @@ void framebuffer_update_display( int *first_row, /* Input and output. */ int *last_row /* Output only */) { - hwaddr src_len; + DirtyBitmapSnapshot *snap; uint8_t *dest; uint8_t *src; int first, last = 0; @@ -78,7 +78,6 @@ void framebuffer_update_display( i = *first_row; *first_row = -1; - src_len = (hwaddr)src_width * rows; mem = mem_section->mr; if (!mem) { @@ -102,9 +101,10 @@ void framebuffer_update_display( src += i * src_width; dest += i * dest_row_pitch; + snap = memory_region_snapshot_and_clear_dirty(mem, addr, src_width * rows, + DIRTY_MEMORY_VGA); for (; i < rows; i++) { - dirty = memory_region_get_dirty(mem, addr, src_width, - DIRTY_MEMORY_VGA); + dirty = memory_region_snapshot_get_dirty(mem, snap, addr, src_width); if (dirty || invalidate) { fn(opaque, dest, src, cols, dest_col_pitch); if (first == -1) @@ -115,11 +115,10 @@ void framebuffer_update_display( src += src_width; dest += dest_row_pitch; } + g_free(snap); if (first < 0) { return; } - memory_region_reset_dirty(mem, mem_section->offset_within_region, src_len, - DIRTY_MEMORY_VGA); *first_row = first; *last_row = last; } diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 8cdc205dd9..86557d14a9 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -64,17 +64,8 @@ typedef struct G364State { static inline int check_dirty(G364State *s, ram_addr_t page) { - return memory_region_get_dirty(&s->mem_vram, page, G364_PAGE_SIZE, - DIRTY_MEMORY_VGA); -} - -static inline void reset_dirty(G364State *s, - ram_addr_t page_min, ram_addr_t page_max) -{ - memory_region_reset_dirty(&s->mem_vram, - page_min, - page_max + G364_PAGE_SIZE - page_min - 1, - DIRTY_MEMORY_VGA); + return memory_region_test_and_clear_dirty(&s->mem_vram, page, G364_PAGE_SIZE, + DIRTY_MEMORY_VGA); } static void g364fb_draw_graphic8(G364State *s) @@ -83,7 +74,7 @@ static void g364fb_draw_graphic8(G364State *s) int i, w; uint8_t *vram; uint8_t *data_display, *dd; - ram_addr_t page, page_min, page_max; + ram_addr_t page; int x, y; int xmin, xmax; int ymin, ymax; @@ -114,8 +105,6 @@ static void g364fb_draw_graphic8(G364State *s) } page = 0; - page_min = (ram_addr_t)-1; - page_max = 0; x = y = 0; xmin = s->width; @@ -137,9 +126,6 @@ static void g364fb_draw_graphic8(G364State *s) if (check_dirty(s, page)) { if (y < ymin) ymin = ymax = y; - if (page_min == (ram_addr_t)-1) - page_min = page; - page_max = page; if (x < xmin) xmin = x; for (i = 0; i < G364_PAGE_SIZE; i++) { @@ -196,10 +182,7 @@ static void g364fb_draw_graphic8(G364State *s) ymax = y; } else { int dy; - if (page_min != (ram_addr_t)-1) { - reset_dirty(s, page_min, page_max); - page_min = (ram_addr_t)-1; - page_max = 0; + if (xmax || ymax) { dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); xmin = s->width; @@ -219,9 +202,8 @@ static void g364fb_draw_graphic8(G364State *s) } done: - if (page_min != (ram_addr_t)-1) { + if (xmax || ymax) { dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); - reset_dirty(s, page_min, page_max); } } diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 9feae78e4e..4d94cecd72 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -305,6 +305,16 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl) qxl->ssd.cursor = cursor_builtin_hidden(); } +static uint32_t qxl_crc32(const uint8_t *p, unsigned len) +{ + /* + * zlib xors the seed with 0xffffffff, and xors the result + * again with 0xffffffff; Both are not done with linux's crc32, + * which we want to be compatible with, so undo that. + */ + return crc32(0xffffffff, p, len) ^ 0xffffffff; +} + static ram_addr_t qxl_rom_size(void) { #define QXL_REQUIRED_SZ (sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes)) @@ -369,6 +379,18 @@ static void init_qxl_rom(PCIQXLDevice *d) rom->num_pages = cpu_to_le32(num_pages); rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size); + if (d->xres && d->yres) { + /* needs linux kernel 4.12+ to work */ + rom->client_monitors_config.count = 1; + rom->client_monitors_config.heads[0].left = 0; + rom->client_monitors_config.heads[0].top = 0; + rom->client_monitors_config.heads[0].right = cpu_to_le32(d->xres); + rom->client_monitors_config.heads[0].bottom = cpu_to_le32(d->yres); + rom->client_monitors_config_crc = qxl_crc32( + (const uint8_t *)&rom->client_monitors_config, + sizeof(rom->client_monitors_config)); + } + d->shadow_rom = *rom; d->rom = rom; d->modes = modes; @@ -1011,16 +1033,6 @@ static void interface_set_client_capabilities(QXLInstance *sin, qxl_send_events(qxl, QXL_INTERRUPT_CLIENT); } -static uint32_t qxl_crc32(const uint8_t *p, unsigned len) -{ - /* - * zlib xors the seed with 0xffffffff, and xors the result - * again with 0xffffffff; Both are not done with linux's crc32, - * which we want to be compatible with, so undo that. - */ - return crc32(0xffffffff, p, len) ^ 0xffffffff; -} - static bool qxl_rom_monitors_config_changed(QXLRom *rom, VDAgentMonitorsConfig *monitors_config, unsigned int max_outputs) @@ -2397,6 +2409,8 @@ static Property qxl_properties[] = { #if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0), #endif + DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0), + DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 77e5a36dc5..f6556adb73 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -119,6 +119,8 @@ typedef struct PCIQXLDevice { uint32_t vram_size_mb; uint32_t vram32_size_mb; uint32_t vgamem_size_mb; + uint32_t xres; + uint32_t yres; /* qxl_render_update state */ int render_update_cookie_num; diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 040a0b93f2..2094adbc9c 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -2,6 +2,7 @@ * QEMU SM501 Device * * Copyright (c) 2008 Shin-ichiro KAWASAKI + * Copyright (c) 2016 BALATON Zoltan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -31,6 +33,7 @@ #include "ui/console.h" #include "hw/devices.h" #include "hw/sysbus.h" +#include "hw/pci/pci.h" #include "qemu/range.h" #include "ui/pixel_ops.h" #include "exec/address-spaces.h" @@ -38,10 +41,13 @@ /* * Status: 2010/05/07 * - Minimum implementation for Linux console : mmio regs and CRT layer. - * - 2D grapihcs acceleration partially supported : only fill rectangle. + * - 2D graphics acceleration partially supported : only fill rectangle. * - * TODO: + * Status: 2016/12/04 + * - Misc fixes: endianness, hardware cursor * - Panel support + * + * TODO: * - Touch panel support * - USB support * - UART support @@ -49,395 +55,396 @@ * - Performance tuning */ -//#define DEBUG_SM501 -//#define DEBUG_BITBLT +/*#define DEBUG_SM501*/ +/*#define DEBUG_BITBLT*/ #ifdef DEBUG_SM501 #define SM501_DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) #else -#define SM501_DPRINTF(fmt, ...) do {} while(0) +#define SM501_DPRINTF(fmt, ...) do {} while (0) #endif - #define MMIO_BASE_OFFSET 0x3e00000 +#define MMIO_SIZE 0x200000 +#define DC_PALETTE_ENTRIES (0x400 * 3) /* SM501 register definitions taken from "linux/include/linux/sm501-regs.h" */ /* System Configuration area */ /* System config base */ -#define SM501_SYS_CONFIG (0x000000) +#define SM501_SYS_CONFIG (0x000000) /* config 1 */ -#define SM501_SYSTEM_CONTROL (0x000000) +#define SM501_SYSTEM_CONTROL (0x000000) -#define SM501_SYSCTRL_PANEL_TRISTATE (1<<0) -#define SM501_SYSCTRL_MEM_TRISTATE (1<<1) -#define SM501_SYSCTRL_CRT_TRISTATE (1<<2) +#define SM501_SYSCTRL_PANEL_TRISTATE (1 << 0) +#define SM501_SYSCTRL_MEM_TRISTATE (1 << 1) +#define SM501_SYSCTRL_CRT_TRISTATE (1 << 2) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_MASK (3<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_1 (0<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_2 (1<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_4 (2<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_8 (3<<4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_MASK (3 << 4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_1 (0 << 4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_2 (1 << 4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_4 (2 << 4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_8 (3 << 4) -#define SM501_SYSCTRL_PCI_CLOCK_RUN_EN (1<<6) -#define SM501_SYSCTRL_PCI_RETRY_DISABLE (1<<7) -#define SM501_SYSCTRL_PCI_SUBSYS_LOCK (1<<11) -#define SM501_SYSCTRL_PCI_BURST_READ_EN (1<<15) +#define SM501_SYSCTRL_PCI_CLOCK_RUN_EN (1 << 6) +#define SM501_SYSCTRL_PCI_RETRY_DISABLE (1 << 7) +#define SM501_SYSCTRL_PCI_SUBSYS_LOCK (1 << 11) +#define SM501_SYSCTRL_PCI_BURST_READ_EN (1 << 15) /* miscellaneous control */ -#define SM501_MISC_CONTROL (0x000004) +#define SM501_MISC_CONTROL (0x000004) -#define SM501_MISC_BUS_SH (0x0) -#define SM501_MISC_BUS_PCI (0x1) -#define SM501_MISC_BUS_XSCALE (0x2) -#define SM501_MISC_BUS_NEC (0x6) -#define SM501_MISC_BUS_MASK (0x7) +#define SM501_MISC_BUS_SH (0x0) +#define SM501_MISC_BUS_PCI (0x1) +#define SM501_MISC_BUS_XSCALE (0x2) +#define SM501_MISC_BUS_NEC (0x6) +#define SM501_MISC_BUS_MASK (0x7) -#define SM501_MISC_VR_62MB (1<<3) -#define SM501_MISC_CDR_RESET (1<<7) -#define SM501_MISC_USB_LB (1<<8) -#define SM501_MISC_USB_SLAVE (1<<9) -#define SM501_MISC_BL_1 (1<<10) -#define SM501_MISC_MC (1<<11) -#define SM501_MISC_DAC_POWER (1<<12) -#define SM501_MISC_IRQ_INVERT (1<<16) -#define SM501_MISC_SH (1<<17) +#define SM501_MISC_VR_62MB (1 << 3) +#define SM501_MISC_CDR_RESET (1 << 7) +#define SM501_MISC_USB_LB (1 << 8) +#define SM501_MISC_USB_SLAVE (1 << 9) +#define SM501_MISC_BL_1 (1 << 10) +#define SM501_MISC_MC (1 << 11) +#define SM501_MISC_DAC_POWER (1 << 12) +#define SM501_MISC_IRQ_INVERT (1 << 16) +#define SM501_MISC_SH (1 << 17) -#define SM501_MISC_HOLD_EMPTY (0<<18) -#define SM501_MISC_HOLD_8 (1<<18) -#define SM501_MISC_HOLD_16 (2<<18) -#define SM501_MISC_HOLD_24 (3<<18) -#define SM501_MISC_HOLD_32 (4<<18) -#define SM501_MISC_HOLD_MASK (7<<18) +#define SM501_MISC_HOLD_EMPTY (0 << 18) +#define SM501_MISC_HOLD_8 (1 << 18) +#define SM501_MISC_HOLD_16 (2 << 18) +#define SM501_MISC_HOLD_24 (3 << 18) +#define SM501_MISC_HOLD_32 (4 << 18) +#define SM501_MISC_HOLD_MASK (7 << 18) -#define SM501_MISC_FREQ_12 (1<<24) -#define SM501_MISC_PNL_24BIT (1<<25) -#define SM501_MISC_8051_LE (1<<26) +#define SM501_MISC_FREQ_12 (1 << 24) +#define SM501_MISC_PNL_24BIT (1 << 25) +#define SM501_MISC_8051_LE (1 << 26) -#define SM501_GPIO31_0_CONTROL (0x000008) -#define SM501_GPIO63_32_CONTROL (0x00000C) -#define SM501_DRAM_CONTROL (0x000010) +#define SM501_GPIO31_0_CONTROL (0x000008) +#define SM501_GPIO63_32_CONTROL (0x00000C) +#define SM501_DRAM_CONTROL (0x000010) /* command list */ -#define SM501_ARBTRTN_CONTROL (0x000014) +#define SM501_ARBTRTN_CONTROL (0x000014) /* command list */ -#define SM501_COMMAND_LIST_STATUS (0x000024) +#define SM501_COMMAND_LIST_STATUS (0x000024) /* interrupt debug */ -#define SM501_RAW_IRQ_STATUS (0x000028) -#define SM501_RAW_IRQ_CLEAR (0x000028) -#define SM501_IRQ_STATUS (0x00002C) -#define SM501_IRQ_MASK (0x000030) -#define SM501_DEBUG_CONTROL (0x000034) +#define SM501_RAW_IRQ_STATUS (0x000028) +#define SM501_RAW_IRQ_CLEAR (0x000028) +#define SM501_IRQ_STATUS (0x00002C) +#define SM501_IRQ_MASK (0x000030) +#define SM501_DEBUG_CONTROL (0x000034) /* power management */ -#define SM501_POWERMODE_P2X_SRC (1<<29) -#define SM501_POWERMODE_V2X_SRC (1<<20) -#define SM501_POWERMODE_M_SRC (1<<12) -#define SM501_POWERMODE_M1_SRC (1<<4) - -#define SM501_CURRENT_GATE (0x000038) -#define SM501_CURRENT_CLOCK (0x00003C) -#define SM501_POWER_MODE_0_GATE (0x000040) -#define SM501_POWER_MODE_0_CLOCK (0x000044) -#define SM501_POWER_MODE_1_GATE (0x000048) -#define SM501_POWER_MODE_1_CLOCK (0x00004C) -#define SM501_SLEEP_MODE_GATE (0x000050) -#define SM501_POWER_MODE_CONTROL (0x000054) +#define SM501_POWERMODE_P2X_SRC (1 << 29) +#define SM501_POWERMODE_V2X_SRC (1 << 20) +#define SM501_POWERMODE_M_SRC (1 << 12) +#define SM501_POWERMODE_M1_SRC (1 << 4) + +#define SM501_CURRENT_GATE (0x000038) +#define SM501_CURRENT_CLOCK (0x00003C) +#define SM501_POWER_MODE_0_GATE (0x000040) +#define SM501_POWER_MODE_0_CLOCK (0x000044) +#define SM501_POWER_MODE_1_GATE (0x000048) +#define SM501_POWER_MODE_1_CLOCK (0x00004C) +#define SM501_SLEEP_MODE_GATE (0x000050) +#define SM501_POWER_MODE_CONTROL (0x000054) /* power gates for units within the 501 */ -#define SM501_GATE_HOST (0) -#define SM501_GATE_MEMORY (1) -#define SM501_GATE_DISPLAY (2) -#define SM501_GATE_2D_ENGINE (3) -#define SM501_GATE_CSC (4) -#define SM501_GATE_ZVPORT (5) -#define SM501_GATE_GPIO (6) -#define SM501_GATE_UART0 (7) -#define SM501_GATE_UART1 (8) -#define SM501_GATE_SSP (10) -#define SM501_GATE_USB_HOST (11) -#define SM501_GATE_USB_GADGET (12) -#define SM501_GATE_UCONTROLLER (17) -#define SM501_GATE_AC97 (18) +#define SM501_GATE_HOST (0) +#define SM501_GATE_MEMORY (1) +#define SM501_GATE_DISPLAY (2) +#define SM501_GATE_2D_ENGINE (3) +#define SM501_GATE_CSC (4) +#define SM501_GATE_ZVPORT (5) +#define SM501_GATE_GPIO (6) +#define SM501_GATE_UART0 (7) +#define SM501_GATE_UART1 (8) +#define SM501_GATE_SSP (10) +#define SM501_GATE_USB_HOST (11) +#define SM501_GATE_USB_GADGET (12) +#define SM501_GATE_UCONTROLLER (17) +#define SM501_GATE_AC97 (18) /* panel clock */ -#define SM501_CLOCK_P2XCLK (24) +#define SM501_CLOCK_P2XCLK (24) /* crt clock */ -#define SM501_CLOCK_V2XCLK (16) +#define SM501_CLOCK_V2XCLK (16) /* main clock */ -#define SM501_CLOCK_MCLK (8) +#define SM501_CLOCK_MCLK (8) /* SDRAM controller clock */ -#define SM501_CLOCK_M1XCLK (0) +#define SM501_CLOCK_M1XCLK (0) /* config 2 */ -#define SM501_PCI_MASTER_BASE (0x000058) -#define SM501_ENDIAN_CONTROL (0x00005C) -#define SM501_DEVICEID (0x000060) +#define SM501_PCI_MASTER_BASE (0x000058) +#define SM501_ENDIAN_CONTROL (0x00005C) +#define SM501_DEVICEID (0x000060) /* 0x050100A0 */ -#define SM501_DEVICEID_SM501 (0x05010000) -#define SM501_DEVICEID_IDMASK (0xffff0000) -#define SM501_DEVICEID_REVMASK (0x000000ff) +#define SM501_DEVICEID_SM501 (0x05010000) +#define SM501_DEVICEID_IDMASK (0xffff0000) +#define SM501_DEVICEID_REVMASK (0x000000ff) -#define SM501_PLLCLOCK_COUNT (0x000064) -#define SM501_MISC_TIMING (0x000068) -#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) +#define SM501_PLLCLOCK_COUNT (0x000064) +#define SM501_MISC_TIMING (0x000068) +#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) -#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) +#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) /* GPIO base */ -#define SM501_GPIO (0x010000) -#define SM501_GPIO_DATA_LOW (0x00) -#define SM501_GPIO_DATA_HIGH (0x04) -#define SM501_GPIO_DDR_LOW (0x08) -#define SM501_GPIO_DDR_HIGH (0x0C) -#define SM501_GPIO_IRQ_SETUP (0x10) -#define SM501_GPIO_IRQ_STATUS (0x14) -#define SM501_GPIO_IRQ_RESET (0x14) +#define SM501_GPIO (0x010000) +#define SM501_GPIO_DATA_LOW (0x00) +#define SM501_GPIO_DATA_HIGH (0x04) +#define SM501_GPIO_DDR_LOW (0x08) +#define SM501_GPIO_DDR_HIGH (0x0C) +#define SM501_GPIO_IRQ_SETUP (0x10) +#define SM501_GPIO_IRQ_STATUS (0x14) +#define SM501_GPIO_IRQ_RESET (0x14) /* I2C controller base */ -#define SM501_I2C (0x010040) -#define SM501_I2C_BYTE_COUNT (0x00) -#define SM501_I2C_CONTROL (0x01) -#define SM501_I2C_STATUS (0x02) -#define SM501_I2C_RESET (0x02) -#define SM501_I2C_SLAVE_ADDRESS (0x03) -#define SM501_I2C_DATA (0x04) +#define SM501_I2C (0x010040) +#define SM501_I2C_BYTE_COUNT (0x00) +#define SM501_I2C_CONTROL (0x01) +#define SM501_I2C_STATUS (0x02) +#define SM501_I2C_RESET (0x02) +#define SM501_I2C_SLAVE_ADDRESS (0x03) +#define SM501_I2C_DATA (0x04) /* SSP base */ -#define SM501_SSP (0x020000) +#define SM501_SSP (0x020000) /* Uart 0 base */ -#define SM501_UART0 (0x030000) +#define SM501_UART0 (0x030000) /* Uart 1 base */ -#define SM501_UART1 (0x030020) +#define SM501_UART1 (0x030020) /* USB host port base */ -#define SM501_USB_HOST (0x040000) +#define SM501_USB_HOST (0x040000) /* USB slave/gadget base */ -#define SM501_USB_GADGET (0x060000) +#define SM501_USB_GADGET (0x060000) /* USB slave/gadget data port base */ -#define SM501_USB_GADGET_DATA (0x070000) +#define SM501_USB_GADGET_DATA (0x070000) /* Display controller/video engine base */ -#define SM501_DC (0x080000) +#define SM501_DC (0x080000) /* common defines for the SM501 address registers */ -#define SM501_ADDR_FLIP (1<<31) -#define SM501_ADDR_EXT (1<<27) -#define SM501_ADDR_CS1 (1<<26) -#define SM501_ADDR_MASK (0x3f << 26) +#define SM501_ADDR_FLIP (1 << 31) +#define SM501_ADDR_EXT (1 << 27) +#define SM501_ADDR_CS1 (1 << 26) +#define SM501_ADDR_MASK (0x3f << 26) -#define SM501_FIFO_MASK (0x3 << 16) -#define SM501_FIFO_1 (0x0 << 16) -#define SM501_FIFO_3 (0x1 << 16) -#define SM501_FIFO_7 (0x2 << 16) -#define SM501_FIFO_11 (0x3 << 16) +#define SM501_FIFO_MASK (0x3 << 16) +#define SM501_FIFO_1 (0x0 << 16) +#define SM501_FIFO_3 (0x1 << 16) +#define SM501_FIFO_7 (0x2 << 16) +#define SM501_FIFO_11 (0x3 << 16) /* common registers for panel and the crt */ -#define SM501_OFF_DC_H_TOT (0x000) -#define SM501_OFF_DC_V_TOT (0x008) -#define SM501_OFF_DC_H_SYNC (0x004) -#define SM501_OFF_DC_V_SYNC (0x00C) - -#define SM501_DC_PANEL_CONTROL (0x000) - -#define SM501_DC_PANEL_CONTROL_FPEN (1<<27) -#define SM501_DC_PANEL_CONTROL_BIAS (1<<26) -#define SM501_DC_PANEL_CONTROL_DATA (1<<25) -#define SM501_DC_PANEL_CONTROL_VDD (1<<24) -#define SM501_DC_PANEL_CONTROL_DP (1<<23) - -#define SM501_DC_PANEL_CONTROL_TFT_888 (0<<21) -#define SM501_DC_PANEL_CONTROL_TFT_333 (1<<21) -#define SM501_DC_PANEL_CONTROL_TFT_444 (2<<21) - -#define SM501_DC_PANEL_CONTROL_DE (1<<20) - -#define SM501_DC_PANEL_CONTROL_LCD_TFT (0<<18) -#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1<<18) -#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2<<18) - -#define SM501_DC_PANEL_CONTROL_CP (1<<14) -#define SM501_DC_PANEL_CONTROL_VSP (1<<13) -#define SM501_DC_PANEL_CONTROL_HSP (1<<12) -#define SM501_DC_PANEL_CONTROL_CK (1<<9) -#define SM501_DC_PANEL_CONTROL_TE (1<<8) -#define SM501_DC_PANEL_CONTROL_VPD (1<<7) -#define SM501_DC_PANEL_CONTROL_VP (1<<6) -#define SM501_DC_PANEL_CONTROL_HPD (1<<5) -#define SM501_DC_PANEL_CONTROL_HP (1<<4) -#define SM501_DC_PANEL_CONTROL_GAMMA (1<<3) -#define SM501_DC_PANEL_CONTROL_EN (1<<2) - -#define SM501_DC_PANEL_CONTROL_8BPP (0<<0) -#define SM501_DC_PANEL_CONTROL_16BPP (1<<0) -#define SM501_DC_PANEL_CONTROL_32BPP (2<<0) - - -#define SM501_DC_PANEL_PANNING_CONTROL (0x004) -#define SM501_DC_PANEL_COLOR_KEY (0x008) -#define SM501_DC_PANEL_FB_ADDR (0x00C) -#define SM501_DC_PANEL_FB_OFFSET (0x010) -#define SM501_DC_PANEL_FB_WIDTH (0x014) -#define SM501_DC_PANEL_FB_HEIGHT (0x018) -#define SM501_DC_PANEL_TL_LOC (0x01C) -#define SM501_DC_PANEL_BR_LOC (0x020) -#define SM501_DC_PANEL_H_TOT (0x024) -#define SM501_DC_PANEL_H_SYNC (0x028) -#define SM501_DC_PANEL_V_TOT (0x02C) -#define SM501_DC_PANEL_V_SYNC (0x030) -#define SM501_DC_PANEL_CUR_LINE (0x034) - -#define SM501_DC_VIDEO_CONTROL (0x040) -#define SM501_DC_VIDEO_FB0_ADDR (0x044) -#define SM501_DC_VIDEO_FB_WIDTH (0x048) -#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) -#define SM501_DC_VIDEO_TL_LOC (0x050) -#define SM501_DC_VIDEO_BR_LOC (0x054) -#define SM501_DC_VIDEO_SCALE (0x058) -#define SM501_DC_VIDEO_INIT_SCALE (0x05C) -#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) -#define SM501_DC_VIDEO_FB1_ADDR (0x064) -#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) - -#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) -#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) -#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) -#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) -#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) -#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) -#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) -#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) -#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) -#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) - -#define SM501_DC_PANEL_HWC_BASE (0x0F0) -#define SM501_DC_PANEL_HWC_ADDR (0x0F0) -#define SM501_DC_PANEL_HWC_LOC (0x0F4) -#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) -#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) - -#define SM501_HWC_EN (1<<31) - -#define SM501_OFF_HWC_ADDR (0x00) -#define SM501_OFF_HWC_LOC (0x04) -#define SM501_OFF_HWC_COLOR_1_2 (0x08) -#define SM501_OFF_HWC_COLOR_3 (0x0C) - -#define SM501_DC_ALPHA_CONTROL (0x100) -#define SM501_DC_ALPHA_FB_ADDR (0x104) -#define SM501_DC_ALPHA_FB_OFFSET (0x108) -#define SM501_DC_ALPHA_TL_LOC (0x10C) -#define SM501_DC_ALPHA_BR_LOC (0x110) -#define SM501_DC_ALPHA_CHROMA_KEY (0x114) -#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) - -#define SM501_DC_CRT_CONTROL (0x200) - -#define SM501_DC_CRT_CONTROL_TVP (1<<15) -#define SM501_DC_CRT_CONTROL_CP (1<<14) -#define SM501_DC_CRT_CONTROL_VSP (1<<13) -#define SM501_DC_CRT_CONTROL_HSP (1<<12) -#define SM501_DC_CRT_CONTROL_VS (1<<11) -#define SM501_DC_CRT_CONTROL_BLANK (1<<10) -#define SM501_DC_CRT_CONTROL_SEL (1<<9) -#define SM501_DC_CRT_CONTROL_TE (1<<8) +#define SM501_OFF_DC_H_TOT (0x000) +#define SM501_OFF_DC_V_TOT (0x008) +#define SM501_OFF_DC_H_SYNC (0x004) +#define SM501_OFF_DC_V_SYNC (0x00C) + +#define SM501_DC_PANEL_CONTROL (0x000) + +#define SM501_DC_PANEL_CONTROL_FPEN (1 << 27) +#define SM501_DC_PANEL_CONTROL_BIAS (1 << 26) +#define SM501_DC_PANEL_CONTROL_DATA (1 << 25) +#define SM501_DC_PANEL_CONTROL_VDD (1 << 24) +#define SM501_DC_PANEL_CONTROL_DP (1 << 23) + +#define SM501_DC_PANEL_CONTROL_TFT_888 (0 << 21) +#define SM501_DC_PANEL_CONTROL_TFT_333 (1 << 21) +#define SM501_DC_PANEL_CONTROL_TFT_444 (2 << 21) + +#define SM501_DC_PANEL_CONTROL_DE (1 << 20) + +#define SM501_DC_PANEL_CONTROL_LCD_TFT (0 << 18) +#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1 << 18) +#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2 << 18) + +#define SM501_DC_PANEL_CONTROL_CP (1 << 14) +#define SM501_DC_PANEL_CONTROL_VSP (1 << 13) +#define SM501_DC_PANEL_CONTROL_HSP (1 << 12) +#define SM501_DC_PANEL_CONTROL_CK (1 << 9) +#define SM501_DC_PANEL_CONTROL_TE (1 << 8) +#define SM501_DC_PANEL_CONTROL_VPD (1 << 7) +#define SM501_DC_PANEL_CONTROL_VP (1 << 6) +#define SM501_DC_PANEL_CONTROL_HPD (1 << 5) +#define SM501_DC_PANEL_CONTROL_HP (1 << 4) +#define SM501_DC_PANEL_CONTROL_GAMMA (1 << 3) +#define SM501_DC_PANEL_CONTROL_EN (1 << 2) + +#define SM501_DC_PANEL_CONTROL_8BPP (0 << 0) +#define SM501_DC_PANEL_CONTROL_16BPP (1 << 0) +#define SM501_DC_PANEL_CONTROL_32BPP (2 << 0) + + +#define SM501_DC_PANEL_PANNING_CONTROL (0x004) +#define SM501_DC_PANEL_COLOR_KEY (0x008) +#define SM501_DC_PANEL_FB_ADDR (0x00C) +#define SM501_DC_PANEL_FB_OFFSET (0x010) +#define SM501_DC_PANEL_FB_WIDTH (0x014) +#define SM501_DC_PANEL_FB_HEIGHT (0x018) +#define SM501_DC_PANEL_TL_LOC (0x01C) +#define SM501_DC_PANEL_BR_LOC (0x020) +#define SM501_DC_PANEL_H_TOT (0x024) +#define SM501_DC_PANEL_H_SYNC (0x028) +#define SM501_DC_PANEL_V_TOT (0x02C) +#define SM501_DC_PANEL_V_SYNC (0x030) +#define SM501_DC_PANEL_CUR_LINE (0x034) + +#define SM501_DC_VIDEO_CONTROL (0x040) +#define SM501_DC_VIDEO_FB0_ADDR (0x044) +#define SM501_DC_VIDEO_FB_WIDTH (0x048) +#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) +#define SM501_DC_VIDEO_TL_LOC (0x050) +#define SM501_DC_VIDEO_BR_LOC (0x054) +#define SM501_DC_VIDEO_SCALE (0x058) +#define SM501_DC_VIDEO_INIT_SCALE (0x05C) +#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) +#define SM501_DC_VIDEO_FB1_ADDR (0x064) +#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) + +#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) +#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) +#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) +#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) +#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) +#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) +#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) +#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) +#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) +#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) + +#define SM501_DC_PANEL_HWC_BASE (0x0F0) +#define SM501_DC_PANEL_HWC_ADDR (0x0F0) +#define SM501_DC_PANEL_HWC_LOC (0x0F4) +#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) +#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) + +#define SM501_HWC_EN (1 << 31) + +#define SM501_OFF_HWC_ADDR (0x00) +#define SM501_OFF_HWC_LOC (0x04) +#define SM501_OFF_HWC_COLOR_1_2 (0x08) +#define SM501_OFF_HWC_COLOR_3 (0x0C) + +#define SM501_DC_ALPHA_CONTROL (0x100) +#define SM501_DC_ALPHA_FB_ADDR (0x104) +#define SM501_DC_ALPHA_FB_OFFSET (0x108) +#define SM501_DC_ALPHA_TL_LOC (0x10C) +#define SM501_DC_ALPHA_BR_LOC (0x110) +#define SM501_DC_ALPHA_CHROMA_KEY (0x114) +#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) + +#define SM501_DC_CRT_CONTROL (0x200) + +#define SM501_DC_CRT_CONTROL_TVP (1 << 15) +#define SM501_DC_CRT_CONTROL_CP (1 << 14) +#define SM501_DC_CRT_CONTROL_VSP (1 << 13) +#define SM501_DC_CRT_CONTROL_HSP (1 << 12) +#define SM501_DC_CRT_CONTROL_VS (1 << 11) +#define SM501_DC_CRT_CONTROL_BLANK (1 << 10) +#define SM501_DC_CRT_CONTROL_SEL (1 << 9) +#define SM501_DC_CRT_CONTROL_TE (1 << 8) #define SM501_DC_CRT_CONTROL_PIXEL_MASK (0xF << 4) -#define SM501_DC_CRT_CONTROL_GAMMA (1<<3) -#define SM501_DC_CRT_CONTROL_ENABLE (1<<2) +#define SM501_DC_CRT_CONTROL_GAMMA (1 << 3) +#define SM501_DC_CRT_CONTROL_ENABLE (1 << 2) -#define SM501_DC_CRT_CONTROL_8BPP (0<<0) -#define SM501_DC_CRT_CONTROL_16BPP (1<<0) -#define SM501_DC_CRT_CONTROL_32BPP (2<<0) +#define SM501_DC_CRT_CONTROL_8BPP (0 << 0) +#define SM501_DC_CRT_CONTROL_16BPP (1 << 0) +#define SM501_DC_CRT_CONTROL_32BPP (2 << 0) -#define SM501_DC_CRT_FB_ADDR (0x204) -#define SM501_DC_CRT_FB_OFFSET (0x208) -#define SM501_DC_CRT_H_TOT (0x20C) -#define SM501_DC_CRT_H_SYNC (0x210) -#define SM501_DC_CRT_V_TOT (0x214) -#define SM501_DC_CRT_V_SYNC (0x218) -#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) -#define SM501_DC_CRT_CUR_LINE (0x220) -#define SM501_DC_CRT_MONITOR_DETECT (0x224) +#define SM501_DC_CRT_FB_ADDR (0x204) +#define SM501_DC_CRT_FB_OFFSET (0x208) +#define SM501_DC_CRT_H_TOT (0x20C) +#define SM501_DC_CRT_H_SYNC (0x210) +#define SM501_DC_CRT_V_TOT (0x214) +#define SM501_DC_CRT_V_SYNC (0x218) +#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) +#define SM501_DC_CRT_CUR_LINE (0x220) +#define SM501_DC_CRT_MONITOR_DETECT (0x224) -#define SM501_DC_CRT_HWC_BASE (0x230) -#define SM501_DC_CRT_HWC_ADDR (0x230) -#define SM501_DC_CRT_HWC_LOC (0x234) -#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) -#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) +#define SM501_DC_CRT_HWC_BASE (0x230) +#define SM501_DC_CRT_HWC_ADDR (0x230) +#define SM501_DC_CRT_HWC_LOC (0x234) +#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) +#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) -#define SM501_DC_PANEL_PALETTE (0x400) +#define SM501_DC_PANEL_PALETTE (0x400) -#define SM501_DC_VIDEO_PALETTE (0x800) +#define SM501_DC_VIDEO_PALETTE (0x800) -#define SM501_DC_CRT_PALETTE (0xC00) +#define SM501_DC_CRT_PALETTE (0xC00) /* Zoom Video port base */ -#define SM501_ZVPORT (0x090000) +#define SM501_ZVPORT (0x090000) /* AC97/I2S base */ -#define SM501_AC97 (0x0A0000) +#define SM501_AC97 (0x0A0000) /* 8051 micro controller base */ -#define SM501_UCONTROLLER (0x0B0000) +#define SM501_UCONTROLLER (0x0B0000) /* 8051 micro controller SRAM base */ -#define SM501_UCONTROLLER_SRAM (0x0C0000) +#define SM501_UCONTROLLER_SRAM (0x0C0000) /* DMA base */ -#define SM501_DMA (0x0D0000) +#define SM501_DMA (0x0D0000) /* 2d engine base */ -#define SM501_2D_ENGINE (0x100000) -#define SM501_2D_SOURCE (0x00) -#define SM501_2D_DESTINATION (0x04) -#define SM501_2D_DIMENSION (0x08) -#define SM501_2D_CONTROL (0x0C) -#define SM501_2D_PITCH (0x10) -#define SM501_2D_FOREGROUND (0x14) -#define SM501_2D_BACKGROUND (0x18) -#define SM501_2D_STRETCH (0x1C) -#define SM501_2D_COLOR_COMPARE (0x20) -#define SM501_2D_COLOR_COMPARE_MASK (0x24) -#define SM501_2D_MASK (0x28) -#define SM501_2D_CLIP_TL (0x2C) -#define SM501_2D_CLIP_BR (0x30) -#define SM501_2D_MONO_PATTERN_LOW (0x34) -#define SM501_2D_MONO_PATTERN_HIGH (0x38) -#define SM501_2D_WINDOW_WIDTH (0x3C) -#define SM501_2D_SOURCE_BASE (0x40) -#define SM501_2D_DESTINATION_BASE (0x44) -#define SM501_2D_ALPHA (0x48) -#define SM501_2D_WRAP (0x4C) -#define SM501_2D_STATUS (0x50) - -#define SM501_CSC_Y_SOURCE_BASE (0xC8) -#define SM501_CSC_CONSTANTS (0xCC) -#define SM501_CSC_Y_SOURCE_X (0xD0) -#define SM501_CSC_Y_SOURCE_Y (0xD4) -#define SM501_CSC_U_SOURCE_BASE (0xD8) -#define SM501_CSC_V_SOURCE_BASE (0xDC) -#define SM501_CSC_SOURCE_DIMENSION (0xE0) -#define SM501_CSC_SOURCE_PITCH (0xE4) -#define SM501_CSC_DESTINATION (0xE8) -#define SM501_CSC_DESTINATION_DIMENSION (0xEC) -#define SM501_CSC_DESTINATION_PITCH (0xF0) -#define SM501_CSC_SCALE_FACTOR (0xF4) -#define SM501_CSC_DESTINATION_BASE (0xF8) -#define SM501_CSC_CONTROL (0xFC) +#define SM501_2D_ENGINE (0x100000) +#define SM501_2D_SOURCE (0x00) +#define SM501_2D_DESTINATION (0x04) +#define SM501_2D_DIMENSION (0x08) +#define SM501_2D_CONTROL (0x0C) +#define SM501_2D_PITCH (0x10) +#define SM501_2D_FOREGROUND (0x14) +#define SM501_2D_BACKGROUND (0x18) +#define SM501_2D_STRETCH (0x1C) +#define SM501_2D_COLOR_COMPARE (0x20) +#define SM501_2D_COLOR_COMPARE_MASK (0x24) +#define SM501_2D_MASK (0x28) +#define SM501_2D_CLIP_TL (0x2C) +#define SM501_2D_CLIP_BR (0x30) +#define SM501_2D_MONO_PATTERN_LOW (0x34) +#define SM501_2D_MONO_PATTERN_HIGH (0x38) +#define SM501_2D_WINDOW_WIDTH (0x3C) +#define SM501_2D_SOURCE_BASE (0x40) +#define SM501_2D_DESTINATION_BASE (0x44) +#define SM501_2D_ALPHA (0x48) +#define SM501_2D_WRAP (0x4C) +#define SM501_2D_STATUS (0x50) + +#define SM501_CSC_Y_SOURCE_BASE (0xC8) +#define SM501_CSC_CONSTANTS (0xCC) +#define SM501_CSC_Y_SOURCE_X (0xD0) +#define SM501_CSC_Y_SOURCE_Y (0xD4) +#define SM501_CSC_U_SOURCE_BASE (0xD8) +#define SM501_CSC_V_SOURCE_BASE (0xDC) +#define SM501_CSC_SOURCE_DIMENSION (0xE0) +#define SM501_CSC_SOURCE_PITCH (0xE4) +#define SM501_CSC_DESTINATION (0xE8) +#define SM501_CSC_DESTINATION_DIMENSION (0xEC) +#define SM501_CSC_DESTINATION_PITCH (0xF0) +#define SM501_CSC_SCALE_FACTOR (0xF4) +#define SM501_CSC_DESTINATION_BASE (0xF8) +#define SM501_CSC_CONTROL (0xFC) /* 2d engine data port base */ -#define SM501_2D_ENGINE_DATA (0x110000) +#define SM501_2D_ENGINE_DATA (0x110000) /* end of register definitions */ @@ -446,12 +453,12 @@ /* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ static const uint32_t sm501_mem_local_size[] = { - [0] = 4*1024*1024, - [1] = 8*1024*1024, - [2] = 16*1024*1024, - [3] = 32*1024*1024, - [4] = 64*1024*1024, - [5] = 2*1024*1024, + [0] = 4 * M_BYTE, + [1] = 8 * M_BYTE, + [2] = 16 * M_BYTE, + [3] = 32 * M_BYTE, + [4] = 64 * M_BYTE, + [5] = 2 * M_BYTE, }; #define get_local_mem_size(s) sm501_mem_local_size[(s)->local_mem_size_index] @@ -460,10 +467,13 @@ typedef struct SM501State { QemuConsole *con; /* status & internal resources */ - hwaddr base; uint32_t local_mem_size_index; - uint8_t * local_mem; + uint8_t *local_mem; MemoryRegion local_mem_region; + MemoryRegion mmio_region; + MemoryRegion system_config_region; + MemoryRegion disp_ctrl_region; + MemoryRegion twoD_engine_region; uint32_t last_width; uint32_t last_height; @@ -473,6 +483,7 @@ typedef struct SM501State { uint32_t gpio_31_0_control; uint32_t gpio_63_32_control; uint32_t dram_control; + uint32_t arbitration_control; uint32_t irq_mask; uint32_t misc_timing; uint32_t power_mode_control; @@ -482,7 +493,7 @@ typedef struct SM501State { uint32_t uart0_mcr; uint32_t uart0_scr; - uint8_t dc_palette[0x400 * 3]; + uint8_t dc_palette[DC_PALETTE_ENTRIES]; uint32_t dc_panel_control; uint32_t dc_panel_panning_control; @@ -502,6 +513,8 @@ typedef struct SM501State { uint32_t dc_panel_hwc_color_1_2; uint32_t dc_panel_hwc_color_3; + uint32_t dc_video_control; + uint32_t dc_crt_control; uint32_t dc_crt_fb_addr; uint32_t dc_crt_fb_offset; @@ -521,13 +534,20 @@ typedef struct SM501State { uint32_t twoD_control; uint32_t twoD_pitch; uint32_t twoD_foreground; + uint32_t twoD_background; uint32_t twoD_stretch; + uint32_t twoD_color_compare; uint32_t twoD_color_compare_mask; uint32_t twoD_mask; + uint32_t twoD_clip_tl; + uint32_t twoD_clip_br; + uint32_t twoD_mono_pattern_low; + uint32_t twoD_mono_pattern_high; uint32_t twoD_window_width; uint32_t twoD_source_base; uint32_t twoD_destination_base; - + uint32_t twoD_alpha; + uint32_t twoD_wrap; } SM501State; static uint32_t get_local_mem_size_index(uint32_t size) @@ -536,18 +556,36 @@ static uint32_t get_local_mem_size_index(uint32_t size) int i, index = 0; for (i = 0; i < ARRAY_SIZE(sm501_mem_local_size); i++) { - uint32_t new_size = sm501_mem_local_size[i]; - if (new_size >= size) { - if (norm_size == 0 || norm_size > new_size) { - norm_size = new_size; - index = i; - } - } + uint32_t new_size = sm501_mem_local_size[i]; + if (new_size >= size) { + if (norm_size == 0 || norm_size > new_size) { + norm_size = new_size; + index = i; + } + } } return index; } +static inline int get_width(SM501State *s, int crt) +{ + int width = crt ? s->dc_crt_h_total : s->dc_panel_h_total; + return (width & 0x00000FFF) + 1; +} + +static inline int get_height(SM501State *s, int crt) +{ + int height = crt ? s->dc_crt_v_total : s->dc_panel_v_total; + return (height & 0x00000FFF) + 1; +} + +static inline int get_bpp(SM501State *s, int crt) +{ + int bpp = crt ? s->dc_crt_control : s->dc_panel_control; + return 1 << (bpp & 3); +} + /** * Check the availability of hardware cursor. * @param crt 0 for PANEL, 1 for CRT. @@ -555,17 +593,17 @@ static uint32_t get_local_mem_size_index(uint32_t size) static inline int is_hwc_enabled(SM501State *state, int crt) { uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; - return addr & 0x80000000; + return addr & SM501_HWC_EN; } /** * Get the address which holds cursor pattern data. * @param crt 0 for PANEL, 1 for CRT. */ -static inline uint32_t get_hwc_address(SM501State *state, int crt) +static inline uint8_t *get_hwc_address(SM501State *state, int crt) { uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; - return (addr & 0x03FFFFF0)/* >> 4*/; + return state->local_mem + (addr & 0x03FFFFF0); } /** @@ -591,53 +629,51 @@ static inline uint32_t get_hwc_x(SM501State *state, int crt) } /** - * Get the cursor position in x coordinate. + * Get the hardware cursor palette. * @param crt 0 for PANEL, 1 for CRT. - * @param index 0, 1, 2 or 3 which specifies color of corsor dot. + * @param palette pointer to a [3 * 3] array to store color values in */ -static inline uint16_t get_hwc_color(SM501State *state, int crt, int index) +static inline void get_hwc_palette(SM501State *state, int crt, uint8_t *palette) { - uint32_t color_reg = 0; - uint16_t color_565 = 0; - - if (index == 0) { - return 0; - } - - switch (index) { - case 1: - case 2: - color_reg = crt ? state->dc_crt_hwc_color_1_2 - : state->dc_panel_hwc_color_1_2; - break; - case 3: - color_reg = crt ? state->dc_crt_hwc_color_3 - : state->dc_panel_hwc_color_3; - break; - default: - printf("invalid hw cursor color.\n"); - abort(); - } + int i; + uint32_t color_reg; + uint16_t rgb565; + + for (i = 0; i < 3; i++) { + if (i + 1 == 3) { + color_reg = crt ? state->dc_crt_hwc_color_3 + : state->dc_panel_hwc_color_3; + } else { + color_reg = crt ? state->dc_crt_hwc_color_1_2 + : state->dc_panel_hwc_color_1_2; + } - switch (index) { - case 1: - case 3: - color_565 = (uint16_t)(color_reg & 0xFFFF); - break; - case 2: - color_565 = (uint16_t)((color_reg >> 16) & 0xFFFF); - break; + if (i + 1 == 2) { + rgb565 = (color_reg >> 16) & 0xFFFF; + } else { + rgb565 = color_reg & 0xFFFF; + } + palette[i * 3 + 0] = (rgb565 << 3) & 0xf8; /* red */ + palette[i * 3 + 1] = (rgb565 >> 3) & 0xfc; /* green */ + palette[i * 3 + 2] = (rgb565 >> 8) & 0xf8; /* blue */ } - return color_565; } -static int within_hwc_y_range(SM501State *state, int y, int crt) +static inline void hwc_invalidate(SM501State *s, int crt) { - int hwc_y = get_hwc_y(state, crt); - return (hwc_y <= y && y < hwc_y + SM501_HWC_HEIGHT); + int w = get_width(s, crt); + int h = get_height(s, crt); + int bpp = get_bpp(s, crt); + int start = get_hwc_y(s, crt); + int end = MIN(h, start + SM501_HWC_HEIGHT) + 1; + + start *= w * bpp; + end *= w * bpp; + + memory_region_set_dirty(&s->local_mem_region, start, end - start); } -static void sm501_2d_operation(SM501State * s) +static void sm501_2d_operation(SM501State *s) { /* obtain operation parameters */ int operation = (s->twoD_control >> 16) & 0x1f; @@ -653,8 +689,8 @@ static void sm501_2d_operation(SM501State * s) int addressing = (s->twoD_stretch >> 16) & 0xF; /* get frame buffer info */ - uint8_t * src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); - uint8_t * dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); + uint8_t *src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); + uint8_t *dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; @@ -671,20 +707,20 @@ static void sm501_2d_operation(SM501State * s) switch (operation) { case 0x00: /* copy area */ -#define COPY_AREA(_bpp, _pixel_type, rtl) { \ - int y, x, index_d, index_s; \ - for (y = 0; y < operation_height; y++) { \ - for (x = 0; x < operation_width; x++) { \ - if (rtl) { \ - index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ - index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ - } else { \ - index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ - index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ - } \ - *(_pixel_type*)&dst[index_d] = *(_pixel_type*)&src[index_s];\ - } \ - } \ +#define COPY_AREA(_bpp, _pixel_type, rtl) { \ + int y, x, index_d, index_s; \ + for (y = 0; y < operation_height; y++) { \ + for (x = 0; x < operation_width; x++) { \ + if (rtl) { \ + index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ + index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ + } else { \ + index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ + index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + } \ + *(_pixel_type *)&dst[index_d] = *(_pixel_type *)&src[index_s];\ + } \ + } \ } switch (format_flags) { case 0: @@ -705,7 +741,7 @@ static void sm501_2d_operation(SM501State * s) for (y = 0; y < operation_height; y++) { \ for (x = 0; x < operation_width; x++) { \ int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ - *(_pixel_type*)&dst[index] = (_pixel_type)color; \ + *(_pixel_type *)&dst[index] = (_pixel_type)color; \ } \ } \ } @@ -733,50 +769,53 @@ static void sm501_2d_operation(SM501State * s) static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; uint32_t ret = 0; SM501_DPRINTF("sm501 system config regs : read addr=%x\n", (int)addr); - switch(addr) { + switch (addr) { case SM501_SYSTEM_CONTROL: - ret = s->system_control; - break; + ret = s->system_control; + break; case SM501_MISC_CONTROL: - ret = s->misc_control; - break; + ret = s->misc_control; + break; case SM501_GPIO31_0_CONTROL: - ret = s->gpio_31_0_control; - break; + ret = s->gpio_31_0_control; + break; case SM501_GPIO63_32_CONTROL: - ret = s->gpio_63_32_control; - break; + ret = s->gpio_63_32_control; + break; case SM501_DEVICEID: - ret = 0x050100A0; - break; + ret = 0x050100A0; + break; case SM501_DRAM_CONTROL: - ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; - break; + ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; + break; + case SM501_ARBTRTN_CONTROL: + ret = s->arbitration_control; + break; case SM501_IRQ_MASK: - ret = s->irq_mask; - break; + ret = s->irq_mask; + break; case SM501_MISC_TIMING: - /* TODO : simulate gate control */ - ret = s->misc_timing; - break; + /* TODO : simulate gate control */ + ret = s->misc_timing; + break; case SM501_CURRENT_GATE: - /* TODO : simulate gate control */ - ret = 0x00021807; - break; + /* TODO : simulate gate control */ + ret = 0x00021807; + break; case SM501_CURRENT_CLOCK: - ret = 0x2A1A0A09; - break; + ret = 0x2A1A0A09; + break; case SM501_POWER_MODE_CONTROL: - ret = s->power_mode_control; - break; + ret = s->power_mode_control; + break; default: - printf("sm501 system config : not implemented register read." - " addr=%x\n", (int)addr); + printf("sm501 system config : not implemented register read." + " addr=%x\n", (int)addr); abort(); } @@ -786,47 +825,50 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, static void sm501_system_config_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 system config regs : write addr=%x, val=%x\n", - (uint32_t)addr, (uint32_t)value); + (uint32_t)addr, (uint32_t)value); - switch(addr) { + switch (addr) { case SM501_SYSTEM_CONTROL: - s->system_control = value & 0xE300B8F7; - break; + s->system_control = value & 0xE300B8F7; + break; case SM501_MISC_CONTROL: - s->misc_control = value & 0xFF7FFF20; - break; + s->misc_control = value & 0xFF7FFF20; + break; case SM501_GPIO31_0_CONTROL: - s->gpio_31_0_control = value; - break; + s->gpio_31_0_control = value; + break; case SM501_GPIO63_32_CONTROL: - s->gpio_63_32_control = value; - break; + s->gpio_63_32_control = value; + break; case SM501_DRAM_CONTROL: - s->local_mem_size_index = (value >> 13) & 0x7; - /* rODO : check validity of size change */ - s->dram_control |= value & 0x7FFFFFC3; - break; + s->local_mem_size_index = (value >> 13) & 0x7; + /* TODO : check validity of size change */ + s->dram_control |= value & 0x7FFFFFC3; + break; + case SM501_ARBTRTN_CONTROL: + s->arbitration_control = value & 0x37777777; + break; case SM501_IRQ_MASK: - s->irq_mask = value; - break; + s->irq_mask = value; + break; case SM501_MISC_TIMING: - s->misc_timing = value & 0xF31F1FFF; - break; + s->misc_timing = value & 0xF31F1FFF; + break; case SM501_POWER_MODE_0_GATE: case SM501_POWER_MODE_1_GATE: case SM501_POWER_MODE_0_CLOCK: case SM501_POWER_MODE_1_CLOCK: - /* TODO : simulate gate & clock control */ - break; + /* TODO : simulate gate & clock control */ + break; case SM501_POWER_MODE_CONTROL: - s->power_mode_control = value & 0x00000003; - break; + s->power_mode_control = value & 0x00000003; + break; default: - printf("sm501 system config : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (uint32_t)value); + printf("sm501 system config : not implemented register write." + " addr=%x, val=%x\n", (int)addr, (uint32_t)value); abort(); } } @@ -838,124 +880,128 @@ static const MemoryRegionOps sm501_system_config_ops = { .min_access_size = 4, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static uint32_t sm501_palette_read(void *opaque, hwaddr addr) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 palette read addr=%x\n", (int)addr); /* TODO : consider BYTE/WORD access */ /* TODO : consider endian */ assert(range_covers_byte(0, 0x400 * 3, addr)); - return *(uint32_t*)&s->dc_palette[addr]; + return *(uint32_t *)&s->dc_palette[addr]; } -static void sm501_palette_write(void *opaque, - hwaddr addr, uint32_t value) +static void sm501_palette_write(void *opaque, hwaddr addr, + uint32_t value) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 palette write addr=%x, val=%x\n", - (int)addr, value); + (int)addr, value); /* TODO : consider BYTE/WORD access */ /* TODO : consider endian */ assert(range_covers_byte(0, 0x400 * 3, addr)); - *(uint32_t*)&s->dc_palette[addr] = value; + *(uint32_t *)&s->dc_palette[addr] = value; } static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; uint32_t ret = 0; SM501_DPRINTF("sm501 disp ctrl regs : read addr=%x\n", (int)addr); - switch(addr) { + switch (addr) { case SM501_DC_PANEL_CONTROL: - ret = s->dc_panel_control; - break; + ret = s->dc_panel_control; + break; case SM501_DC_PANEL_PANNING_CONTROL: - ret = s->dc_panel_panning_control; - break; + ret = s->dc_panel_panning_control; + break; case SM501_DC_PANEL_FB_ADDR: - ret = s->dc_panel_fb_addr; - break; + ret = s->dc_panel_fb_addr; + break; case SM501_DC_PANEL_FB_OFFSET: - ret = s->dc_panel_fb_offset; - break; + ret = s->dc_panel_fb_offset; + break; case SM501_DC_PANEL_FB_WIDTH: - ret = s->dc_panel_fb_width; - break; + ret = s->dc_panel_fb_width; + break; case SM501_DC_PANEL_FB_HEIGHT: - ret = s->dc_panel_fb_height; - break; + ret = s->dc_panel_fb_height; + break; case SM501_DC_PANEL_TL_LOC: - ret = s->dc_panel_tl_location; - break; + ret = s->dc_panel_tl_location; + break; case SM501_DC_PANEL_BR_LOC: - ret = s->dc_panel_br_location; - break; + ret = s->dc_panel_br_location; + break; case SM501_DC_PANEL_H_TOT: - ret = s->dc_panel_h_total; - break; + ret = s->dc_panel_h_total; + break; case SM501_DC_PANEL_H_SYNC: - ret = s->dc_panel_h_sync; - break; + ret = s->dc_panel_h_sync; + break; case SM501_DC_PANEL_V_TOT: - ret = s->dc_panel_v_total; - break; + ret = s->dc_panel_v_total; + break; case SM501_DC_PANEL_V_SYNC: - ret = s->dc_panel_v_sync; - break; + ret = s->dc_panel_v_sync; + break; + + case SM501_DC_VIDEO_CONTROL: + ret = s->dc_video_control; + break; case SM501_DC_CRT_CONTROL: - ret = s->dc_crt_control; - break; + ret = s->dc_crt_control; + break; case SM501_DC_CRT_FB_ADDR: - ret = s->dc_crt_fb_addr; - break; + ret = s->dc_crt_fb_addr; + break; case SM501_DC_CRT_FB_OFFSET: - ret = s->dc_crt_fb_offset; - break; + ret = s->dc_crt_fb_offset; + break; case SM501_DC_CRT_H_TOT: - ret = s->dc_crt_h_total; - break; + ret = s->dc_crt_h_total; + break; case SM501_DC_CRT_H_SYNC: - ret = s->dc_crt_h_sync; - break; + ret = s->dc_crt_h_sync; + break; case SM501_DC_CRT_V_TOT: - ret = s->dc_crt_v_total; - break; + ret = s->dc_crt_v_total; + break; case SM501_DC_CRT_V_SYNC: - ret = s->dc_crt_v_sync; - break; + ret = s->dc_crt_v_sync; + break; case SM501_DC_CRT_HWC_ADDR: - ret = s->dc_crt_hwc_addr; - break; + ret = s->dc_crt_hwc_addr; + break; case SM501_DC_CRT_HWC_LOC: - ret = s->dc_crt_hwc_location; - break; + ret = s->dc_crt_hwc_location; + break; case SM501_DC_CRT_HWC_COLOR_1_2: - ret = s->dc_crt_hwc_color_1_2; - break; + ret = s->dc_crt_hwc_color_1_2; + break; case SM501_DC_CRT_HWC_COLOR_3: - ret = s->dc_crt_hwc_color_3; - break; + ret = s->dc_crt_hwc_color_3; + break; - case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: + case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400 * 3 - 4: ret = sm501_palette_read(opaque, addr - SM501_DC_PANEL_PALETTE); break; default: - printf("sm501 disp ctrl : not implemented register read." - " addr=%x\n", (int)addr); + printf("sm501 disp ctrl : not implemented register read." + " addr=%x\n", (int)addr); abort(); } @@ -965,104 +1011,124 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 disp ctrl regs : write addr=%x, val=%x\n", - (unsigned)addr, (unsigned)value); + (unsigned)addr, (unsigned)value); - switch(addr) { + switch (addr) { case SM501_DC_PANEL_CONTROL: - s->dc_panel_control = value & 0x0FFF73FF; - break; + s->dc_panel_control = value & 0x0FFF73FF; + break; case SM501_DC_PANEL_PANNING_CONTROL: - s->dc_panel_panning_control = value & 0xFF3FFF3F; - break; + s->dc_panel_panning_control = value & 0xFF3FFF3F; + break; case SM501_DC_PANEL_FB_ADDR: - s->dc_panel_fb_addr = value & 0x8FFFFFF0; - break; + s->dc_panel_fb_addr = value & 0x8FFFFFF0; + break; case SM501_DC_PANEL_FB_OFFSET: - s->dc_panel_fb_offset = value & 0x3FF03FF0; - break; + s->dc_panel_fb_offset = value & 0x3FF03FF0; + break; case SM501_DC_PANEL_FB_WIDTH: - s->dc_panel_fb_width = value & 0x0FFF0FFF; - break; + s->dc_panel_fb_width = value & 0x0FFF0FFF; + break; case SM501_DC_PANEL_FB_HEIGHT: - s->dc_panel_fb_height = value & 0x0FFF0FFF; - break; + s->dc_panel_fb_height = value & 0x0FFF0FFF; + break; case SM501_DC_PANEL_TL_LOC: - s->dc_panel_tl_location = value & 0x07FF07FF; - break; + s->dc_panel_tl_location = value & 0x07FF07FF; + break; case SM501_DC_PANEL_BR_LOC: - s->dc_panel_br_location = value & 0x07FF07FF; - break; + s->dc_panel_br_location = value & 0x07FF07FF; + break; case SM501_DC_PANEL_H_TOT: - s->dc_panel_h_total = value & 0x0FFF0FFF; - break; + s->dc_panel_h_total = value & 0x0FFF0FFF; + break; case SM501_DC_PANEL_H_SYNC: - s->dc_panel_h_sync = value & 0x00FF0FFF; - break; + s->dc_panel_h_sync = value & 0x00FF0FFF; + break; case SM501_DC_PANEL_V_TOT: - s->dc_panel_v_total = value & 0x0FFF0FFF; - break; + s->dc_panel_v_total = value & 0x0FFF0FFF; + break; case SM501_DC_PANEL_V_SYNC: - s->dc_panel_v_sync = value & 0x003F0FFF; - break; + s->dc_panel_v_sync = value & 0x003F0FFF; + break; case SM501_DC_PANEL_HWC_ADDR: - s->dc_panel_hwc_addr = value & 0x8FFFFFF0; - break; + value &= 0x8FFFFFF0; + if (value != s->dc_panel_hwc_addr) { + hwc_invalidate(s, 0); + s->dc_panel_hwc_addr = value; + } + break; case SM501_DC_PANEL_HWC_LOC: - s->dc_panel_hwc_location = value & 0x0FFF0FFF; - break; + value &= 0x0FFF0FFF; + if (value != s->dc_panel_hwc_location) { + hwc_invalidate(s, 0); + s->dc_panel_hwc_location = value; + } + break; case SM501_DC_PANEL_HWC_COLOR_1_2: - s->dc_panel_hwc_color_1_2 = value; - break; + s->dc_panel_hwc_color_1_2 = value; + break; case SM501_DC_PANEL_HWC_COLOR_3: - s->dc_panel_hwc_color_3 = value & 0x0000FFFF; - break; + s->dc_panel_hwc_color_3 = value & 0x0000FFFF; + break; + + case SM501_DC_VIDEO_CONTROL: + s->dc_video_control = value & 0x00037FFF; + break; case SM501_DC_CRT_CONTROL: - s->dc_crt_control = value & 0x0003FFFF; - break; + s->dc_crt_control = value & 0x0003FFFF; + break; case SM501_DC_CRT_FB_ADDR: - s->dc_crt_fb_addr = value & 0x8FFFFFF0; - break; + s->dc_crt_fb_addr = value & 0x8FFFFFF0; + break; case SM501_DC_CRT_FB_OFFSET: - s->dc_crt_fb_offset = value & 0x3FF03FF0; - break; + s->dc_crt_fb_offset = value & 0x3FF03FF0; + break; case SM501_DC_CRT_H_TOT: - s->dc_crt_h_total = value & 0x0FFF0FFF; - break; + s->dc_crt_h_total = value & 0x0FFF0FFF; + break; case SM501_DC_CRT_H_SYNC: - s->dc_crt_h_sync = value & 0x00FF0FFF; - break; + s->dc_crt_h_sync = value & 0x00FF0FFF; + break; case SM501_DC_CRT_V_TOT: - s->dc_crt_v_total = value & 0x0FFF0FFF; - break; + s->dc_crt_v_total = value & 0x0FFF0FFF; + break; case SM501_DC_CRT_V_SYNC: - s->dc_crt_v_sync = value & 0x003F0FFF; - break; + s->dc_crt_v_sync = value & 0x003F0FFF; + break; case SM501_DC_CRT_HWC_ADDR: - s->dc_crt_hwc_addr = value & 0x8FFFFFF0; - break; + value &= 0x8FFFFFF0; + if (value != s->dc_crt_hwc_addr) { + hwc_invalidate(s, 1); + s->dc_crt_hwc_addr = value; + } + break; case SM501_DC_CRT_HWC_LOC: - s->dc_crt_hwc_location = value & 0x0FFF0FFF; - break; + value &= 0x0FFF0FFF; + if (value != s->dc_crt_hwc_location) { + hwc_invalidate(s, 1); + s->dc_crt_hwc_location = value; + } + break; case SM501_DC_CRT_HWC_COLOR_1_2: - s->dc_crt_hwc_color_1_2 = value; - break; + s->dc_crt_hwc_color_1_2 = value; + break; case SM501_DC_CRT_HWC_COLOR_3: - s->dc_crt_hwc_color_3 = value & 0x0000FFFF; - break; + s->dc_crt_hwc_color_3 = value & 0x0000FFFF; + break; - case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: + case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400 * 3 - 4: sm501_palette_write(opaque, addr - SM501_DC_PANEL_PALETTE, value); break; default: - printf("sm501 disp ctrl : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (unsigned)value); + printf("sm501 disp ctrl : not implemented register write." + " addr=%x, val=%x\n", (int)addr, (unsigned)value); abort(); } } @@ -1074,20 +1140,80 @@ static const MemoryRegionOps sm501_disp_ctrl_ops = { .min_access_size = 4, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; uint32_t ret = 0; SM501_DPRINTF("sm501 2d engine regs : read addr=%x\n", (int)addr); - switch(addr) { + switch (addr) { + case SM501_2D_SOURCE: + ret = s->twoD_source; + break; + case SM501_2D_DESTINATION: + ret = s->twoD_destination; + break; + case SM501_2D_DIMENSION: + ret = s->twoD_dimension; + break; + case SM501_2D_CONTROL: + ret = s->twoD_control; + break; + case SM501_2D_PITCH: + ret = s->twoD_pitch; + break; + case SM501_2D_FOREGROUND: + ret = s->twoD_foreground; + break; + case SM501_2D_BACKGROUND: + ret = s->twoD_background; + break; + case SM501_2D_STRETCH: + ret = s->twoD_stretch; + break; + case SM501_2D_COLOR_COMPARE: + ret = s->twoD_color_compare; + break; + case SM501_2D_COLOR_COMPARE_MASK: + ret = s->twoD_color_compare_mask; + break; + case SM501_2D_MASK: + ret = s->twoD_mask; + break; + case SM501_2D_CLIP_TL: + ret = s->twoD_clip_tl; + break; + case SM501_2D_CLIP_BR: + ret = s->twoD_clip_br; + break; + case SM501_2D_MONO_PATTERN_LOW: + ret = s->twoD_mono_pattern_low; + break; + case SM501_2D_MONO_PATTERN_HIGH: + ret = s->twoD_mono_pattern_high; + break; + case SM501_2D_WINDOW_WIDTH: + ret = s->twoD_window_width; + break; case SM501_2D_SOURCE_BASE: ret = s->twoD_source_base; break; + case SM501_2D_DESTINATION_BASE: + ret = s->twoD_destination_base; + break; + case SM501_2D_ALPHA: + ret = s->twoD_alpha; + break; + case SM501_2D_WRAP: + ret = s->twoD_wrap; + break; + case SM501_2D_STATUS: + ret = 0; /* Should return interrupt status */ + break; default: printf("sm501 disp ctrl : not implemented register read." " addr=%x\n", (int)addr); @@ -1100,11 +1226,11 @@ static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, static void sm501_2d_engine_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n", (unsigned)addr, (unsigned)value); - switch(addr) { + switch (addr) { case SM501_2D_SOURCE: s->twoD_source = value; break; @@ -1130,15 +1256,33 @@ static void sm501_2d_engine_write(void *opaque, hwaddr addr, case SM501_2D_FOREGROUND: s->twoD_foreground = value; break; + case SM501_2D_BACKGROUND: + s->twoD_background = value; + break; case SM501_2D_STRETCH: s->twoD_stretch = value; break; + case SM501_2D_COLOR_COMPARE: + s->twoD_color_compare = value; + break; case SM501_2D_COLOR_COMPARE_MASK: s->twoD_color_compare_mask = value; break; case SM501_2D_MASK: s->twoD_mask = value; break; + case SM501_2D_CLIP_TL: + s->twoD_clip_tl = value; + break; + case SM501_2D_CLIP_BR: + s->twoD_clip_br = value; + break; + case SM501_2D_MONO_PATTERN_LOW: + s->twoD_mono_pattern_low = value; + break; + case SM501_2D_MONO_PATTERN_HIGH: + s->twoD_mono_pattern_high = value; + break; case SM501_2D_WINDOW_WIDTH: s->twoD_window_width = value; break; @@ -1148,6 +1292,15 @@ static void sm501_2d_engine_write(void *opaque, hwaddr addr, case SM501_2D_DESTINATION_BASE: s->twoD_destination_base = value; break; + case SM501_2D_ALPHA: + s->twoD_alpha = value; + break; + case SM501_2D_WRAP: + s->twoD_wrap = value; + break; + case SM501_2D_STATUS: + /* ignored, writing 0 should clear interrupt status */ + break; default: printf("sm501 2d engine : not implemented register write." " addr=%x, val=%x\n", (int)addr, (unsigned)value); @@ -1162,16 +1315,17 @@ static const MemoryRegionOps sm501_2d_engine_ops = { .min_access_size = 4, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; /* draw line functions for all console modes */ typedef void draw_line_func(uint8_t *d, const uint8_t *s, - int width, const uint32_t *pal); + int width, const uint32_t *pal); -typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, - int c_y, uint8_t *d, int width); +typedef void draw_hwc_line_func(uint8_t *d, const uint8_t *s, + int width, const uint8_t *palette, + int c_x, int c_y); #define DEPTH 8 #include "sm501_template.h" @@ -1197,7 +1351,7 @@ typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, #define DEPTH 32 #include "sm501_template.h" -static draw_line_func * draw_line8_funcs[] = { +static draw_line_func *draw_line8_funcs[] = { draw_line8_8, draw_line8_15, draw_line8_16, @@ -1207,7 +1361,7 @@ static draw_line_func * draw_line8_funcs[] = { draw_line8_16bgr, }; -static draw_line_func * draw_line16_funcs[] = { +static draw_line_func *draw_line16_funcs[] = { draw_line16_8, draw_line16_15, draw_line16_16, @@ -1217,7 +1371,7 @@ static draw_line_func * draw_line16_funcs[] = { draw_line16_16bgr, }; -static draw_line_func * draw_line32_funcs[] = { +static draw_line_func *draw_line32_funcs[] = { draw_line32_8, draw_line32_15, draw_line32_16, @@ -1227,7 +1381,7 @@ static draw_line_func * draw_line32_funcs[] = { draw_line32_16bgr, }; -static draw_hwc_line_func * draw_hwc_line_funcs[] = { +static draw_hwc_line_func *draw_hwc_line_funcs[] = { draw_hwc_line_8, draw_hwc_line_15, draw_hwc_line_16, @@ -1242,7 +1396,7 @@ static inline int get_depth_index(DisplaySurface *surface) switch (surface_bits_per_pixel(surface)) { default: case 8: - return 0; + return 0; case 15: return 1; case 16: @@ -1256,203 +1410,459 @@ static inline int get_depth_index(DisplaySurface *surface) } } -static void sm501_draw_crt(SM501State * s) +static void sm501_update_display(void *opaque) { + SM501State *s = (SM501State *)opaque; DisplaySurface *surface = qemu_console_surface(s->con); - int y; - int width = (s->dc_crt_h_total & 0x00000FFF) + 1; - int height = (s->dc_crt_v_total & 0x00000FFF) + 1; - - uint8_t * src = s->local_mem; - int src_bpp = 0; + int y, c_x = 0, c_y = 0; + int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; + int width = get_width(s, crt); + int height = get_height(s, crt); + int src_bpp = get_bpp(s, crt); int dst_bpp = surface_bytes_per_pixel(surface); - uint32_t * palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE - - SM501_DC_PANEL_PALETTE]; - uint8_t hwc_palette[3 * 3]; - int ds_depth_index = get_depth_index(surface); - draw_line_func * draw_line = NULL; - draw_hwc_line_func * draw_hwc_line = NULL; + int dst_depth_index = get_depth_index(surface); + draw_line_func *draw_line = NULL; + draw_hwc_line_func *draw_hwc_line = NULL; int full_update = 0; int y_start = -1; ram_addr_t page_min = ~0l; ram_addr_t page_max = 0l; - ram_addr_t offset = 0; + ram_addr_t offset; + uint32_t *palette; + uint8_t hwc_palette[3 * 3]; + uint8_t *hwc_src = NULL; + + if (!((crt ? s->dc_crt_control : s->dc_panel_control) + & SM501_DC_CRT_CONTROL_ENABLE)) { + return; + } + + palette = (uint32_t *)(crt ? &s->dc_palette[SM501_DC_CRT_PALETTE - + SM501_DC_PANEL_PALETTE] + : &s->dc_palette[0]); /* choose draw_line function */ - switch (s->dc_crt_control & 3) { - case SM501_DC_CRT_CONTROL_8BPP: - src_bpp = 1; - draw_line = draw_line8_funcs[ds_depth_index]; - break; - case SM501_DC_CRT_CONTROL_16BPP: - src_bpp = 2; - draw_line = draw_line16_funcs[ds_depth_index]; - break; - case SM501_DC_CRT_CONTROL_32BPP: - src_bpp = 4; - draw_line = draw_line32_funcs[ds_depth_index]; - break; + switch (src_bpp) { + case 1: + draw_line = draw_line8_funcs[dst_depth_index]; + break; + case 2: + draw_line = draw_line16_funcs[dst_depth_index]; + break; + case 4: + draw_line = draw_line32_funcs[dst_depth_index]; + break; default: - printf("sm501 draw crt : invalid DC_CRT_CONTROL=%x.\n", - s->dc_crt_control); + printf("sm501 update display : invalid control register value.\n"); abort(); - break; + break; } /* set up to draw hardware cursor */ - if (is_hwc_enabled(s, 1)) { - int i; - - /* get cursor palette */ - for (i = 0; i < 3; i++) { - uint16_t rgb565 = get_hwc_color(s, 1, i + 1); - hwc_palette[i * 3 + 0] = (rgb565 & 0xf800) >> 8; /* red */ - hwc_palette[i * 3 + 1] = (rgb565 & 0x07e0) >> 3; /* green */ - hwc_palette[i * 3 + 2] = (rgb565 & 0x001f) << 3; /* blue */ - } - + if (is_hwc_enabled(s, crt)) { /* choose cursor draw line function */ - draw_hwc_line = draw_hwc_line_funcs[ds_depth_index]; + draw_hwc_line = draw_hwc_line_funcs[dst_depth_index]; + hwc_src = get_hwc_address(s, crt); + c_x = get_hwc_x(s, crt); + c_y = get_hwc_y(s, crt); + get_hwc_palette(s, crt, hwc_palette); } /* adjust console size */ if (s->last_width != width || s->last_height != height) { qemu_console_resize(s->con, width, height); surface = qemu_console_surface(s->con); - s->last_width = width; - s->last_height = height; - full_update = 1; + s->last_width = width; + s->last_height = height; + full_update = 1; } /* draw each line according to conditions */ memory_region_sync_dirty_bitmap(&s->local_mem_region); - for (y = 0; y < height; y++) { - int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0; - int update = full_update || update_hwc; + for (y = 0, offset = 0; y < height; y++, offset += width * src_bpp) { + int update, update_hwc; ram_addr_t page0 = offset; ram_addr_t page1 = offset + width * src_bpp - 1; - /* check dirty flags for each line */ - update = memory_region_get_dirty(&s->local_mem_region, page0, - page1 - page0, DIRTY_MEMORY_VGA); + /* check if hardware cursor is enabled and we're within its range */ + update_hwc = draw_hwc_line && c_y <= y && y < c_y + SM501_HWC_HEIGHT; + update = full_update || update_hwc; + /* check dirty flags for each line */ + update |= memory_region_get_dirty(&s->local_mem_region, page0, + page1 - page0, DIRTY_MEMORY_VGA); - /* draw line and change status */ - if (update) { + /* draw line and change status */ + if (update) { uint8_t *d = surface_data(surface); d += y * width * dst_bpp; /* draw graphics layer */ - draw_line(d, src, width, palette); + draw_line(d, s->local_mem + offset, width, palette); - /* draw haredware cursor */ + /* draw hardware cursor */ if (update_hwc) { - draw_hwc_line(s, 1, hwc_palette, y - get_hwc_y(s, 1), d, width); + draw_hwc_line(d, hwc_src, width, hwc_palette, c_x, y - c_y); } - if (y_start < 0) - y_start = y; - if (page0 < page_min) - page_min = page0; - if (page1 > page_max) - page_max = page1; - } else { - if (y_start >= 0) { - /* flush to display */ + if (y_start < 0) { + y_start = y; + } + if (page0 < page_min) { + page_min = page0; + } + if (page1 > page_max) { + page_max = page1; + } + } else { + if (y_start >= 0) { + /* flush to display */ dpy_gfx_update(s->con, 0, y_start, width, y - y_start); - y_start = -1; - } - } - - src += width * src_bpp; - offset += width * src_bpp; + y_start = -1; + } + } } /* complete flush to display */ - if (y_start >= 0) + if (y_start >= 0) { dpy_gfx_update(s->con, 0, y_start, width, y - y_start); + } /* clear dirty flags */ if (page_min != ~0l) { - memory_region_reset_dirty(&s->local_mem_region, + memory_region_reset_dirty(&s->local_mem_region, page_min, page_max + TARGET_PAGE_SIZE, DIRTY_MEMORY_VGA); } } -static void sm501_update_display(void *opaque) -{ - SM501State * s = (SM501State *)opaque; - - if (s->dc_crt_control & SM501_DC_CRT_CONTROL_ENABLE) - sm501_draw_crt(s); -} - static const GraphicHwOps sm501_ops = { .gfx_update = sm501_update_display, }; -void sm501_init(MemoryRegion *address_space_mem, uint32_t base, - uint32_t local_mem_bytes, qemu_irq irq, Chardev *chr) +static void sm501_reset(SM501State *s) { - SM501State * s; - DeviceState *dev; - MemoryRegion *sm501_system_config = g_new(MemoryRegion, 1); - MemoryRegion *sm501_disp_ctrl = g_new(MemoryRegion, 1); - MemoryRegion *sm501_2d_engine = g_new(MemoryRegion, 1); - - /* allocate management data region */ - s = (SM501State *)g_malloc0(sizeof(SM501State)); - s->base = base; - s->local_mem_size_index - = get_local_mem_size_index(local_mem_bytes); - SM501_DPRINTF("local mem size=%x. index=%d\n", get_local_mem_size(s), - s->local_mem_size_index); - s->system_control = 0x00100000; - s->misc_control = 0x00001000; /* assumes SH, active=low */ - s->dc_panel_control = 0x00010000; + s->system_control = 0x00100000; /* 2D engine FIFO empty */ + /* Bits 17 (SH), 7 (CDR), 6:5 (Test), 2:0 (Bus) are all supposed + * to be determined at reset by GPIO lines which set config bits. + * We hardwire them: + * SH = 0 : Hitachi Ready Polarity == Active Low + * CDR = 0 : do not reset clock divider + * TEST = 0 : Normal mode (not testing the silicon) + * BUS = 0 : Hitachi SH3/SH4 + */ + s->misc_control = SM501_MISC_DAC_POWER; + s->gpio_31_0_control = 0; + s->gpio_63_32_control = 0; + s->dram_control = 0; + s->arbitration_control = 0x05146732; + s->irq_mask = 0; + s->misc_timing = 0; + s->power_mode_control = 0; + s->dc_panel_control = 0x00010000; /* FIFO level 3 */ + s->dc_video_control = 0; s->dc_crt_control = 0x00010000; + s->twoD_source = 0; + s->twoD_destination = 0; + s->twoD_dimension = 0; + s->twoD_control = 0; + s->twoD_pitch = 0; + s->twoD_foreground = 0; + s->twoD_background = 0; + s->twoD_stretch = 0; + s->twoD_color_compare = 0; + s->twoD_color_compare_mask = 0; + s->twoD_mask = 0; + s->twoD_clip_tl = 0; + s->twoD_clip_br = 0; + s->twoD_mono_pattern_low = 0; + s->twoD_mono_pattern_high = 0; + s->twoD_window_width = 0; + s->twoD_source_base = 0; + s->twoD_destination_base = 0; + s->twoD_alpha = 0; + s->twoD_wrap = 0; +} + +static void sm501_init(SM501State *s, DeviceState *dev, + uint32_t local_mem_bytes) +{ + s->local_mem_size_index = get_local_mem_size_index(local_mem_bytes); + SM501_DPRINTF("sm501 local mem size=%x. index=%d\n", get_local_mem_size(s), + s->local_mem_size_index); - /* allocate local memory */ - memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local", - local_mem_bytes, &error_fatal); + /* local memory */ + memory_region_init_ram(&s->local_mem_region, OBJECT(dev), "sm501.local", + get_local_mem_size(s), &error_fatal); vmstate_register_ram_global(&s->local_mem_region); memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA); s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); - memory_region_add_subregion(address_space_mem, base, &s->local_mem_region); - /* map mmio */ - memory_region_init_io(sm501_system_config, NULL, &sm501_system_config_ops, s, + /* mmio */ + memory_region_init(&s->mmio_region, OBJECT(dev), "sm501.mmio", MMIO_SIZE); + memory_region_init_io(&s->system_config_region, OBJECT(dev), + &sm501_system_config_ops, s, "sm501-system-config", 0x6c); - memory_region_add_subregion(address_space_mem, base + MMIO_BASE_OFFSET, - sm501_system_config); - memory_region_init_io(sm501_disp_ctrl, NULL, &sm501_disp_ctrl_ops, s, + memory_region_add_subregion(&s->mmio_region, SM501_SYS_CONFIG, + &s->system_config_region); + memory_region_init_io(&s->disp_ctrl_region, OBJECT(dev), + &sm501_disp_ctrl_ops, s, "sm501-disp-ctrl", 0x1000); - memory_region_add_subregion(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_DC, - sm501_disp_ctrl); - memory_region_init_io(sm501_2d_engine, NULL, &sm501_2d_engine_ops, s, + memory_region_add_subregion(&s->mmio_region, SM501_DC, + &s->disp_ctrl_region); + memory_region_init_io(&s->twoD_engine_region, OBJECT(dev), + &sm501_2d_engine_ops, s, "sm501-2d-engine", 0x54); - memory_region_add_subregion(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_2D_ENGINE, - sm501_2d_engine); + memory_region_add_subregion(&s->mmio_region, SM501_2D_ENGINE, + &s->twoD_engine_region); + + /* create qemu graphic console */ + s->con = graphic_console_init(DEVICE(dev), 0, &sm501_ops, s); +} + +static const VMStateDescription vmstate_sm501_state = { + .name = "sm501-state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(local_mem_size_index, SM501State), + VMSTATE_UINT32(system_control, SM501State), + VMSTATE_UINT32(misc_control, SM501State), + VMSTATE_UINT32(gpio_31_0_control, SM501State), + VMSTATE_UINT32(gpio_63_32_control, SM501State), + VMSTATE_UINT32(dram_control, SM501State), + VMSTATE_UINT32(arbitration_control, SM501State), + VMSTATE_UINT32(irq_mask, SM501State), + VMSTATE_UINT32(misc_timing, SM501State), + VMSTATE_UINT32(power_mode_control, SM501State), + VMSTATE_UINT32(uart0_ier, SM501State), + VMSTATE_UINT32(uart0_lcr, SM501State), + VMSTATE_UINT32(uart0_mcr, SM501State), + VMSTATE_UINT32(uart0_scr, SM501State), + VMSTATE_UINT8_ARRAY(dc_palette, SM501State, DC_PALETTE_ENTRIES), + VMSTATE_UINT32(dc_panel_control, SM501State), + VMSTATE_UINT32(dc_panel_panning_control, SM501State), + VMSTATE_UINT32(dc_panel_fb_addr, SM501State), + VMSTATE_UINT32(dc_panel_fb_offset, SM501State), + VMSTATE_UINT32(dc_panel_fb_width, SM501State), + VMSTATE_UINT32(dc_panel_fb_height, SM501State), + VMSTATE_UINT32(dc_panel_tl_location, SM501State), + VMSTATE_UINT32(dc_panel_br_location, SM501State), + VMSTATE_UINT32(dc_panel_h_total, SM501State), + VMSTATE_UINT32(dc_panel_h_sync, SM501State), + VMSTATE_UINT32(dc_panel_v_total, SM501State), + VMSTATE_UINT32(dc_panel_v_sync, SM501State), + VMSTATE_UINT32(dc_panel_hwc_addr, SM501State), + VMSTATE_UINT32(dc_panel_hwc_location, SM501State), + VMSTATE_UINT32(dc_panel_hwc_color_1_2, SM501State), + VMSTATE_UINT32(dc_panel_hwc_color_3, SM501State), + VMSTATE_UINT32(dc_video_control, SM501State), + VMSTATE_UINT32(dc_crt_control, SM501State), + VMSTATE_UINT32(dc_crt_fb_addr, SM501State), + VMSTATE_UINT32(dc_crt_fb_offset, SM501State), + VMSTATE_UINT32(dc_crt_h_total, SM501State), + VMSTATE_UINT32(dc_crt_h_sync, SM501State), + VMSTATE_UINT32(dc_crt_v_total, SM501State), + VMSTATE_UINT32(dc_crt_v_sync, SM501State), + VMSTATE_UINT32(dc_crt_hwc_addr, SM501State), + VMSTATE_UINT32(dc_crt_hwc_location, SM501State), + VMSTATE_UINT32(dc_crt_hwc_color_1_2, SM501State), + VMSTATE_UINT32(dc_crt_hwc_color_3, SM501State), + VMSTATE_UINT32(twoD_source, SM501State), + VMSTATE_UINT32(twoD_destination, SM501State), + VMSTATE_UINT32(twoD_dimension, SM501State), + VMSTATE_UINT32(twoD_control, SM501State), + VMSTATE_UINT32(twoD_pitch, SM501State), + VMSTATE_UINT32(twoD_foreground, SM501State), + VMSTATE_UINT32(twoD_background, SM501State), + VMSTATE_UINT32(twoD_stretch, SM501State), + VMSTATE_UINT32(twoD_color_compare, SM501State), + VMSTATE_UINT32(twoD_color_compare_mask, SM501State), + VMSTATE_UINT32(twoD_mask, SM501State), + VMSTATE_UINT32(twoD_clip_tl, SM501State), + VMSTATE_UINT32(twoD_clip_br, SM501State), + VMSTATE_UINT32(twoD_mono_pattern_low, SM501State), + VMSTATE_UINT32(twoD_mono_pattern_high, SM501State), + VMSTATE_UINT32(twoD_window_width, SM501State), + VMSTATE_UINT32(twoD_source_base, SM501State), + VMSTATE_UINT32(twoD_destination_base, SM501State), + VMSTATE_UINT32(twoD_alpha, SM501State), + VMSTATE_UINT32(twoD_wrap, SM501State), + VMSTATE_END_OF_LIST() + } +}; + +#define TYPE_SYSBUS_SM501 "sysbus-sm501" +#define SYSBUS_SM501(obj) \ + OBJECT_CHECK(SM501SysBusState, (obj), TYPE_SYSBUS_SM501) + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + SM501State state; + uint32_t vram_size; + uint32_t base; + void *chr_state; +} SM501SysBusState; + +static void sm501_realize_sysbus(DeviceState *dev, Error **errp) +{ + SM501SysBusState *s = SYSBUS_SM501(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + DeviceState *usb_dev; + + sm501_init(&s->state, dev, s->vram_size); + if (get_local_mem_size(&s->state) != s->vram_size) { + error_setg(errp, "Invalid VRAM size, nearest valid size is %" PRIu32, + get_local_mem_size(&s->state)); + return; + } + sysbus_init_mmio(sbd, &s->state.local_mem_region); + sysbus_init_mmio(sbd, &s->state.mmio_region); /* bridge to usb host emulation module */ - dev = qdev_create(NULL, "sysbus-ohci"); - qdev_prop_set_uint32(dev, "num-ports", 2); - qdev_prop_set_uint64(dev, "dma-offset", base); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, - base + MMIO_BASE_OFFSET + SM501_USB_HOST); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + usb_dev = qdev_create(NULL, "sysbus-ohci"); + qdev_prop_set_uint32(usb_dev, "num-ports", 2); + qdev_prop_set_uint64(usb_dev, "dma-offset", s->base); + qdev_init_nofail(usb_dev); + memory_region_add_subregion(&s->state.mmio_region, SM501_USB_HOST, + sysbus_mmio_get_region(SYS_BUS_DEVICE(usb_dev), 0)); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(usb_dev)); /* bridge to serial emulation module */ - if (chr) { - serial_mm_init(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_UART0, 2, + if (s->chr_state) { + serial_mm_init(&s->state.mmio_region, SM501_UART0, 2, NULL, /* TODO : chain irq to IRL */ - 115200, chr, DEVICE_NATIVE_ENDIAN); + 115200, s->chr_state, DEVICE_LITTLE_ENDIAN); } +} - /* create qemu graphic console */ - s->con = graphic_console_init(DEVICE(dev), 0, &sm501_ops, s); +static Property sm501_sysbus_properties[] = { + DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), + DEFINE_PROP_UINT32("base", SM501SysBusState, base, 0), + DEFINE_PROP_PTR("chr-state", SM501SysBusState, chr_state), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sm501_reset_sysbus(DeviceState *dev) +{ + SM501SysBusState *s = SYSBUS_SM501(dev); + sm501_reset(&s->state); } + +static const VMStateDescription vmstate_sm501_sysbus = { + .name = TYPE_SYSBUS_SM501, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(state, SM501SysBusState, 1, + vmstate_sm501_state, SM501State), + VMSTATE_END_OF_LIST() + } +}; + +static void sm501_sysbus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = sm501_realize_sysbus; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->desc = "SM501 Multimedia Companion"; + dc->props = sm501_sysbus_properties; + dc->reset = sm501_reset_sysbus; + dc->vmsd = &vmstate_sm501_sysbus; + /* Note: pointer property "chr-state" may remain null, thus + * no need for dc->cannot_instantiate_with_device_add_yet = true; + */ +} + +static const TypeInfo sm501_sysbus_info = { + .name = TYPE_SYSBUS_SM501, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SM501SysBusState), + .class_init = sm501_sysbus_class_init, +}; + +#define TYPE_PCI_SM501 "sm501" +#define PCI_SM501(obj) OBJECT_CHECK(SM501PCIState, (obj), TYPE_PCI_SM501) + +typedef struct { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + SM501State state; + uint32_t vram_size; +} SM501PCIState; + +static void sm501_realize_pci(PCIDevice *dev, Error **errp) +{ + SM501PCIState *s = PCI_SM501(dev); + + sm501_init(&s->state, DEVICE(dev), s->vram_size); + if (get_local_mem_size(&s->state) != s->vram_size) { + error_setg(errp, "Invalid VRAM size, nearest valid size is %" PRIu32, + get_local_mem_size(&s->state)); + return; + } + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->state.local_mem_region); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->state.mmio_region); +} + +static Property sm501_pci_properties[] = { + DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * M_BYTE), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sm501_reset_pci(DeviceState *dev) +{ + SM501PCIState *s = PCI_SM501(dev); + sm501_reset(&s->state); + /* Bits 2:0 of misc_control register is 001 for PCI */ + s->state.misc_control |= 1; +} + +static const VMStateDescription vmstate_sm501_pci = { + .name = TYPE_PCI_SM501, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, SM501PCIState), + VMSTATE_STRUCT(state, SM501PCIState, 1, + vmstate_sm501_state, SM501State), + VMSTATE_END_OF_LIST() + } +}; + +static void sm501_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = sm501_realize_pci; + k->vendor_id = PCI_VENDOR_ID_SILICON_MOTION; + k->device_id = PCI_DEVICE_ID_SM501; + k->class_id = PCI_CLASS_DISPLAY_OTHER; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->desc = "SM501 Display Controller"; + dc->props = sm501_pci_properties; + dc->reset = sm501_reset_pci; + dc->hotpluggable = false; + dc->vmsd = &vmstate_sm501_pci; +} + +static const TypeInfo sm501_pci_info = { + .name = TYPE_PCI_SM501, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(SM501PCIState), + .class_init = sm501_pci_class_init, +}; + +static void sm501_register_types(void) +{ + type_register_static(&sm501_sysbus_info); + type_register_static(&sm501_pci_info); +} + +type_init(sm501_register_types) diff --git a/hw/display/sm501_template.h b/hw/display/sm501_template.h index f33e499be4..a60abad019 100644 --- a/hw/display/sm501_template.h +++ b/hw/display/sm501_template.h @@ -47,81 +47,67 @@ static void glue(draw_line8_, PIXEL_NAME)( { uint8_t v, r, g, b; do { - v = ldub_p(s); - r = (pal[v] >> 16) & 0xff; - g = (pal[v] >> 8) & 0xff; - b = (pal[v] >> 0) & 0xff; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s ++; - d += BPP; - } while (-- width != 0); + v = ldub_p(s); + r = (pal[v] >> 16) & 0xff; + g = (pal[v] >> 8) & 0xff; + b = (pal[v] >> 0) & 0xff; + *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s++; + d += BPP; + } while (--width != 0); } static void glue(draw_line16_, PIXEL_NAME)( - uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) + uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) { uint16_t rgb565; uint8_t r, g, b; do { - rgb565 = lduw_p(s); - r = ((rgb565 >> 11) & 0x1f) << 3; - g = ((rgb565 >> 5) & 0x3f) << 2; - b = ((rgb565 >> 0) & 0x1f) << 3; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s += 2; - d += BPP; - } while (-- width != 0); + rgb565 = lduw_le_p(s); + r = (rgb565 >> 8) & 0xf8; + g = (rgb565 >> 3) & 0xfc; + b = (rgb565 << 3) & 0xf8; + *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s += 2; + d += BPP; + } while (--width != 0); } static void glue(draw_line32_, PIXEL_NAME)( - uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) + uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) { uint8_t r, g, b; do { - ldub_p(s); -#if defined(TARGET_WORDS_BIGENDIAN) - r = s[1]; - g = s[2]; - b = s[3]; -#else - b = s[0]; - g = s[1]; r = s[2]; -#endif - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s += 4; - d += BPP; - } while (-- width != 0); + g = s[1]; + b = s[0]; + *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s += 4; + d += BPP; + } while (--width != 0); } /** * Draw hardware cursor image on the given line. */ -static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt, - uint8_t * palette, int c_y, uint8_t *d, int width) +static void glue(draw_hwc_line_, PIXEL_NAME)(uint8_t *d, const uint8_t *s, + int width, const uint8_t *palette, int c_x, int c_y) { - int x, i; - uint8_t bitset = 0; - - /* get hardware cursor pattern */ - uint32_t cursor_addr = get_hwc_address(s, crt); - assert(0 <= c_y && c_y < SM501_HWC_HEIGHT); - cursor_addr += 64 * c_y / 4; /* 4 pixels per byte */ - cursor_addr += s->base; + int i; + uint8_t r, g, b, v, bitset = 0; /* get cursor position */ - x = get_hwc_x(s, crt); - d += x * BPP; - - for (i = 0; i < SM501_HWC_WIDTH && x + i < width; i++) { - uint8_t v; + assert(0 <= c_y && c_y < SM501_HWC_HEIGHT); + s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */ + d += c_x * BPP; + for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) { /* get pixel value */ if (i % 4 == 0) { - bitset = ldub_phys(&address_space_memory, cursor_addr); - cursor_addr++; + bitset = ldub_p(s); + s++; } v = bitset & 3; bitset >>= 2; @@ -129,10 +115,10 @@ static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt, /* write pixel */ if (v) { v--; - uint8_t r = palette[v * 3 + 0]; - uint8_t g = palette[v * 3 + 1]; - uint8_t b = palette[v * 3 + 2]; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + r = palette[v * 3 + 0]; + g = palette[v * 3 + 1]; + b = palette[v * 3 + 2]; + *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); } d += BPP; } diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 8e26aae801..5a1115cc65 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" -#include "cpu.h" /* FIXME shouldn't use TARGET_PAGE_SIZE */ #include "ui/console.h" #include "ui/pixel_ops.h" #include "hw/loader.h" @@ -93,41 +92,46 @@ typedef struct TCXState { uint16_t cursy; } TCXState; -static void tcx_set_dirty(TCXState *s) +static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len) { - memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); + memory_region_set_dirty(&s->vram_mem, addr, len); + + if (s->depth == 24) { + memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4, + len * 4); + memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4, + len * 4); + } } -static inline int tcx24_check_dirty(TCXState *s, ram_addr_t page, - ram_addr_t page24, ram_addr_t cpage) +static int tcx_check_dirty(TCXState *s, ram_addr_t addr, int len) { int ret; - ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, - DIRTY_MEMORY_VGA); - ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, - DIRTY_MEMORY_VGA); + ret = memory_region_get_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA); + + if (s->depth == 24) { + ret |= memory_region_get_dirty(&s->vram_mem, + s->vram24_offset + addr * 4, len * 4, + DIRTY_MEMORY_VGA); + ret |= memory_region_get_dirty(&s->vram_mem, + s->cplane_offset + addr * 4, len * 4, + DIRTY_MEMORY_VGA); + } + return ret; } -static inline void tcx24_reset_dirty(TCXState *ts, ram_addr_t page_min, - ram_addr_t page_max, ram_addr_t page24, - ram_addr_t cpage) +static void tcx_reset_dirty(TCXState *s, ram_addr_t addr, int len) { - memory_region_reset_dirty(&ts->vram_mem, - page_min, - (page_max - page_min) + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - memory_region_reset_dirty(&ts->vram_mem, - page24 + page_min * 4, - (page_max - page_min) * 4 + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - memory_region_reset_dirty(&ts->vram_mem, - cpage + page_min * 4, - (page_max - page_min) * 4 + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); + memory_region_reset_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA); + + if (s->depth == 24) { + memory_region_reset_dirty(&s->vram_mem, s->vram24_offset + addr * 4, + len * 4, DIRTY_MEMORY_VGA); + memory_region_reset_dirty(&s->vram_mem, s->cplane_offset + addr * 4, + len * 4, DIRTY_MEMORY_VGA); + } } static void update_palette_entries(TCXState *s, int start, int end) @@ -136,27 +140,14 @@ static void update_palette_entries(TCXState *s, int start, int end) int i; for (i = start; i < end; i++) { - switch (surface_bits_per_pixel(surface)) { - default: - case 8: - s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); - break; - case 15: - s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); - break; - case 16: - s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); - break; - case 32: - if (is_surface_bgr(surface)) { - s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); - } else { - s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); - } - break; + if (is_surface_bgr(surface)) { + s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); + } else { + s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); } + break; } - tcx_set_dirty(s); + tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); } static void tcx_draw_line32(TCXState *s1, uint8_t *d, @@ -172,31 +163,6 @@ static void tcx_draw_line32(TCXState *s1, uint8_t *d, } } -static void tcx_draw_line16(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - uint16_t *p = (uint16_t *)d; - - for (x = 0; x < width; x++) { - val = *s++; - *p++ = s1->palette[val]; - } -} - -static void tcx_draw_line8(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - - for(x = 0; x < width; x++) { - val = *s++; - *d++ = s1->palette[val]; - } -} - static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, int y, int width) { @@ -223,57 +189,6 @@ static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, } } -static void tcx_draw_cursor16(TCXState *s1, uint8_t *d, - int y, int width) -{ - int x, len; - uint32_t mask, bits; - uint16_t *p = (uint16_t *)d; - - y = y - s1->cursy; - mask = s1->cursmask[y]; - bits = s1->cursbits[y]; - len = MIN(width - s1->cursx, 32); - p = &p[s1->cursx]; - for (x = 0; x < len; x++) { - if (mask & 0x80000000) { - if (bits & 0x80000000) { - *p = s1->palette[259]; - } else { - *p = s1->palette[258]; - } - } - p++; - mask <<= 1; - bits <<= 1; - } -} - -static void tcx_draw_cursor8(TCXState *s1, uint8_t *d, - int y, int width) -{ - int x, len; - uint32_t mask, bits; - - y = y - s1->cursy; - mask = s1->cursmask[y]; - bits = s1->cursbits[y]; - len = MIN(width - s1->cursx, 32); - d = &d[s1->cursx]; - for (x = 0; x < len; x++) { - if (mask & 0x80000000) { - if (bits & 0x80000000) { - *d = s1->palette[259]; - } else { - *d = s1->palette[258]; - } - } - d++; - mask <<= 1; - bits <<= 1; - } -} - /* XXX Could be much more optimal: * detect if line/page/whole screen is in 24 bit mode @@ -322,10 +237,8 @@ static void tcx_update_display(void *opaque) ram_addr_t page, page_min, page_max; int y, y_start, dd, ds; uint8_t *d, *s; - void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); - void (*fc)(TCXState *s1, uint8_t *dst, int y, int width); - if (surface_bits_per_pixel(surface) == 0) { + if (surface_bits_per_pixel(surface) != 32) { return; } @@ -338,29 +251,9 @@ static void tcx_update_display(void *opaque) dd = surface_stride(surface); ds = 1024; - switch (surface_bits_per_pixel(surface)) { - case 32: - f = tcx_draw_line32; - fc = tcx_draw_cursor32; - break; - case 15: - case 16: - f = tcx_draw_line16; - fc = tcx_draw_cursor16; - break; - default: - case 8: - f = tcx_draw_line8; - fc = tcx_draw_cursor8; - break; - case 0: - return; - } - memory_region_sync_dirty_bitmap(&ts->vram_mem); - for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) { - if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA)) { + for (y = 0; y < ts->height; y++, page += ds) { + if (tcx_check_dirty(ts, page, ds)) { if (y_start < 0) y_start = y; if (page < page_min) @@ -368,37 +261,10 @@ static void tcx_update_display(void *opaque) if (page > page_max) page_max = page; - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); + tcx_draw_line32(ts, d, s, ts->width); if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); + tcx_draw_cursor32(ts, d, y, ts->width); } - d += dd; - s += ds; - y++; } else { if (y_start >= 0) { /* flush to display */ @@ -406,10 +272,9 @@ static void tcx_update_display(void *opaque) ts->width, y - y_start); y_start = -1; } - d += dd * 4; - s += ds * 4; - y += 4; } + s += ds; + d += dd; } if (y_start >= 0) { /* flush to display */ @@ -418,10 +283,7 @@ static void tcx_update_display(void *opaque) } /* reset modified pages */ if (page_max >= page_min) { - memory_region_reset_dirty(&ts->vram_mem, - page_min, - (page_max - page_min) + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); + tcx_reset_dirty(ts, page_min, page_max - page_min); } } @@ -429,7 +291,7 @@ static void tcx24_update_display(void *opaque) { TCXState *ts = opaque; DisplaySurface *surface = qemu_console_surface(ts->con); - ram_addr_t page, page_min, page_max, cpage, page24; + ram_addr_t page, page_min, page_max; int y, y_start, dd, ds; uint8_t *d, *s; uint32_t *cptr, *s24; @@ -439,8 +301,6 @@ static void tcx24_update_display(void *opaque) } page = 0; - page24 = ts->vram24_offset; - cpage = ts->cplane_offset; y_start = -1; page_min = -1; page_max = 0; @@ -452,9 +312,8 @@ static void tcx24_update_display(void *opaque) ds = 1024; memory_region_sync_dirty_bitmap(&ts->vram_mem); - for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE, - page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { - if (tcx24_check_dirty(ts, page, page24, cpage)) { + for (y = 0; y < ts->height; y++, page += ds) { + if (tcx_check_dirty(ts, page, ds)) { if (y_start < 0) y_start = y; if (page < page_min) @@ -465,38 +324,6 @@ static void tcx24_update_display(void *opaque) if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { tcx_draw_cursor32(ts, d, y, ts->width); } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; } else { if (y_start >= 0) { /* flush to display */ @@ -504,12 +331,11 @@ static void tcx24_update_display(void *opaque) ts->width, y - y_start); y_start = -1; } - d += dd * 4; - s += ds * 4; - cptr += ds * 4; - s24 += ds * 4; - y += 4; } + d += dd; + s += ds; + cptr += ds; + s24 += ds; } if (y_start >= 0) { /* flush to display */ @@ -518,7 +344,7 @@ static void tcx24_update_display(void *opaque) } /* reset modified pages */ if (page_max >= page_min) { - tcx24_reset_dirty(ts, page_min, page_max, page24, cpage); + tcx_reset_dirty(ts, page_min, page_max - page_min); } } @@ -526,7 +352,7 @@ static void tcx_invalidate_display(void *opaque) { TCXState *s = opaque; - tcx_set_dirty(s); + tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); qemu_console_resize(s->con, s->width, s->height); } @@ -534,7 +360,7 @@ static void tcx24_invalidate_display(void *opaque) { TCXState *s = opaque; - tcx_set_dirty(s); + tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); qemu_console_resize(s->con, s->width, s->height); } @@ -543,7 +369,7 @@ static int vmstate_tcx_post_load(void *opaque, int version_id) TCXState *s = opaque; update_palette_entries(s, 0, 256); - tcx_set_dirty(s); + tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); return 0; } @@ -699,7 +525,7 @@ static void tcx_stip_writel(void *opaque, hwaddr addr, val <<= 1; } } - memory_region_set_dirty(&s->vram_mem, addr, 32); + tcx_set_dirty(s, addr, 32); } } @@ -732,7 +558,7 @@ static void tcx_rstip_writel(void *opaque, hwaddr addr, val <<= 1; } } - memory_region_set_dirty(&s->vram_mem, addr, 32); + tcx_set_dirty(s, addr, 32); } } @@ -790,7 +616,7 @@ static void tcx_blit_writel(void *opaque, hwaddr addr, memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); } } - memory_region_set_dirty(&s->vram_mem, addr, len); + tcx_set_dirty(s, addr, len); } } @@ -824,7 +650,7 @@ static void tcx_rblit_writel(void *opaque, hwaddr addr, memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4); } } - memory_region_set_dirty(&s->vram_mem, addr, len); + tcx_set_dirty(s, addr, len); } } @@ -861,7 +687,7 @@ static void tcx_invalidate_cursor_position(TCXState *s) start = ymin * 1024; end = ymax * 1024; - memory_region_set_dirty(&s->vram_mem, start, end-start); + tcx_set_dirty(s, start, end - start); } static uint64_t tcx_thc_readl(void *opaque, hwaddr addr, @@ -1017,8 +843,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp) vmstate_register_ram_global(&s->rom); fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); if (fcode_filename) { - ret = load_image_targphys(fcode_filename, s->prom_addr, - FCODE_MAX_ROM_SIZE); + ret = load_image_mr(fcode_filename, &s->rom); g_free(fcode_filename); if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { error_report("tcx: could not load prom '%s'", TCX_ROM_FILE); @@ -1076,7 +901,6 @@ static Property tcx_properties[] = { DEFINE_PROP_UINT16("width", TCXState, width, -1), DEFINE_PROP_UINT16("height", TCXState, height, -1), DEFINE_PROP_UINT16("depth", TCXState, depth, -1), - DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga.c b/hw/display/vga.c index 69c3e1d674..b2516c8d21 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1434,6 +1434,14 @@ void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2) } } +static bool vga_scanline_invalidated(VGACommonState *s, int y) +{ + if (y >= VGA_MAX_HEIGHT) { + return false; + } + return s->invalidated_y_table[y >> 5] & (1 << (y & 0x1f)); +} + void vga_sync_dirty_bitmap(VGACommonState *s) { memory_region_sync_dirty_bitmap(&s->vram); @@ -1457,7 +1465,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) DisplaySurface *surface = qemu_console_surface(s->con); int y1, y, update, linesize, y_start, double_scan, mask, depth; int width, height, shift_control, line_offset, bwidth, bits; - ram_addr_t page0, page1, page_min, page_max; + ram_addr_t page0, page1; + DirtyBitmapSnapshot *snap = NULL; int disp_width, multi_scan, multi_run; uint8_t *d; uint32_t v, addr1, addr; @@ -1472,9 +1481,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) full_update |= update_basic_params(s); - if (!full_update) - vga_sync_dirty_bitmap(s); - s->get_resolution(s, &width, &height); disp_width = width; @@ -1617,11 +1623,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) addr1 = (s->start_addr * 4); bwidth = (width * bits + 7) / 8; y_start = -1; - page_min = -1; - page_max = 0; d = surface_data(surface); linesize = surface_stride(surface); y1 = 0; + + if (!full_update) { + vga_sync_dirty_bitmap(s); + snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1, + bwidth * height, + DIRTY_MEMORY_VGA); + } + for(y = 0; y < height; y++) { addr = addr1; if (!(s->cr[VGA_CRTC_MODE] & 1)) { @@ -1636,17 +1648,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) update = full_update; page0 = addr; page1 = addr + bwidth - 1; - update |= memory_region_get_dirty(&s->vram, page0, page1 - page0, - DIRTY_MEMORY_VGA); - /* explicit invalidation for the hardware cursor */ - update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1; + if (full_update) { + update = 1; + } else { + update = memory_region_snapshot_get_dirty(&s->vram, snap, + page0, page1 - page0); + } + /* explicit invalidation for the hardware cursor (cirrus only) */ + update |= vga_scanline_invalidated(s, y); if (update) { if (y_start < 0) y_start = y; - if (page0 < page_min) - page_min = page0; - if (page1 > page_max) - page_max = page1; if (!(is_buffer_shared(surface))) { vga_draw_line(s, d, s->vram_ptr + addr, width); if (s->cursor_draw_line) @@ -1679,14 +1691,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) dpy_gfx_update(s->con, 0, y_start, disp_width, y - y_start); } - /* reset modified pages */ - if (page_max >= page_min) { - memory_region_reset_dirty(&s->vram, - page_min, - page_max - page_min, - DIRTY_MEMORY_VGA); - } - memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4); + g_free(snap); + memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table)); } static void vga_draw_blank(VGACommonState *s, int full_update) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 9b530ab5b0..e1056f34df 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -258,41 +258,22 @@ void virtio_gpu_get_display_info(VirtIOGPU *g, static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format) { switch (virtio_gpu_format) { -#ifdef HOST_WORDS_BIGENDIAN case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: - return PIXMAN_b8g8r8x8; + return PIXMAN_BE_b8g8r8x8; case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: - return PIXMAN_b8g8r8a8; + return PIXMAN_BE_b8g8r8a8; case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: - return PIXMAN_x8r8g8b8; + return PIXMAN_BE_x8r8g8b8; case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: - return PIXMAN_a8r8g8b8; + return PIXMAN_BE_a8r8g8b8; case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: - return PIXMAN_r8g8b8x8; + return PIXMAN_BE_r8g8b8x8; case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: - return PIXMAN_r8g8b8a8; + return PIXMAN_BE_r8g8b8a8; case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: - return PIXMAN_x8b8g8r8; + return PIXMAN_BE_x8b8g8r8; case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: - return PIXMAN_a8b8g8r8; -#else - case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: - return PIXMAN_x8r8g8b8; - case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: - return PIXMAN_a8r8g8b8; - case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: - return PIXMAN_b8g8r8x8; - case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: - return PIXMAN_b8g8r8a8; - case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: - return PIXMAN_x8b8g8r8; - case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: - return PIXMAN_a8b8g8r8; - case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: - return PIXMAN_r8g8b8x8; - case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: - return PIXMAN_r8g8b8a8; -#endif + return PIXMAN_BE_a8b8g8r8; default: return 0; } @@ -1170,8 +1151,8 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, g->config_size); - g->req_state[0].width = 1024; - g->req_state[0].height = 768; + g->req_state[0].width = g->conf.xres; + g->req_state[0].height = g->conf.yres; if (virtio_gpu_virgl_enabled(g->conf)) { /* use larger control queue in 3d mode */ @@ -1291,6 +1272,8 @@ static Property virtio_gpu_properties[] = { DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), #endif + DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024), + DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 6599cf078d..ec5f27d67e 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1118,9 +1118,9 @@ static void vmsvga_update_display(void *opaque) { struct vmsvga_state_s *s = opaque; DisplaySurface *surface; - bool dirty = false; - if (!s->enable) { + if (!s->enable || !s->config) { + /* in standard vga mode */ s->vga.hw_ops->gfx_update(&s->vga); return; } @@ -1131,26 +1131,11 @@ static void vmsvga_update_display(void *opaque) vmsvga_fifo_run(s); vmsvga_update_rect_flush(s); - /* - * Is it more efficient to look at vram VGA-dirty bits or wait - * for the driver to issue SVGA_CMD_UPDATE? - */ - if (memory_region_is_logging(&s->vga.vram, DIRTY_MEMORY_VGA)) { - vga_sync_dirty_bitmap(&s->vga); - dirty = memory_region_get_dirty(&s->vga.vram, 0, - surface_stride(surface) * surface_height(surface), - DIRTY_MEMORY_VGA); - } - if (s->invalidated || dirty) { + if (s->invalidated) { s->invalidated = 0; dpy_gfx_update(s->vga.con, 0, 0, surface_width(surface), surface_height(surface)); } - if (dirty) { - memory_region_reset_dirty(&s->vga.vram, 0, - surface_stride(surface) * surface_height(surface), - DIRTY_MEMORY_VGA); - } } static void vmsvga_reset(DeviceState *dev) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 22d8226e43..02f047c8e3 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -595,6 +595,22 @@ static inline uint32_t vtd_get_agaw_from_context_entry(VTDContextEntry *ce) return 30 + (ce->hi & VTD_CONTEXT_ENTRY_AW) * 9; } +static inline uint64_t vtd_iova_limit(VTDContextEntry *ce) +{ + uint32_t ce_agaw = vtd_get_agaw_from_context_entry(ce); + return 1ULL << MIN(ce_agaw, VTD_MGAW); +} + +/* Return true if IOVA passes range check, otherwise false. */ +static inline bool vtd_iova_range_check(uint64_t iova, VTDContextEntry *ce) +{ + /* + * Check if @iova is above 2^X-1, where X is the minimum of MGAW + * in CAP_REG and AW in context-entry. + */ + return !(iova & ~(vtd_iova_limit(ce) - 1)); +} + static const uint64_t vtd_paging_entry_rsvd_field[] = { [0] = ~0ULL, /* For not large page */ @@ -630,13 +646,9 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, uint32_t level = vtd_get_level_from_context_entry(ce); uint32_t offset; uint64_t slpte; - uint32_t ce_agaw = vtd_get_agaw_from_context_entry(ce); uint64_t access_right_check; - /* Check if @iova is above 2^X-1, where X is the minimum of MGAW - * in CAP_REG and AW in context-entry. - */ - if (iova & ~((1ULL << MIN(ce_agaw, VTD_MGAW)) - 1)) { + if (!vtd_iova_range_check(iova, ce)) { VTD_DPRINTF(GENERAL, "error: iova 0x%"PRIx64 " exceeds limits", iova); return -VTD_FR_ADDR_BEYOND_MGAW; } @@ -684,6 +696,135 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, } } +typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); + +/** + * vtd_page_walk_level - walk over specific level for IOVA range + * + * @addr: base GPA addr to start the walk + * @start: IOVA range start address + * @end: IOVA range end address (start <= addr < end) + * @hook_fn: hook func to be called when detected page + * @private: private data to be passed into hook func + * @read: whether parent level has read permission + * @write: whether parent level has write permission + * @notify_unmap: whether we should notify invalid entries + */ +static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, + uint64_t end, vtd_page_walk_hook hook_fn, + void *private, uint32_t level, + bool read, bool write, bool notify_unmap) +{ + bool read_cur, write_cur, entry_valid; + uint32_t offset; + uint64_t slpte; + uint64_t subpage_size, subpage_mask; + IOMMUTLBEntry entry; + uint64_t iova = start; + uint64_t iova_next; + int ret = 0; + + trace_vtd_page_walk_level(addr, level, start, end); + + subpage_size = 1ULL << vtd_slpt_level_shift(level); + subpage_mask = vtd_slpt_level_page_mask(level); + + while (iova < end) { + iova_next = (iova & subpage_mask) + subpage_size; + + offset = vtd_iova_level_offset(iova, level); + slpte = vtd_get_slpte(addr, offset); + + if (slpte == (uint64_t)-1) { + trace_vtd_page_walk_skip_read(iova, iova_next); + goto next; + } + + if (vtd_slpte_nonzero_rsvd(slpte, level)) { + trace_vtd_page_walk_skip_reserve(iova, iova_next); + goto next; + } + + /* Permissions are stacked with parents' */ + read_cur = read && (slpte & VTD_SL_R); + write_cur = write && (slpte & VTD_SL_W); + + /* + * As long as we have either read/write permission, this is a + * valid entry. The rule works for both page entries and page + * table entries. + */ + entry_valid = read_cur | write_cur; + + if (vtd_is_last_slpte(slpte, level)) { + entry.target_as = &address_space_memory; + entry.iova = iova & subpage_mask; + /* NOTE: this is only meaningful if entry_valid == true */ + entry.translated_addr = vtd_get_slpte_addr(slpte); + entry.addr_mask = ~subpage_mask; + entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); + if (!entry_valid && !notify_unmap) { + trace_vtd_page_walk_skip_perm(iova, iova_next); + goto next; + } + trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr, + entry.addr_mask, entry.perm); + if (hook_fn) { + ret = hook_fn(&entry, private); + if (ret < 0) { + return ret; + } + } + } else { + if (!entry_valid) { + trace_vtd_page_walk_skip_perm(iova, iova_next); + goto next; + } + ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte), iova, + MIN(iova_next, end), hook_fn, private, + level - 1, read_cur, write_cur, + notify_unmap); + if (ret < 0) { + return ret; + } + } + +next: + iova = iova_next; + } + + return 0; +} + +/** + * vtd_page_walk - walk specific IOVA range, and call the hook + * + * @ce: context entry to walk upon + * @start: IOVA address to start the walk + * @end: IOVA range end address (start <= addr < end) + * @hook_fn: the hook that to be called for each detected area + * @private: private data for the hook function + */ +static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, + vtd_page_walk_hook hook_fn, void *private, + bool notify_unmap) +{ + dma_addr_t addr = vtd_get_slpt_base_from_context(ce); + uint32_t level = vtd_get_level_from_context_entry(ce); + + if (!vtd_iova_range_check(start, ce)) { + return -VTD_FR_ADDR_BEYOND_MGAW; + } + + if (!vtd_iova_range_check(end, ce)) { + /* Fix end so that it reaches the maximum */ + end = vtd_iova_limit(ce); + } + + return vtd_page_walk_level(addr, start, end, hook_fn, private, + level, true, true, notify_unmap); +} + /* Map a device to its corresponding domain (context-entry) */ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, uint8_t devfn, VTDContextEntry *ce) @@ -898,6 +1039,15 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) s->intr_root, s->intr_size); } +static void vtd_iommu_replay_all(IntelIOMMUState *s) +{ + IntelIOMMUNotifierNode *node; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + memory_region_iommu_replay_all(&node->vtd_as->iommu); + } +} + static void vtd_context_global_invalidate(IntelIOMMUState *s) { trace_vtd_inv_desc_cc_global(); @@ -905,6 +1055,14 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s) if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { vtd_reset_context_cache(s); } + /* + * From VT-d spec 6.5.2.1, a global context entry invalidation + * should be followed by a IOTLB global invalidation, so we should + * be safe even without this. Hoewever, let's replay the region as + * well to be safer, and go back here when we need finer tunes for + * VT-d emulation codes. + */ + vtd_iommu_replay_all(s); } @@ -971,6 +1129,16 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it), VTD_PCI_FUNC(devfn_it)); vtd_as->context_cache_entry.context_cache_gen = 0; + /* + * So a device is moving out of (or moving into) a + * domain, a replay() suites here to notify all the + * IOMMU_NOTIFIER_MAP registers about this change. + * This won't bring bad even if we have no such + * notifier registered - the IOMMU notification + * framework will skip MAP notifications if that + * happened. + */ + memory_region_iommu_replay_all(&vtd_as->iommu); } } } @@ -1012,12 +1180,53 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) { trace_vtd_iotlb_reset("global invalidation recved"); vtd_reset_iotlb(s); + vtd_iommu_replay_all(s); } static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) { + IntelIOMMUNotifierNode *node; + VTDContextEntry ce; + VTDAddressSpace *vtd_as; + g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, &domain_id); + + QLIST_FOREACH(node, &s->notifiers_list, next) { + vtd_as = node->vtd_as; + if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce) && + domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { + memory_region_iommu_replay_all(&vtd_as->iommu); + } + } +} + +static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, + void *private) +{ + memory_region_notify_iommu((MemoryRegion *)private, *entry); + return 0; +} + +static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + uint16_t domain_id, hwaddr addr, + uint8_t am) +{ + IntelIOMMUNotifierNode *node; + VTDContextEntry ce; + int ret; + + QLIST_FOREACH(node, &(s->notifiers_list), next) { + VTDAddressSpace *vtd_as = node->vtd_as; + ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce); + if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { + vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE, + vtd_page_invalidate_notify_hook, + (void *)&vtd_as->iommu, true); + } + } } static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, @@ -1030,6 +1239,7 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, info.addr = addr; info.mask = ~((1 << am) - 1); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); + vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am); } /* Flush IOTLB @@ -1151,9 +1361,49 @@ static void vtd_handle_gcmd_sirtp(IntelIOMMUState *s) vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_IRTPS); } +static void vtd_switch_address_space(VTDAddressSpace *as) +{ + assert(as); + + trace_vtd_switch_address_space(pci_bus_num(as->bus), + VTD_PCI_SLOT(as->devfn), + VTD_PCI_FUNC(as->devfn), + as->iommu_state->dmar_enabled); + + /* Turn off first then on the other */ + if (as->iommu_state->dmar_enabled) { + memory_region_set_enabled(&as->sys_alias, false); + memory_region_set_enabled(&as->iommu, true); + } else { + memory_region_set_enabled(&as->iommu, false); + memory_region_set_enabled(&as->sys_alias, true); + } +} + +static void vtd_switch_address_space_all(IntelIOMMUState *s) +{ + GHashTableIter iter; + VTDBus *vtd_bus; + int i; + + g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) { + for (i = 0; i < X86_IOMMU_PCI_DEVFN_MAX; i++) { + if (!vtd_bus->dev_as[i]) { + continue; + } + vtd_switch_address_space(vtd_bus->dev_as[i]); + } + } +} + /* Handle Translation Enable/Disable */ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) { + if (s->dmar_enabled == en) { + return; + } + VTD_DPRINTF(CSR, "Translation Enable %s", (en ? "on" : "off")); if (en) { @@ -1168,6 +1418,8 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) /* Ok - report back to driver */ vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0); } + + vtd_switch_address_space_all(s); } /* Handle Interrupt Remap Enable/Disable */ @@ -1457,7 +1709,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, entry.iova = addr; entry.perm = IOMMU_NONE; entry.translated_addr = 0; - memory_region_notify_iommu(entry.target_as->root, entry); + memory_region_notify_iommu(&vtd_dev_as->iommu, entry); done: return true; @@ -2005,15 +2257,33 @@ static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu, IOMMUNotifierFlag new) { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); + IntelIOMMUState *s = vtd_as->iommu_state; + IntelIOMMUNotifierNode *node = NULL; + IntelIOMMUNotifierNode *next_node = NULL; - if (new & IOMMU_NOTIFIER_MAP) { - error_report("Device at bus %s addr %02x.%d requires iommu " - "notifier which is currently not supported by " - "intel-iommu emulation", - vtd_as->bus->qbus.name, PCI_SLOT(vtd_as->devfn), - PCI_FUNC(vtd_as->devfn)); + if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) { + error_report("We need to set cache_mode=1 for intel-iommu to enable " + "device assignment with IOMMU protection."); exit(1); } + + if (old == IOMMU_NOTIFIER_NONE) { + node = g_malloc0(sizeof(*node)); + node->vtd_as = vtd_as; + QLIST_INSERT_HEAD(&s->notifiers_list, node, next); + return; + } + + /* update notifier node with new flags */ + QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { + if (node->vtd_as == vtd_as) { + if (new == IOMMU_NOTIFIER_NONE) { + QLIST_REMOVE(node, next); + g_free(node); + } + return; + } + } } static const VMStateDescription vtd_vmstate = { @@ -2389,19 +2659,150 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as->devfn = (uint8_t)devfn; vtd_dev_as->iommu_state = s; vtd_dev_as->context_cache_entry.context_cache_gen = 0; + + /* + * Memory region relationships looks like (Address range shows + * only lower 32 bits to make it short in length...): + * + * |-----------------+-------------------+----------| + * | Name | Address range | Priority | + * |-----------------+-------------------+----------+ + * | vtd_root | 00000000-ffffffff | 0 | + * | intel_iommu | 00000000-ffffffff | 1 | + * | vtd_sys_alias | 00000000-ffffffff | 1 | + * | intel_iommu_ir | fee00000-feefffff | 64 | + * |-----------------+-------------------+----------| + * + * We enable/disable DMAR by switching enablement for + * vtd_sys_alias and intel_iommu regions. IR region is always + * enabled. + */ memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s), - &s->iommu_ops, "intel_iommu", UINT64_MAX); + &s->iommu_ops, "intel_iommu_dmar", + UINT64_MAX); + memory_region_init_alias(&vtd_dev_as->sys_alias, OBJECT(s), + "vtd_sys_alias", get_system_memory(), + 0, memory_region_size(get_system_memory())); memory_region_init_io(&vtd_dev_as->iommu_ir, OBJECT(s), &vtd_mem_ir_ops, s, "intel_iommu_ir", VTD_INTERRUPT_ADDR_SIZE); - memory_region_add_subregion(&vtd_dev_as->iommu, VTD_INTERRUPT_ADDR_FIRST, - &vtd_dev_as->iommu_ir); - address_space_init(&vtd_dev_as->as, - &vtd_dev_as->iommu, name); + memory_region_init(&vtd_dev_as->root, OBJECT(s), + "vtd_root", UINT64_MAX); + memory_region_add_subregion_overlap(&vtd_dev_as->root, + VTD_INTERRUPT_ADDR_FIRST, + &vtd_dev_as->iommu_ir, 64); + address_space_init(&vtd_dev_as->as, &vtd_dev_as->root, name); + memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, + &vtd_dev_as->sys_alias, 1); + memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, + &vtd_dev_as->iommu, 1); + vtd_switch_address_space(vtd_dev_as); } return vtd_dev_as; } +/* Unmap the whole range in the notifier's scope. */ +static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) +{ + IOMMUTLBEntry entry; + hwaddr size; + hwaddr start = n->start; + hwaddr end = n->end; + + /* + * Note: all the codes in this function has a assumption that IOVA + * bits are no more than VTD_MGAW bits (which is restricted by + * VT-d spec), otherwise we need to consider overflow of 64 bits. + */ + + if (end > VTD_ADDRESS_SIZE) { + /* + * Don't need to unmap regions that is bigger than the whole + * VT-d supported address space size + */ + end = VTD_ADDRESS_SIZE; + } + + assert(start <= end); + size = end - start; + + if (ctpop64(size) != 1) { + /* + * This size cannot format a correct mask. Let's enlarge it to + * suite the minimum available mask. + */ + int n = 64 - clz64(size); + if (n > VTD_MGAW) { + /* should not happen, but in case it happens, limit it */ + n = VTD_MGAW; + } + size = 1ULL << n; + } + + entry.target_as = &address_space_memory; + /* Adjust iova for the size */ + entry.iova = n->start & ~(size - 1); + /* This field is meaningless for unmap */ + entry.translated_addr = 0; + entry.perm = IOMMU_NONE; + entry.addr_mask = size - 1; + + trace_vtd_as_unmap_whole(pci_bus_num(as->bus), + VTD_PCI_SLOT(as->devfn), + VTD_PCI_FUNC(as->devfn), + entry.iova, size); + + memory_region_notify_one(n, &entry); +} + +static void vtd_address_space_unmap_all(IntelIOMMUState *s) +{ + IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; + IOMMUNotifier *n; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + vtd_as = node->vtd_as; + IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { + vtd_address_space_unmap(vtd_as, n); + } + } +} + +static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) +{ + memory_region_notify_one((IOMMUNotifier *)private, entry); + return 0; +} + +static void vtd_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) +{ + VTDAddressSpace *vtd_as = container_of(mr, VTDAddressSpace, iommu); + IntelIOMMUState *s = vtd_as->iommu_state; + uint8_t bus_n = pci_bus_num(vtd_as->bus); + VTDContextEntry ce; + + /* + * The replay can be triggered by either a invalidation or a newly + * created entry. No matter what, we release existing mappings + * (it means flushing caches for UNMAP-only registers). + */ + vtd_address_space_unmap(vtd_as, n); + + if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) { + trace_vtd_replay_ce_valid(bus_n, PCI_SLOT(vtd_as->devfn), + PCI_FUNC(vtd_as->devfn), + VTD_CONTEXT_ENTRY_DID(ce.hi), + ce.hi, ce.lo); + vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false); + } else { + trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), + PCI_FUNC(vtd_as->devfn)); + } + + return; +} + /* Do the initialization. It will also be called when reset, so pay * attention when adding new initialization stuff. */ @@ -2416,6 +2817,7 @@ static void vtd_init(IntelIOMMUState *s) s->iommu_ops.translate = vtd_iommu_translate; s->iommu_ops.notify_flag_changed = vtd_iommu_notify_flag_changed; + s->iommu_ops.replay = vtd_iommu_replay; s->root = 0; s->root_extended = false; s->dmar_enabled = false; @@ -2511,6 +2913,11 @@ static void vtd_reset(DeviceState *dev) VTD_DPRINTF(GENERAL, ""); vtd_init(s); + + /* + * When device reset, throw away all mappings and external caches + */ + vtd_address_space_unmap_all(s); } static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) @@ -2574,6 +2981,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) return; } + QLIST_INIT(&s->notifiers_list); memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 41041219ba..29d67075f4 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -197,6 +197,7 @@ #define VTD_DOMAIN_ID_MASK ((1UL << VTD_DOMAIN_ID_SHIFT) - 1) #define VTD_CAP_ND (((VTD_DOMAIN_ID_SHIFT - 4) / 2) & 7ULL) #define VTD_MGAW 39 /* Maximum Guest Address Width */ +#define VTD_ADDRESS_SIZE (1ULL << VTD_MGAW) #define VTD_CAP_MGAW (((VTD_MGAW - 1) & 0x3fULL) << 16) #define VTD_MAMV 18ULL #define VTD_CAP_MAMV (VTD_MAMV << 48) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d24388e05f..f3b372a18f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1104,9 +1104,7 @@ static void pc_new_cpu(const char *typename, int64_t apic_id, Error **errp) object_property_set_bool(cpu, true, "realized", &local_err); object_unref(cpu); - if (local_err) { - error_propagate(errp, local_err); - } + error_propagate(errp, local_err); } void pc_hot_add_cpu(const int64_t id, Error **errp) diff --git a/hw/i386/trace-events b/hw/i386/trace-events index baed874a80..04a6980800 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -4,7 +4,6 @@ x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32 # hw/i386/intel_iommu.c -vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" vtd_inv_desc(const char *type, uint64_t hi, uint64_t lo) "invalidate desc type %s high 0x%"PRIx64" low 0x%"PRIx64 vtd_inv_desc_invalid(uint64_t hi, uint64_t lo) "invalid inv desc hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_inv_desc_cc_domain(uint16_t domain) "context invalidate domain 0x%"PRIx16 @@ -30,6 +29,15 @@ vtd_iotlb_cc_hit(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32 vtd_iotlb_cc_update(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32_t gen1, uint32_t gen2) "IOTLB context update bus 0x%"PRIx8" devfn 0x%"PRIx8" high 0x%"PRIx64" low 0x%"PRIx64" gen %"PRIu32" -> gen %"PRIu32 vtd_iotlb_reset(const char *reason) "IOTLB reset (reason: %s)" vtd_fault_disabled(void) "Fault processing disabled for context entry" +vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 +vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 +vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 +vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" +vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" +vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" +vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" +vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 # hw/i386/amd_iommu.c amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32 diff --git a/hw/i386/xen/Makefile.objs b/hw/i386/xen/Makefile.objs index 801a68d326..be9d10cf2a 100644 --- a/hw/i386/xen/Makefile.objs +++ b/hw/i386/xen/Makefile.objs @@ -1 +1 @@ -obj-y += xen_platform.o xen_apic.o xen_pvdevice.o +obj-y += xen_platform.o xen_apic.o xen_pvdevice.o xen-hvm.o xen-mapcache.o diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index 321fe60fed..547438db13 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -4,3 +4,20 @@ xen_platform_log(char *s) "xen platform: %s" # hw/i386/xen/xen_pvdevice.c xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address %"PRIx64")" xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address %"PRIx64")" + +# xen-hvm.c +xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx" +xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "%#"PRIx64" size %#lx, log_dirty %i" +handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" +handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" +handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" +cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" +cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=%#"PRIx64" port=%#"PRIx64" size=%d" +cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=%#"PRIx64" port=%#"PRIx64" size=%d" +cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" + +# xen-mapcache.c +xen_map_cache(uint64_t phys_addr) "want %#"PRIx64 +xen_remap_bucket(uint64_t index) "index %#"PRIx64 +xen_map_cache_return(void* ptr) "%p" + diff --git a/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 5043beb98f..b1c05ffb86 100644 --- a/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -22,7 +22,7 @@ #include "qemu/error-report.h" #include "qemu/range.h" #include "sysemu/xen-mapcache.h" -#include "trace-root.h" +#include "trace.h" #include "exec/address-spaces.h" #include <xen/hvm/ioreq.h> @@ -125,8 +125,8 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) void xen_piix3_set_irq(void *opaque, int irq_num, int level) { - xc_hvm_set_pci_intx_level(xen_xc, xen_domid, 0, 0, irq_num >> 2, - irq_num & 3, level); + xen_set_pci_intx_level(xen_domid, 0, 0, irq_num >> 2, + irq_num & 3, level); } void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) @@ -141,7 +141,7 @@ void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) } v &= 0xf; if (((address + i) >= 0x60) && ((address + i) <= 0x63)) { - xc_hvm_set_pci_link_route(xen_xc, xen_domid, address + i - 0x60, v); + xen_set_pci_link_route(xen_domid, address + i - 0x60, v); } } } @@ -156,7 +156,7 @@ int xen_is_pirq_msi(uint32_t msi_data) void xen_hvm_inject_msi(uint64_t addr, uint32_t data) { - xc_hvm_inject_msi(xen_xc, xen_domid, addr, data); + xen_inject_msi(xen_domid, addr, data); } static void xen_suspend_notifier(Notifier *notifier, void *data) @@ -168,7 +168,7 @@ static void xen_suspend_notifier(Notifier *notifier, void *data) static void xen_set_irq(void *opaque, int irq, int level) { - xc_hvm_set_isa_irq_level(xen_xc, xen_domid, irq, level); + xen_set_isa_irq_level(xen_domid, irq, level); } qemu_irq *xen_interrupt_controller_init(void) @@ -454,10 +454,10 @@ static void xen_set_memory(struct MemoryListener *listener, return; } else { if (add) { - xen_map_memory_section(xen_xc, xen_domid, state->ioservid, + xen_map_memory_section(xen_domid, state->ioservid, section); } else { - xen_unmap_memory_section(xen_xc, xen_domid, state->ioservid, + xen_unmap_memory_section(xen_domid, state->ioservid, section); } } @@ -481,10 +481,10 @@ static void xen_set_memory(struct MemoryListener *listener, section->mr, section->offset_within_region); } else { mem_type = HVMMEM_ram_ro; - if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type, - start_addr >> TARGET_PAGE_BITS, - size >> TARGET_PAGE_BITS)) { - DPRINTF("xc_hvm_set_mem_type error, addr: "TARGET_FMT_plx"\n", + if (xen_set_mem_type(xen_domid, mem_type, + start_addr >> TARGET_PAGE_BITS, + size >> TARGET_PAGE_BITS)) { + DPRINTF("xen_set_mem_type error, addr: "TARGET_FMT_plx"\n", start_addr); } } @@ -521,7 +521,7 @@ static void xen_io_add(MemoryListener *listener, memory_region_ref(mr); - xen_map_io_section(xen_xc, xen_domid, state->ioservid, section); + xen_map_io_section(xen_domid, state->ioservid, section); } static void xen_io_del(MemoryListener *listener, @@ -534,7 +534,7 @@ static void xen_io_del(MemoryListener *listener, return; } - xen_unmap_io_section(xen_xc, xen_domid, state->ioservid, section); + xen_unmap_io_section(xen_domid, state->ioservid, section); memory_region_unref(mr); } @@ -547,7 +547,7 @@ static void xen_device_realize(DeviceListener *listener, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pci_dev = PCI_DEVICE(dev); - xen_map_pcidev(xen_xc, xen_domid, state->ioservid, pci_dev); + xen_map_pcidev(xen_domid, state->ioservid, pci_dev); } } @@ -559,7 +559,7 @@ static void xen_device_unrealize(DeviceListener *listener, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pci_dev = PCI_DEVICE(dev); - xen_unmap_pcidev(xen_xc, xen_domid, state->ioservid, pci_dev); + xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev); } } @@ -586,9 +586,8 @@ static void xen_sync_dirty_bitmap(XenIOState *state, return; } - rc = xc_hvm_track_dirty_vram(xen_xc, xen_domid, - start_addr >> TARGET_PAGE_BITS, npages, - bitmap); + rc = xen_track_dirty_vram(xen_domid, start_addr >> TARGET_PAGE_BITS, + npages, bitmap); if (rc < 0) { #ifndef ENODATA #define ENODATA ENOENT @@ -634,7 +633,7 @@ static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { state->log_for_dirtybit = NULL; /* Disable dirty bit tracking */ - xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL); + xen_track_dirty_vram(xen_domid, 0, 0, NULL); } } @@ -1139,7 +1138,7 @@ static void xen_hvm_change_state_handler(void *opaque, int running, xen_main_loop_prepare(state); } - xen_set_ioreq_server_state(xen_xc, xen_domid, + xen_set_ioreq_server_state(xen_domid, state->ioservid, (rstate == RUN_STATE_RUNNING)); } @@ -1227,7 +1226,15 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - xen_create_ioreq_server(xen_xc, xen_domid, &state->ioservid); + if (xen_domid_restrict) { + rc = xen_restrict(xen_domid); + if (rc < 0) { + error_report("failed to restrict: error %d", errno); + goto err; + } + } + + xen_create_ioreq_server(xen_domid, &state->ioservid); state->exit.notify = xen_exit_notifier; qemu_add_exit_notifier(&state->exit); @@ -1238,7 +1245,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) state->wakeup.notify = xen_wakeup_notifier; qemu_register_wakeup_notifier(&state->wakeup); - rc = xen_get_ioreq_server_info(xen_xc, xen_domid, state->ioservid, + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, &ioreq_pfn, &bufioreq_pfn, &bufioreq_evtchn); if (rc < 0) { @@ -1288,7 +1295,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) /* Note: cpus is empty at this point in init */ state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); - rc = xen_set_ioreq_server_state(xen_xc, xen_domid, state->ioservid, true); + rc = xen_set_ioreq_server_state(xen_domid, state->ioservid, true); if (rc < 0) { error_report("failed to enable ioreq server info: error %d handle=%p", errno, xen_xc); @@ -1391,7 +1398,7 @@ void xen_shutdown_fatal_error(const char *fmt, ...) qemu_system_shutdown_request(); } -void xen_modified_memory(ram_addr_t start, ram_addr_t length) +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) { if (unlikely(xen_in_migration)) { int rc; @@ -1403,7 +1410,7 @@ void xen_modified_memory(ram_addr_t start, ram_addr_t length) start_pfn = start >> TARGET_PAGE_BITS; nb_pages = ((start + length + TARGET_PAGE_SIZE - 1) >> TARGET_PAGE_BITS) - start_pfn; - rc = xc_hvm_modified_memory(xen_xc, xen_domid, start_pfn, nb_pages); + rc = xen_modified_memory(xen_domid, start_pfn, nb_pages); if (rc) { fprintf(stderr, "%s failed for "RAM_ADDR_FMT" ("RAM_ADDR_FMT"): %i, %s\n", diff --git a/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index 1a96d2e5db..31debdfb2c 100644 --- a/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -19,7 +19,7 @@ #include <xen/hvm/params.h> #include "sysemu/xen-mapcache.h" -#include "trace-root.h" +#include "trace.h" //#define MAPCACHE_DEBUG diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 6010f35266..1419fc96d2 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -195,7 +195,7 @@ static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t v case 0: /* Platform flags */ { hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ? HVMMEM_ram_ro : HVMMEM_ram_rw; - if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type, 0xc0, 0x40)) { + if (xen_set_mem_type(xen_domid, mem_type, 0xc0, 0x40)) { DPRINTF("unable to change ro/rw state of ROM memory area!\n"); } else { s->flags = val & PFFLAG_ROM_LOCK; diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index adedd0da5f..78426a7daf 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -35,6 +35,7 @@ obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o +obj-$(CONFIG_POWERNV) += xics_pnv.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index bef4caf980..711c11454f 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -21,11 +21,14 @@ S390FLICState *s390_get_flic(void) { - S390FLICState *fs; + static S390FLICState *fs; - fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL)); if (!fs) { - fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL)); + fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL)); + if (!fs) { + fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, + NULL)); + } } return fs; } diff --git a/hw/intc/xics.c b/hw/intc/xics.c index e740989a11..292fffecd3 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -38,21 +38,10 @@ #include "monitor/monitor.h" #include "hw/intc/intc.h" -int xics_get_cpu_index_by_dt_id(int cpu_dt_id) -{ - PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); - - if (cpu) { - return cpu->parent_obj.cpu_index; - } - - return -1; -} - void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(xi, cs->cpu_index); + ICPState *icp = ICP(cpu->intc); assert(icp); assert(cs == icp->cs); @@ -61,15 +50,15 @@ void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) icp->cs = NULL; } -void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu) +void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - ICPState *icp = xics_icp_get(xi, cs->cpu_index); ICPStateClass *icpc; assert(icp); + cpu->intc = OBJECT(icp); icp->cs = cs; icpc = ICP_GET_CLASS(icp); @@ -348,6 +337,7 @@ static void icp_reset(void *dev) static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); + ICPStateClass *icpc = ICP_GET_CLASS(dev); Object *obj; Error *err = NULL; @@ -360,6 +350,10 @@ static void icp_realize(DeviceState *dev, Error **errp) icp->xics = XICS_FABRIC(obj); + if (icpc->realize) { + icpc->realize(dev, errp); + } + qemu_register_reset(icp_reset, dev); } diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c new file mode 100644 index 0000000000..12ae605f10 --- /dev/null +++ b/hw/intc/xics_pnv.c @@ -0,0 +1,192 @@ +/* + * QEMU PowerPC PowerNV Interrupt Control Presenter (ICP) model + * + * Copyright (c) 2017, IBM Corporation. + * + * 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 "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/ppc/xics.h" + +#define ICP_XIRR_POLL 0 /* 1 byte (CPRR) or 4 bytes */ +#define ICP_XIRR 4 /* 1 byte (CPRR) or 4 bytes */ +#define ICP_MFRR 12 /* 1 byte access only */ + +#define ICP_LINKA 16 /* unused */ +#define ICP_LINKB 20 /* unused */ +#define ICP_LINKC 24 /* unused */ + +static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width) +{ + ICPState *icp = ICP(opaque); + PnvICPState *picp = PNV_ICP(opaque); + bool byte0 = (width == 1 && (addr & 0x3) == 0); + uint64_t val = 0xffffffff; + + switch (addr & 0xffc) { + case ICP_XIRR_POLL: + val = icp_ipoll(icp, NULL); + if (byte0) { + val >>= 24; + } else if (width != 4) { + goto bad_access; + } + break; + case ICP_XIRR: + if (byte0) { + val = icp_ipoll(icp, NULL) >> 24; + } else if (width == 4) { + val = icp_accept(icp); + } else { + goto bad_access; + } + break; + case ICP_MFRR: + if (byte0) { + val = icp->mfrr; + } else { + goto bad_access; + } + break; + case ICP_LINKA: + if (width == 4) { + val = picp->links[0]; + } else { + goto bad_access; + } + break; + case ICP_LINKB: + if (width == 4) { + val = picp->links[1]; + } else { + goto bad_access; + } + break; + case ICP_LINKC: + if (width == 4) { + val = picp->links[2]; + } else { + goto bad_access; + } + break; + default: +bad_access: + qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" + HWADDR_PRIx"/%d\n", addr, width); + } + + return val; +} + +static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + ICPState *icp = ICP(opaque); + PnvICPState *picp = PNV_ICP(opaque); + bool byte0 = (width == 1 && (addr & 0x3) == 0); + + switch (addr & 0xffc) { + case ICP_XIRR: + if (byte0) { + icp_set_cppr(icp, val); + } else if (width == 4) { + icp_eoi(icp, val); + } else { + goto bad_access; + } + break; + case ICP_MFRR: + if (byte0) { + icp_set_mfrr(icp, val); + } else { + goto bad_access; + } + break; + case ICP_LINKA: + if (width == 4) { + picp->links[0] = val; + } else { + goto bad_access; + } + break; + case ICP_LINKB: + if (width == 4) { + picp->links[1] = val; + } else { + goto bad_access; + } + break; + case ICP_LINKC: + if (width == 4) { + picp->links[2] = val; + } else { + goto bad_access; + } + break; + default: +bad_access: + qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" + HWADDR_PRIx"/%d\n", addr, width); + } +} + +static const MemoryRegionOps pnv_icp_ops = { + .read = pnv_icp_read, + .write = pnv_icp_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void pnv_icp_realize(DeviceState *dev, Error **errp) +{ + PnvICPState *icp = PNV_ICP(dev); + + memory_region_init_io(&icp->mmio, OBJECT(dev), &pnv_icp_ops, + icp, "icp-thread", 0x1000); +} + +static void pnv_icp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICPStateClass *icpc = ICP_CLASS(klass); + + icpc->realize = pnv_icp_realize; + dc->desc = "PowerNV ICP"; +} + +static const TypeInfo pnv_icp_info = { + .name = TYPE_PNV_ICP, + .parent = TYPE_ICP, + .instance_size = sizeof(PnvICPState), + .class_init = pnv_icp_class_init, + .class_size = sizeof(ICPStateClass), +}; + +static void pnv_icp_register_types(void) +{ + type_register_static(&pnv_icp_info); +} + +type_init(pnv_icp_register_types) diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 84d24b2837..f05308b897 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -43,20 +43,17 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); target_ulong cppr = args[0]; - icp_set_cppr(icp, cppr); + icp_set_cppr(ICP(cpu->intc), cppr); return H_SUCCESS; } static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - target_ulong server = xics_get_cpu_index_by_dt_id(args[0]); target_ulong mfrr = args[1]; - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), server); + ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]); if (!icp) { return H_PARAMETER; @@ -69,9 +66,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); - uint32_t xirr = icp_accept(icp); + uint32_t xirr = icp_accept(ICP(cpu->intc)); args[0] = xirr; return H_SUCCESS; @@ -80,9 +75,7 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); - uint32_t xirr = icp_accept(icp); + uint32_t xirr = icp_accept(ICP(cpu->intc)); args[0] = xirr; args[1] = cpu_get_host_ticks(); @@ -92,21 +85,17 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); target_ulong xirr = args[0]; - icp_eoi(icp, xirr); + icp_eoi(ICP(cpu->intc), xirr); return H_SUCCESS; } static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); uint32_t mfrr; - uint32_t xirr = icp_ipoll(icp, &mfrr); + uint32_t xirr = icp_ipoll(ICP(cpu->intc), &mfrr); args[0] = xirr; args[1] = mfrr; @@ -132,7 +121,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, } nr = rtas_ld(args, 0); - server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1)); + server = rtas_ld(args, 1); priority = rtas_ld(args, 2); if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index c7883d6f5e..277c28cb40 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -27,6 +27,7 @@ #include "qemu/timer.h" #include "hw/ipmi/ipmi.h" #include "qemu/error-report.h" +#include "hw/loader.h" #define IPMI_NETFN_CHASSIS 0x00 @@ -79,6 +80,9 @@ #define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A #define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B #define IPMI_CMD_RUN_INIT_AGENT 0x2C +#define IPMI_CMD_GET_FRU_AREA_INFO 0x10 +#define IPMI_CMD_READ_FRU_DATA 0x11 +#define IPMI_CMD_WRITE_FRU_DATA 0x12 #define IPMI_CMD_GET_SEL_INFO 0x40 #define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41 #define IPMI_CMD_RESERVE_SEL 0x42 @@ -121,6 +125,13 @@ typedef struct IPMISdr { uint8_t overflow; } IPMISdr; +typedef struct IPMIFru { + char *filename; + unsigned int nentries; + uint16_t areasize; + uint8_t *data; +} IPMIFru; + typedef struct IPMISensor { uint8_t status; uint8_t reading; @@ -212,7 +223,9 @@ struct IPMIBmcSim { IPMISel sel; IPMISdr sdr; + IPMIFru fru; IPMISensor sensors[MAX_SENSORS]; + char *sdr_filename; /* Odd netfns are for responses, so we only need the even ones. */ const IPMINetfn *netfns[MAX_NETFNS / 2]; @@ -403,6 +416,22 @@ static int sdr_find_entry(IPMISdr *sdr, uint16_t recid, return 1; } +int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, + const struct ipmi_sdr_compact **sdr, uint16_t *nextrec) + +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + unsigned int pos; + + pos = 0; + if (sdr_find_entry(&ibs->sdr, recid, &pos, nextrec)) { + return -1; + } + + *sdr = (const struct ipmi_sdr_compact *) &ibs->sdr.sdr[pos]; + return 0; +} + static void sel_inc_reservation(IPMISel *sel) { sel->reservation++; @@ -444,6 +473,30 @@ static int attn_irq_enabled(IPMIBmcSim *ibs) IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)); } +void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log) +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) { + return; + } + + if (log && IPMI_BMC_EVENT_LOG_ENABLED(ibs)) { + sel_add_event(ibs, evt); + } + + if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { + goto out; + } + + memcpy(ibs->evtbuf, evt, 16); + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + out: + return; +} static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, uint8_t evd1, uint8_t evd2, uint8_t evd3) { @@ -1315,6 +1368,91 @@ static void get_sel_info(IPMIBmcSim *ibs, rsp_buffer_push(rsp, (ibs->sel.overflow << 7) | 0x02); } +static void get_fru_area_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t fru_entry_size; + + fruid = cmd[2]; + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry_size = ibs->fru.areasize; + + rsp_buffer_push(rsp, fru_entry_size & 0xff); + rsp_buffer_push(rsp, fru_entry_size >> 8 & 0xff); + rsp_buffer_push(rsp, 0x0); +} + +static void read_fru_data(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t offset; + int i; + uint8_t *fru_entry; + unsigned int count; + + fruid = cmd[2]; + offset = (cmd[3] | cmd[4] << 8); + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (offset >= ibs->fru.areasize - 1) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize]; + + count = MIN(cmd[5], ibs->fru.areasize - offset); + + rsp_buffer_push(rsp, count & 0xff); + for (i = 0; i < count; i++) { + rsp_buffer_push(rsp, fru_entry[offset + i]); + } +} + +static void write_fru_data(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t offset; + uint8_t *fru_entry; + unsigned int count; + + fruid = cmd[2]; + offset = (cmd[3] | cmd[4] << 8); + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (offset >= ibs->fru.areasize - 1) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize]; + + count = MIN(cmd_len - 5, ibs->fru.areasize - offset); + + memcpy(fru_entry + offset, cmd + 5, count); + + rsp_buffer_push(rsp, count & 0xff); +} + static void reserve_sel(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, RspBuffer *rsp) @@ -1651,6 +1789,9 @@ static const IPMINetfn app_netfn = { }; static const IPMICmdHandler storage_cmds[] = { + [IPMI_CMD_GET_FRU_AREA_INFO] = { get_fru_area_info, 3 }, + [IPMI_CMD_READ_FRU_DATA] = { read_fru_data, 5 }, + [IPMI_CMD_WRITE_FRU_DATA] = { write_fru_data, 5 }, [IPMI_CMD_GET_SDR_REP_INFO] = { get_sdr_rep_info }, [IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep }, [IPMI_CMD_GET_SDR] = { get_sdr, 8 }, @@ -1696,22 +1837,33 @@ static void ipmi_sdr_init(IPMIBmcSim *ibs) sdrs_size = sizeof(init_sdrs); sdrs = init_sdrs; + if (ibs->sdr_filename && + !g_file_get_contents(ibs->sdr_filename, (gchar **) &sdrs, &sdrs_size, + NULL)) { + error_report("failed to load sdr file '%s'", ibs->sdr_filename); + sdrs_size = sizeof(init_sdrs); + sdrs = init_sdrs; + } for (i = 0; i < sdrs_size; i += len) { struct ipmi_sdr_header *sdrh; if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) { error_report("Problem with recid 0x%4.4x", i); - return; + break; } sdrh = (struct ipmi_sdr_header *) &sdrs[i]; len = ipmi_sdr_length(sdrh); if (i + len > sdrs_size) { error_report("Problem with recid 0x%4.4x", i); - return; + break; } sdr_add_entry(ibs, sdrh, len, NULL); } + + if (sdrs != init_sdrs) { + g_free(sdrs); + } } static const VMStateDescription vmstate_ipmi_sim = { @@ -1742,6 +1894,36 @@ static const VMStateDescription vmstate_ipmi_sim = { } }; +static void ipmi_fru_init(IPMIFru *fru) +{ + int fsize; + int size = 0; + + if (!fru->filename) { + goto out; + } + + fsize = get_image_size(fru->filename); + if (fsize > 0) { + size = QEMU_ALIGN_UP(fsize, fru->areasize); + fru->data = g_malloc0(size); + if (load_image_size(fru->filename, fru->data, fsize) != fsize) { + error_report("Could not load file '%s'", fru->filename); + g_free(fru->data); + fru->data = NULL; + } + } + +out: + if (!fru->data) { + /* give one default FRU */ + size = fru->areasize; + fru->data = g_malloc0(size); + } + + fru->nentries = size / fru->areasize; +} + static void ipmi_sim_realize(DeviceState *dev, Error **errp) { IPMIBmc *b = IPMI_BMC(dev); @@ -1763,6 +1945,8 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) ipmi_sdr_init(ibs); + ipmi_fru_init(&ibs->fru); + ibs->acpi_power_state[0] = 0; ibs->acpi_power_state[1] = 0; @@ -1780,6 +1964,13 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); } +static Property ipmi_sim_properties[] = { + DEFINE_PROP_UINT16("fruareasize", IPMIBmcSim, fru.areasize, 1024), + DEFINE_PROP_STRING("frudatafile", IPMIBmcSim, fru.filename), + DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename), + DEFINE_PROP_END_OF_LIST(), +}; + static void ipmi_sim_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -1787,6 +1978,7 @@ static void ipmi_sim_class_init(ObjectClass *oc, void *data) dc->hotpluggable = false; dc->realize = ipmi_sim_realize; + dc->props = ipmi_sim_properties; bk->handle_command = ipmi_sim_handle_command; } diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c index e30dbc7d3d..63a8ccd355 100644 --- a/hw/misc/exynos4210_pmu.c +++ b/hw/misc/exynos4210_pmu.c @@ -401,8 +401,8 @@ static uint64_t exynos4210_pmu_read(void *opaque, hwaddr offset, unsigned size) { Exynos4210PmuState *s = (Exynos4210PmuState *)opaque; - unsigned i; const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs; + unsigned int i; for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { if (reg_p->offset == offset) { @@ -420,8 +420,8 @@ static void exynos4210_pmu_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { Exynos4210PmuState *s = (Exynos4210PmuState *)opaque; - unsigned i; const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs; + unsigned int i; for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { if (reg_p->offset == offset) { diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 6a95d92d37..5ddaffe63a 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -26,6 +26,7 @@ common-obj-$(CONFIG_IMX_FEC) += imx_fec.o common-obj-$(CONFIG_CADENCE) += cadence_gem.o common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o common-obj-$(CONFIG_LANCE) += lance.o +common-obj-$(CONFIG_FTGMAC100) += ftgmac100.o obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o obj-$(CONFIG_COLDFIRE) += mcf_fec.o diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index d4de8ad9f1..3943187572 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -300,6 +300,8 @@ #define DESC_1_RX_SOF 0x00004000 #define DESC_1_RX_EOF 0x00008000 +#define GEM_MODID_VALUE 0x00020118 + static inline unsigned tx_desc_get_buffer(unsigned *desc) { return desc[0]; @@ -481,14 +483,17 @@ static int gem_can_receive(NetClientState *nc) } for (i = 0; i < s->num_priority_queues; i++) { - if (rx_desc_get_ownership(s->rx_desc[i]) == 1) { - if (s->can_rx_state != 2) { - s->can_rx_state = 2; - DB_PRINT("can't receive - busy buffer descriptor (q%d) 0x%x\n", - i, s->rx_desc_addr[i]); - } - return 0; + if (rx_desc_get_ownership(s->rx_desc[i]) != 1) { + break; + } + }; + + if (i == s->num_priority_queues) { + if (s->can_rx_state != 2) { + s->can_rx_state = 2; + DB_PRINT("can't receive - all the buffer descriptors are busy\n"); } + return 0; } if (s->can_rx_state != 0) { @@ -506,7 +511,18 @@ static void gem_update_int_status(CadenceGEMState *s) { int i; - if ((s->num_priority_queues == 1) && s->regs[GEM_ISR]) { + if (!s->regs[GEM_ISR]) { + /* ISR isn't set, clear all the interrupts */ + for (i = 0; i < s->num_priority_queues; ++i) { + qemu_set_irq(s->irq[i], 0); + } + return; + } + + /* If we get here we know s->regs[GEM_ISR] is set, so we don't need to + * check it again. + */ + if (s->num_priority_queues == 1) { /* No priority queues, just trigger the interrupt */ DB_PRINT("asserting int.\n"); qemu_set_irq(s->irq[0], 1); @@ -790,8 +806,8 @@ static void gem_get_rx_desc(CadenceGEMState *s, int q) { DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr[q]); /* read current descriptor */ - cpu_physical_memory_read(s->rx_desc_addr[0], - (uint8_t *)s->rx_desc[0], sizeof(s->rx_desc[0])); + cpu_physical_memory_read(s->rx_desc_addr[q], + (uint8_t *)s->rx_desc[q], sizeof(s->rx_desc[q])); /* Descriptor owned by software ? */ if (rx_desc_get_ownership(s->rx_desc[q]) == 1) { @@ -1209,7 +1225,7 @@ static void gem_reset(DeviceState *d) s->regs[GEM_TXPAUSE] = 0x0000ffff; s->regs[GEM_TXPARTIALSF] = 0x000003ff; s->regs[GEM_RXPARTIALSF] = 0x000003ff; - s->regs[GEM_MODID] = 0x00020118; + s->regs[GEM_MODID] = s->revision; s->regs[GEM_DESCONF] = 0x02500111; s->regs[GEM_DESCONF2] = 0x2ab13fff; s->regs[GEM_DESCONF5] = 0x002f2145; @@ -1271,7 +1287,6 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) { CadenceGEMState *s; uint32_t retval; - int i; s = (CadenceGEMState *)opaque; offset >>= 2; @@ -1282,9 +1297,7 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) switch (offset) { case GEM_ISR: DB_PRINT("lowering irqs on ISR read\n"); - for (i = 0; i < s->num_priority_queues; ++i) { - qemu_set_irq(s->irq[i], 0); - } + /* The interrupts get updated at the end of the function. */ break; case GEM_PHYMNTNC: if (retval & GEM_PHYMNTNC_OP_R) { @@ -1508,6 +1521,8 @@ static const VMStateDescription vmstate_cadence_gem = { static Property gem_properties[] = { DEFINE_NIC_PROPERTIES(CadenceGEMState, conf), + DEFINE_PROP_UINT32("revision", CadenceGEMState, revision, + GEM_MODID_VALUE), DEFINE_PROP_UINT8("num-priority-queues", CadenceGEMState, num_priority_queues, 1), DEFINE_PROP_UINT8("num-type1-screeners", CadenceGEMState, diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c new file mode 100644 index 0000000000..3c36ab9cec --- /dev/null +++ b/hw/net/ftgmac100.c @@ -0,0 +1,1016 @@ +/* + * Faraday FTGMAC100 Gigabit Ethernet + * + * Copyright (C) 2016-2017, IBM Corporation. + * + * Based on Coldfire Fast Ethernet Controller emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/net/ftgmac100.h" +#include "sysemu/dma.h" +#include "qemu/log.h" +#include "net/checksum.h" +#include "net/eth.h" +#include "hw/net/mii.h" + +/* For crc32 */ +#include <zlib.h> + +/* + * FTGMAC100 registers + */ +#define FTGMAC100_ISR 0x00 +#define FTGMAC100_IER 0x04 +#define FTGMAC100_MAC_MADR 0x08 +#define FTGMAC100_MAC_LADR 0x0c +#define FTGMAC100_MATH0 0x10 +#define FTGMAC100_MATH1 0x14 +#define FTGMAC100_NPTXPD 0x18 +#define FTGMAC100_RXPD 0x1C +#define FTGMAC100_NPTXR_BADR 0x20 +#define FTGMAC100_RXR_BADR 0x24 +#define FTGMAC100_HPTXPD 0x28 +#define FTGMAC100_HPTXR_BADR 0x2c +#define FTGMAC100_ITC 0x30 +#define FTGMAC100_APTC 0x34 +#define FTGMAC100_DBLAC 0x38 +#define FTGMAC100_REVR 0x40 +#define FTGMAC100_FEAR1 0x44 +#define FTGMAC100_RBSR 0x4c +#define FTGMAC100_TPAFCR 0x48 + +#define FTGMAC100_MACCR 0x50 +#define FTGMAC100_MACSR 0x54 +#define FTGMAC100_PHYCR 0x60 +#define FTGMAC100_PHYDATA 0x64 +#define FTGMAC100_FCR 0x68 + +/* + * Interrupt status register & interrupt enable register + */ +#define FTGMAC100_INT_RPKT_BUF (1 << 0) +#define FTGMAC100_INT_RPKT_FIFO (1 << 1) +#define FTGMAC100_INT_NO_RXBUF (1 << 2) +#define FTGMAC100_INT_RPKT_LOST (1 << 3) +#define FTGMAC100_INT_XPKT_ETH (1 << 4) +#define FTGMAC100_INT_XPKT_FIFO (1 << 5) +#define FTGMAC100_INT_NO_NPTXBUF (1 << 6) +#define FTGMAC100_INT_XPKT_LOST (1 << 7) +#define FTGMAC100_INT_AHB_ERR (1 << 8) +#define FTGMAC100_INT_PHYSTS_CHG (1 << 9) +#define FTGMAC100_INT_NO_HPTXBUF (1 << 10) + +/* + * Automatic polling timer control register + */ +#define FTGMAC100_APTC_RXPOLL_CNT(x) ((x) & 0xf) +#define FTGMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) +#define FTGMAC100_APTC_TXPOLL_CNT(x) (((x) >> 8) & 0xf) +#define FTGMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) + +/* + * PHY control register + */ +#define FTGMAC100_PHYCR_MIIRD (1 << 26) +#define FTGMAC100_PHYCR_MIIWR (1 << 27) + +#define FTGMAC100_PHYCR_DEV(x) (((x) >> 16) & 0x1f) +#define FTGMAC100_PHYCR_REG(x) (((x) >> 21) & 0x1f) + +/* + * PHY data register + */ +#define FTGMAC100_PHYDATA_MIIWDATA(x) ((x) & 0xffff) +#define FTGMAC100_PHYDATA_MIIRDATA(x) (((x) >> 16) & 0xffff) + +/* + * Feature Register + */ +#define FTGMAC100_REVR_NEW_MDIO_INTERFACE (1 << 31) + +/* + * MAC control register + */ +#define FTGMAC100_MACCR_TXDMA_EN (1 << 0) +#define FTGMAC100_MACCR_RXDMA_EN (1 << 1) +#define FTGMAC100_MACCR_TXMAC_EN (1 << 2) +#define FTGMAC100_MACCR_RXMAC_EN (1 << 3) +#define FTGMAC100_MACCR_RM_VLAN (1 << 4) +#define FTGMAC100_MACCR_HPTXR_EN (1 << 5) +#define FTGMAC100_MACCR_LOOP_EN (1 << 6) +#define FTGMAC100_MACCR_ENRX_IN_HALFTX (1 << 7) +#define FTGMAC100_MACCR_FULLDUP (1 << 8) +#define FTGMAC100_MACCR_GIGA_MODE (1 << 9) +#define FTGMAC100_MACCR_CRC_APD (1 << 10) /* not needed */ +#define FTGMAC100_MACCR_RX_RUNT (1 << 12) +#define FTGMAC100_MACCR_JUMBO_LF (1 << 13) +#define FTGMAC100_MACCR_RX_ALL (1 << 14) +#define FTGMAC100_MACCR_HT_MULTI_EN (1 << 15) +#define FTGMAC100_MACCR_RX_MULTIPKT (1 << 16) +#define FTGMAC100_MACCR_RX_BROADPKT (1 << 17) +#define FTGMAC100_MACCR_DISCARD_CRCERR (1 << 18) +#define FTGMAC100_MACCR_FAST_MODE (1 << 19) +#define FTGMAC100_MACCR_SW_RST (1 << 31) + +/* + * Transmit descriptor + */ +#define FTGMAC100_TXDES0_TXBUF_SIZE(x) ((x) & 0x3fff) +#define FTGMAC100_TXDES0_EDOTR (1 << 15) +#define FTGMAC100_TXDES0_CRC_ERR (1 << 19) +#define FTGMAC100_TXDES0_LTS (1 << 28) +#define FTGMAC100_TXDES0_FTS (1 << 29) +#define FTGMAC100_TXDES0_EDOTR_ASPEED (1 << 30) +#define FTGMAC100_TXDES0_TXDMA_OWN (1 << 31) + +#define FTGMAC100_TXDES1_VLANTAG_CI(x) ((x) & 0xffff) +#define FTGMAC100_TXDES1_INS_VLANTAG (1 << 16) +#define FTGMAC100_TXDES1_TCP_CHKSUM (1 << 17) +#define FTGMAC100_TXDES1_UDP_CHKSUM (1 << 18) +#define FTGMAC100_TXDES1_IP_CHKSUM (1 << 19) +#define FTGMAC100_TXDES1_LLC (1 << 22) +#define FTGMAC100_TXDES1_TX2FIC (1 << 30) +#define FTGMAC100_TXDES1_TXIC (1 << 31) + +/* + * Receive descriptor + */ +#define FTGMAC100_RXDES0_VDBC 0x3fff +#define FTGMAC100_RXDES0_EDORR (1 << 15) +#define FTGMAC100_RXDES0_MULTICAST (1 << 16) +#define FTGMAC100_RXDES0_BROADCAST (1 << 17) +#define FTGMAC100_RXDES0_RX_ERR (1 << 18) +#define FTGMAC100_RXDES0_CRC_ERR (1 << 19) +#define FTGMAC100_RXDES0_FTL (1 << 20) +#define FTGMAC100_RXDES0_RUNT (1 << 21) +#define FTGMAC100_RXDES0_RX_ODD_NB (1 << 22) +#define FTGMAC100_RXDES0_FIFO_FULL (1 << 23) +#define FTGMAC100_RXDES0_PAUSE_OPCODE (1 << 24) +#define FTGMAC100_RXDES0_PAUSE_FRAME (1 << 25) +#define FTGMAC100_RXDES0_LRS (1 << 28) +#define FTGMAC100_RXDES0_FRS (1 << 29) +#define FTGMAC100_RXDES0_EDORR_ASPEED (1 << 30) +#define FTGMAC100_RXDES0_RXPKT_RDY (1 << 31) + +#define FTGMAC100_RXDES1_VLANTAG_CI 0xffff +#define FTGMAC100_RXDES1_PROT_MASK (0x3 << 20) +#define FTGMAC100_RXDES1_PROT_NONIP (0x0 << 20) +#define FTGMAC100_RXDES1_PROT_IP (0x1 << 20) +#define FTGMAC100_RXDES1_PROT_TCPIP (0x2 << 20) +#define FTGMAC100_RXDES1_PROT_UDPIP (0x3 << 20) +#define FTGMAC100_RXDES1_LLC (1 << 22) +#define FTGMAC100_RXDES1_DF (1 << 23) +#define FTGMAC100_RXDES1_VLANTAG_AVAIL (1 << 24) +#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR (1 << 25) +#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) +#define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) + +/* + * Receive and transmit Buffer Descriptor + */ +typedef struct { + uint32_t des0; + uint32_t des1; + uint32_t des2; /* not used by HW */ + uint32_t des3; +} FTGMAC100Desc; + +/* + * Specific RTL8211E MII Registers + */ +#define RTL8211E_MII_PHYCR 16 /* PHY Specific Control */ +#define RTL8211E_MII_PHYSR 17 /* PHY Specific Status */ +#define RTL8211E_MII_INER 18 /* Interrupt Enable */ +#define RTL8211E_MII_INSR 19 /* Interrupt Status */ +#define RTL8211E_MII_RXERC 24 /* Receive Error Counter */ +#define RTL8211E_MII_LDPSR 27 /* Link Down Power Saving */ +#define RTL8211E_MII_EPAGSR 30 /* Extension Page Select */ +#define RTL8211E_MII_PAGSEL 31 /* Page Select */ + +/* + * RTL8211E Interrupt Status + */ +#define PHY_INT_AUTONEG_ERROR (1 << 15) +#define PHY_INT_PAGE_RECV (1 << 12) +#define PHY_INT_AUTONEG_COMPLETE (1 << 11) +#define PHY_INT_LINK_STATUS (1 << 10) +#define PHY_INT_ERROR (1 << 9) +#define PHY_INT_DOWN (1 << 8) +#define PHY_INT_JABBER (1 << 0) + +/* + * Max frame size for the receiving buffer + */ +#define FTGMAC100_MAX_FRAME_SIZE 10240 + +/* Limits depending on the type of the frame + * + * 9216 for Jumbo frames (+ 4 for VLAN) + * 1518 for other frames (+ 4 for VLAN) + */ +static int ftgmac100_max_frame_size(FTGMAC100State *s) +{ + return (s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518) + 4; +} + +static void ftgmac100_update_irq(FTGMAC100State *s) +{ + qemu_set_irq(s->irq, s->isr & s->ier); +} + +/* + * The MII phy could raise a GPIO to the processor which in turn + * could be handled as an interrpt by the OS. + * For now we don't handle any GPIO/interrupt line, so the OS will + * have to poll for the PHY status. + */ +static void phy_update_irq(FTGMAC100State *s) +{ + ftgmac100_update_irq(s); +} + +static void phy_update_link(FTGMAC100State *s) +{ + /* Autonegotiation status mirrors link status. */ + if (qemu_get_queue(s->nic)->link_down) { + s->phy_status &= ~(MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); + s->phy_int |= PHY_INT_DOWN; + } else { + s->phy_status |= (MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); + s->phy_int |= PHY_INT_AUTONEG_COMPLETE; + } + phy_update_irq(s); +} + +static void ftgmac100_set_link(NetClientState *nc) +{ + phy_update_link(FTGMAC100(qemu_get_nic_opaque(nc))); +} + +static void phy_reset(FTGMAC100State *s) +{ + s->phy_status = (MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD | + MII_BMSR_10T_HD | MII_BMSR_EXTSTAT | MII_BMSR_MFPS | + MII_BMSR_AN_COMP | MII_BMSR_AUTONEG | MII_BMSR_LINK_ST | + MII_BMSR_EXTCAP); + s->phy_control = (MII_BMCR_AUTOEN | MII_BMCR_FD | MII_BMCR_SPEED1000); + s->phy_advertise = (MII_ANAR_PAUSE_ASYM | MII_ANAR_PAUSE | MII_ANAR_TXFD | + MII_ANAR_TX | MII_ANAR_10FD | MII_ANAR_10 | + MII_ANAR_CSMACD); + s->phy_int_mask = 0; + s->phy_int = 0; +} + +static uint32_t do_phy_read(FTGMAC100State *s, int reg) +{ + uint32_t val; + + switch (reg) { + case MII_BMCR: /* Basic Control */ + val = s->phy_control; + break; + case MII_BMSR: /* Basic Status */ + val = s->phy_status; + break; + case MII_PHYID1: /* ID1 */ + val = RTL8211E_PHYID1; + break; + case MII_PHYID2: /* ID2 */ + val = RTL8211E_PHYID2; + break; + case MII_ANAR: /* Auto-neg advertisement */ + val = s->phy_advertise; + break; + case MII_ANLPAR: /* Auto-neg Link Partner Ability */ + val = (MII_ANLPAR_ACK | MII_ANLPAR_PAUSE | MII_ANLPAR_TXFD | + MII_ANLPAR_TX | MII_ANLPAR_10FD | MII_ANLPAR_10 | + MII_ANLPAR_CSMACD); + break; + case MII_ANER: /* Auto-neg Expansion */ + val = MII_ANER_NWAY; + break; + case MII_CTRL1000: /* 1000BASE-T control */ + val = (MII_CTRL1000_HALF | MII_CTRL1000_FULL); + break; + case MII_STAT1000: /* 1000BASE-T status */ + val = MII_STAT1000_FULL; + break; + case RTL8211E_MII_INSR: /* Interrupt status. */ + val = s->phy_int; + s->phy_int = 0; + phy_update_irq(s); + break; + case RTL8211E_MII_INER: /* Interrupt enable */ + val = s->phy_int_mask; + break; + case RTL8211E_MII_PHYCR: + case RTL8211E_MII_PHYSR: + case RTL8211E_MII_RXERC: + case RTL8211E_MII_LDPSR: + case RTL8211E_MII_EPAGSR: + case RTL8211E_MII_PAGSEL: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + val = 0; + break; + } + + return val; +} + +#define MII_BMCR_MASK (MII_BMCR_LOOPBACK | MII_BMCR_SPEED100 | \ + MII_BMCR_SPEED | MII_BMCR_AUTOEN | MII_BMCR_PDOWN | \ + MII_BMCR_FD | MII_BMCR_CTST) +#define MII_ANAR_MASK 0x2d7f + +static void do_phy_write(FTGMAC100State *s, int reg, uint32_t val) +{ + switch (reg) { + case MII_BMCR: /* Basic Control */ + if (val & MII_BMCR_RESET) { + phy_reset(s); + } else { + s->phy_control = val & MII_BMCR_MASK; + /* Complete autonegotiation immediately. */ + if (val & MII_BMCR_AUTOEN) { + s->phy_status |= MII_BMSR_AN_COMP; + } + } + break; + case MII_ANAR: /* Auto-neg advertisement */ + s->phy_advertise = (val & MII_ANAR_MASK) | MII_ANAR_TX; + break; + case RTL8211E_MII_INER: /* Interrupt enable */ + s->phy_int_mask = val & 0xff; + phy_update_irq(s); + break; + case RTL8211E_MII_PHYCR: + case RTL8211E_MII_PHYSR: + case RTL8211E_MII_RXERC: + case RTL8211E_MII_LDPSR: + case RTL8211E_MII_EPAGSR: + case RTL8211E_MII_PAGSEL: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + break; + } +} + +static int ftgmac100_read_bd(FTGMAC100Desc *bd, dma_addr_t addr) +{ + if (dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to read descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + bd->des0 = le32_to_cpu(bd->des0); + bd->des1 = le32_to_cpu(bd->des1); + bd->des2 = le32_to_cpu(bd->des2); + bd->des3 = le32_to_cpu(bd->des3); + return 0; +} + +static int ftgmac100_write_bd(FTGMAC100Desc *bd, dma_addr_t addr) +{ + FTGMAC100Desc lebd; + + lebd.des0 = cpu_to_le32(bd->des0); + lebd.des1 = cpu_to_le32(bd->des1); + lebd.des2 = cpu_to_le32(bd->des2); + lebd.des3 = cpu_to_le32(bd->des3); + if (dma_memory_write(&address_space_memory, addr, &lebd, sizeof(lebd))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to write descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + return 0; +} + +static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, + uint32_t tx_descriptor) +{ + int frame_size = 0; + uint8_t *ptr = s->frame; + uint32_t addr = tx_descriptor; + uint32_t flags = 0; + int max_frame_size = ftgmac100_max_frame_size(s); + + while (1) { + FTGMAC100Desc bd; + int len; + + if (ftgmac100_read_bd(&bd, addr) || + ((bd.des0 & FTGMAC100_TXDES0_TXDMA_OWN) == 0)) { + /* Run out of descriptors to transmit. */ + s->isr |= FTGMAC100_INT_NO_NPTXBUF; + break; + } + + /* record transmit flags as they are valid only on the first + * segment */ + if (bd.des0 & FTGMAC100_TXDES0_FTS) { + flags = bd.des1; + } + + len = bd.des0 & 0x3FFF; + if (frame_size + len > max_frame_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n", + __func__, len); + len = max_frame_size - frame_size; + } + + if (dma_memory_read(&address_space_memory, bd.des3, ptr, len)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to read packet @ 0x%x\n", + __func__, bd.des3); + s->isr |= FTGMAC100_INT_NO_NPTXBUF; + break; + } + + ptr += len; + frame_size += len; + if (bd.des0 & FTGMAC100_TXDES0_LTS) { + if (flags & FTGMAC100_TXDES1_IP_CHKSUM) { + net_checksum_calculate(s->frame, frame_size); + } + /* Last buffer in frame. */ + qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size); + ptr = s->frame; + frame_size = 0; + if (flags & FTGMAC100_TXDES1_TXIC) { + s->isr |= FTGMAC100_INT_XPKT_ETH; + } + } + + if (flags & FTGMAC100_TXDES1_TX2FIC) { + s->isr |= FTGMAC100_INT_XPKT_FIFO; + } + bd.des0 &= ~FTGMAC100_TXDES0_TXDMA_OWN; + + /* Write back the modified descriptor. */ + ftgmac100_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if (bd.des0 & s->txdes0_edotr) { + addr = tx_ring; + } else { + addr += sizeof(FTGMAC100Desc); + } + } + + s->tx_descriptor = addr; + + ftgmac100_update_irq(s); +} + +static int ftgmac100_can_receive(NetClientState *nc) +{ + FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); + FTGMAC100Desc bd; + + if ((s->maccr & (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) + != (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) { + return 0; + } + + if (ftgmac100_read_bd(&bd, s->rx_descriptor)) { + return 0; + } + return !(bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY); +} + +/* + * This is purely informative. The HW can poll the RW (and RX) ring + * buffers for available descriptors but we don't need to trigger a + * timer for that in qemu. + */ +static uint32_t ftgmac100_rxpoll(FTGMAC100State *s) +{ + /* Polling times : + * + * Speed TIME_SEL=0 TIME_SEL=1 + * + * 10 51.2 ms 819.2 ms + * 100 5.12 ms 81.92 ms + * 1000 1.024 ms 16.384 ms + */ + static const int div[] = { 20, 200, 1000 }; + + uint32_t cnt = 1024 * FTGMAC100_APTC_RXPOLL_CNT(s->aptcr); + uint32_t speed = (s->maccr & FTGMAC100_MACCR_FAST_MODE) ? 1 : 0; + uint32_t period; + + if (s->aptcr & FTGMAC100_APTC_RXPOLL_TIME_SEL) { + cnt <<= 4; + } + + if (s->maccr & FTGMAC100_MACCR_GIGA_MODE) { + speed = 2; + } + + period = cnt / div[speed]; + + return period; +} + +static void ftgmac100_reset(DeviceState *d) +{ + FTGMAC100State *s = FTGMAC100(d); + + /* Reset the FTGMAC100 */ + s->isr = 0; + s->ier = 0; + s->rx_enabled = 0; + s->rx_ring = 0; + s->rbsr = 0x640; + s->rx_descriptor = 0; + s->tx_ring = 0; + s->tx_descriptor = 0; + s->math[0] = 0; + s->math[1] = 0; + s->itc = 0; + s->aptcr = 1; + s->dblac = 0x00022f00; + s->revr = 0; + s->fear1 = 0; + s->tpafcr = 0xf1; + + s->maccr = 0; + s->phycr = 0; + s->phydata = 0; + s->fcr = 0x400; + + /* and the PHY */ + phy_reset(s); +} + +static uint64_t ftgmac100_read(void *opaque, hwaddr addr, unsigned size) +{ + FTGMAC100State *s = FTGMAC100(opaque); + + switch (addr & 0xff) { + case FTGMAC100_ISR: + return s->isr; + case FTGMAC100_IER: + return s->ier; + case FTGMAC100_MAC_MADR: + return (s->conf.macaddr.a[0] << 8) | s->conf.macaddr.a[1]; + case FTGMAC100_MAC_LADR: + return ((uint32_t) s->conf.macaddr.a[2] << 24) | + (s->conf.macaddr.a[3] << 16) | (s->conf.macaddr.a[4] << 8) | + s->conf.macaddr.a[5]; + case FTGMAC100_MATH0: + return s->math[0]; + case FTGMAC100_MATH1: + return s->math[1]; + case FTGMAC100_ITC: + return s->itc; + case FTGMAC100_DBLAC: + return s->dblac; + case FTGMAC100_REVR: + return s->revr; + case FTGMAC100_FEAR1: + return s->fear1; + case FTGMAC100_TPAFCR: + return s->tpafcr; + case FTGMAC100_FCR: + return s->fcr; + case FTGMAC100_MACCR: + return s->maccr; + case FTGMAC100_PHYCR: + return s->phycr; + case FTGMAC100_PHYDATA: + return s->phydata; + + /* We might want to support these one day */ + case FTGMAC100_HPTXPD: /* High Priority Transmit Poll Demand */ + case FTGMAC100_HPTXR_BADR: /* High Priority Transmit Ring Base Address */ + case FTGMAC100_MACSR: /* MAC Status Register (MACSR) */ + qemu_log_mask(LOG_UNIMP, "%s: read to unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + return 0; + } +} + +static void ftgmac100_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + FTGMAC100State *s = FTGMAC100(opaque); + int reg; + + switch (addr & 0xff) { + case FTGMAC100_ISR: /* Interrupt status */ + s->isr &= ~value; + break; + case FTGMAC100_IER: /* Interrupt control */ + s->ier = value; + break; + case FTGMAC100_MAC_MADR: /* MAC */ + s->conf.macaddr.a[0] = value >> 8; + s->conf.macaddr.a[1] = value; + break; + case FTGMAC100_MAC_LADR: + s->conf.macaddr.a[2] = value >> 24; + s->conf.macaddr.a[3] = value >> 16; + s->conf.macaddr.a[4] = value >> 8; + s->conf.macaddr.a[5] = value; + break; + case FTGMAC100_MATH0: /* Multicast Address Hash Table 0 */ + s->math[0] = value; + break; + case FTGMAC100_MATH1: /* Multicast Address Hash Table 1 */ + s->math[1] = value; + break; + case FTGMAC100_ITC: /* TODO: Interrupt Timer Control */ + s->itc = value; + break; + case FTGMAC100_RXR_BADR: /* Ring buffer address */ + s->rx_ring = value; + s->rx_descriptor = s->rx_ring; + break; + + case FTGMAC100_RBSR: /* DMA buffer size */ + s->rbsr = value; + break; + + case FTGMAC100_NPTXR_BADR: /* Transmit buffer address */ + s->tx_ring = value; + s->tx_descriptor = s->tx_ring; + break; + + case FTGMAC100_NPTXPD: /* Trigger transmit */ + if ((s->maccr & (FTGMAC100_MACCR_TXDMA_EN | FTGMAC100_MACCR_TXMAC_EN)) + == (FTGMAC100_MACCR_TXDMA_EN | FTGMAC100_MACCR_TXMAC_EN)) { + /* TODO: high priority tx ring */ + ftgmac100_do_tx(s, s->tx_ring, s->tx_descriptor); + } + if (ftgmac100_can_receive(qemu_get_queue(s->nic))) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + break; + + case FTGMAC100_RXPD: /* Receive Poll Demand Register */ + if (ftgmac100_can_receive(qemu_get_queue(s->nic))) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + break; + + case FTGMAC100_APTC: /* Automatic polling */ + s->aptcr = value; + + if (FTGMAC100_APTC_RXPOLL_CNT(s->aptcr)) { + ftgmac100_rxpoll(s); + } + + if (FTGMAC100_APTC_TXPOLL_CNT(s->aptcr)) { + qemu_log_mask(LOG_UNIMP, "%s: no transmit polling\n", __func__); + } + break; + + case FTGMAC100_MACCR: /* MAC Device control */ + s->maccr = value; + if (value & FTGMAC100_MACCR_SW_RST) { + ftgmac100_reset(DEVICE(s)); + } + + if (ftgmac100_can_receive(qemu_get_queue(s->nic))) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + break; + + case FTGMAC100_PHYCR: /* PHY Device control */ + reg = FTGMAC100_PHYCR_REG(value); + s->phycr = value; + if (value & FTGMAC100_PHYCR_MIIWR) { + do_phy_write(s, reg, s->phydata & 0xffff); + s->phycr &= ~FTGMAC100_PHYCR_MIIWR; + } else { + s->phydata = do_phy_read(s, reg) << 16; + s->phycr &= ~FTGMAC100_PHYCR_MIIRD; + } + break; + case FTGMAC100_PHYDATA: + s->phydata = value & 0xffff; + break; + case FTGMAC100_DBLAC: /* DMA Burst Length and Arbitration Control */ + s->dblac = value; + break; + case FTGMAC100_REVR: /* Feature Register */ + /* TODO: Only Old MDIO interface is supported */ + s->revr = value & ~FTGMAC100_REVR_NEW_MDIO_INTERFACE; + break; + case FTGMAC100_FEAR1: /* Feature Register 1 */ + s->fear1 = value; + break; + case FTGMAC100_TPAFCR: /* Transmit Priority Arbitration and FIFO Control */ + s->tpafcr = value; + break; + case FTGMAC100_FCR: /* Flow Control */ + s->fcr = value; + break; + + case FTGMAC100_HPTXPD: /* High Priority Transmit Poll Demand */ + case FTGMAC100_HPTXR_BADR: /* High Priority Transmit Ring Base Address */ + case FTGMAC100_MACSR: /* MAC Status Register (MACSR) */ + qemu_log_mask(LOG_UNIMP, "%s: write to unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } + + ftgmac100_update_irq(s); +} + +static int ftgmac100_filter(FTGMAC100State *s, const uint8_t *buf, size_t len) +{ + unsigned mcast_idx; + + if (s->maccr & FTGMAC100_MACCR_RX_ALL) { + return 1; + } + + switch (get_eth_packet_type(PKT_GET_ETH_HDR(buf))) { + case ETH_PKT_BCAST: + if (!(s->maccr & FTGMAC100_MACCR_RX_BROADPKT)) { + return 0; + } + break; + case ETH_PKT_MCAST: + if (!(s->maccr & FTGMAC100_MACCR_RX_MULTIPKT)) { + if (!(s->maccr & FTGMAC100_MACCR_HT_MULTI_EN)) { + return 0; + } + + /* TODO: this does not seem to work for ftgmac100 */ + mcast_idx = compute_mcast_idx(buf); + if (!(s->math[mcast_idx / 32] & (1 << (mcast_idx % 32)))) { + return 0; + } + } + break; + case ETH_PKT_UCAST: + if (memcmp(s->conf.macaddr.a, buf, 6)) { + return 0; + } + break; + } + + return 1; +} + +static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, + size_t len) +{ + FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); + FTGMAC100Desc bd; + uint32_t flags = 0; + uint32_t addr; + uint32_t crc; + uint32_t buf_addr; + uint8_t *crc_ptr; + uint32_t buf_len; + size_t size = len; + uint32_t first = FTGMAC100_RXDES0_FRS; + int max_frame_size = ftgmac100_max_frame_size(s); + + if ((s->maccr & (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) + != (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) { + return -1; + } + + /* TODO : Pad to minimum Ethernet frame length */ + /* handle small packets. */ + if (size < 10) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: dropped frame of %zd bytes\n", + __func__, size); + return size; + } + + if (size < 64 && !(s->maccr & FTGMAC100_MACCR_RX_RUNT)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: dropped runt frame of %zd bytes\n", + __func__, size); + return size; + } + + if (!ftgmac100_filter(s, buf, size)) { + return size; + } + + /* 4 bytes for the CRC. */ + size += 4; + crc = cpu_to_be32(crc32(~0, buf, size)); + crc_ptr = (uint8_t *) &crc; + + /* Huge frames are truncated. */ + if (size > max_frame_size) { + size = max_frame_size; + qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %zd bytes\n", + __func__, size); + flags |= FTGMAC100_RXDES0_FTL; + } + + switch (get_eth_packet_type(PKT_GET_ETH_HDR(buf))) { + case ETH_PKT_BCAST: + flags |= FTGMAC100_RXDES0_BROADCAST; + break; + case ETH_PKT_MCAST: + flags |= FTGMAC100_RXDES0_MULTICAST; + break; + case ETH_PKT_UCAST: + break; + } + + addr = s->rx_descriptor; + while (size > 0) { + if (!ftgmac100_can_receive(nc)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unexpected packet\n", __func__); + return -1; + } + + if (ftgmac100_read_bd(&bd, addr) || + (bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY)) { + /* No descriptors available. Bail out. */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Lost end of frame\n", + __func__); + s->isr |= FTGMAC100_INT_NO_RXBUF; + break; + } + buf_len = (size <= s->rbsr) ? size : s->rbsr; + bd.des0 |= buf_len & 0x3fff; + size -= buf_len; + + /* The last 4 bytes are the CRC. */ + if (size < 4) { + buf_len += size - 4; + } + buf_addr = bd.des3; + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + buf += buf_len; + if (size < 4) { + dma_memory_write(&address_space_memory, buf_addr + buf_len, + crc_ptr, 4 - size); + crc_ptr += 4 - size; + } + + bd.des0 |= first | FTGMAC100_RXDES0_RXPKT_RDY; + first = 0; + if (size == 0) { + /* Last buffer in frame. */ + bd.des0 |= flags | FTGMAC100_RXDES0_LRS; + s->isr |= FTGMAC100_INT_RPKT_BUF; + } else { + s->isr |= FTGMAC100_INT_RPKT_FIFO; + } + ftgmac100_write_bd(&bd, addr); + if (bd.des0 & s->rxdes0_edorr) { + addr = s->rx_ring; + } else { + addr += sizeof(FTGMAC100Desc); + } + } + s->rx_descriptor = addr; + + ftgmac100_update_irq(s); + return len; +} + +static const MemoryRegionOps ftgmac100_ops = { + .read = ftgmac100_read, + .write = ftgmac100_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ftgmac100_cleanup(NetClientState *nc) +{ + FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); + + s->nic = NULL; +} + +static NetClientInfo net_ftgmac100_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = ftgmac100_can_receive, + .receive = ftgmac100_receive, + .cleanup = ftgmac100_cleanup, + .link_status_changed = ftgmac100_set_link, +}; + +static void ftgmac100_realize(DeviceState *dev, Error **errp) +{ + FTGMAC100State *s = FTGMAC100(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + if (s->aspeed) { + s->txdes0_edotr = FTGMAC100_TXDES0_EDOTR_ASPEED; + s->rxdes0_edorr = FTGMAC100_RXDES0_EDORR_ASPEED; + } else { + s->txdes0_edotr = FTGMAC100_TXDES0_EDOTR; + s->rxdes0_edorr = FTGMAC100_RXDES0_EDORR; + } + + memory_region_init_io(&s->iomem, OBJECT(dev), &ftgmac100_ops, s, + TYPE_FTGMAC100, 0x2000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + s->conf.peers.ncs[0] = nd_table[0].netdev; + + s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf, + object_get_typename(OBJECT(dev)), DEVICE(dev)->id, + s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); + + s->frame = g_malloc(FTGMAC100_MAX_FRAME_SIZE); +} + +static const VMStateDescription vmstate_ftgmac100 = { + .name = TYPE_FTGMAC100, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irq_state, FTGMAC100State), + VMSTATE_UINT32(isr, FTGMAC100State), + VMSTATE_UINT32(ier, FTGMAC100State), + VMSTATE_UINT32(rx_enabled, FTGMAC100State), + VMSTATE_UINT32(rx_ring, FTGMAC100State), + VMSTATE_UINT32(rbsr, FTGMAC100State), + VMSTATE_UINT32(tx_ring, FTGMAC100State), + VMSTATE_UINT32(rx_descriptor, FTGMAC100State), + VMSTATE_UINT32(tx_descriptor, FTGMAC100State), + VMSTATE_UINT32_ARRAY(math, FTGMAC100State, 2), + VMSTATE_UINT32(itc, FTGMAC100State), + VMSTATE_UINT32(aptcr, FTGMAC100State), + VMSTATE_UINT32(dblac, FTGMAC100State), + VMSTATE_UINT32(revr, FTGMAC100State), + VMSTATE_UINT32(fear1, FTGMAC100State), + VMSTATE_UINT32(tpafcr, FTGMAC100State), + VMSTATE_UINT32(maccr, FTGMAC100State), + VMSTATE_UINT32(phycr, FTGMAC100State), + VMSTATE_UINT32(phydata, FTGMAC100State), + VMSTATE_UINT32(fcr, FTGMAC100State), + VMSTATE_UINT32(phy_status, FTGMAC100State), + VMSTATE_UINT32(phy_control, FTGMAC100State), + VMSTATE_UINT32(phy_advertise, FTGMAC100State), + VMSTATE_UINT32(phy_int, FTGMAC100State), + VMSTATE_UINT32(phy_int_mask, FTGMAC100State), + VMSTATE_UINT32(txdes0_edotr, FTGMAC100State), + VMSTATE_UINT32(rxdes0_edorr, FTGMAC100State), + VMSTATE_END_OF_LIST() + } +}; + +static Property ftgmac100_properties[] = { + DEFINE_PROP_BOOL("aspeed", FTGMAC100State, aspeed, false), + DEFINE_NIC_PROPERTIES(FTGMAC100State, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ftgmac100_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_ftgmac100; + dc->reset = ftgmac100_reset; + dc->props = ftgmac100_properties; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->realize = ftgmac100_realize; + dc->desc = "Faraday FTGMAC100 Gigabit Ethernet emulation"; +} + +static const TypeInfo ftgmac100_info = { + .name = TYPE_FTGMAC100, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FTGMAC100State), + .class_init = ftgmac100_class_init, +}; + +static void ftgmac100_register_types(void) +{ + type_register_static(&ftgmac100_info); +} + +type_init(ftgmac100_register_types) diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 467cbb9cb8..27fde46126 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -380,20 +380,8 @@ static void pci_vpb_reset(DeviceState *d) static void pci_vpb_init(Object *obj) { - PCIHostState *h = PCI_HOST_BRIDGE(obj); PCIVPBState *s = PCI_VPB(obj); - memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 1ULL << 32); - memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 1ULL << 32); - - pci_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), "pci", - &s->pci_mem_space, &s->pci_io_space, - PCI_DEVFN(11, 0), TYPE_PCI_BUS); - h->bus = &s->pci_bus; - - object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST); - qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus)); - /* Window sizes for VersatilePB; realview_pci's init will override */ s->mem_win_size[0] = 0x0c000000; s->mem_win_size[1] = 0x10000000; @@ -403,10 +391,22 @@ static void pci_vpb_init(Object *obj) static void pci_vpb_realize(DeviceState *dev, Error **errp) { PCIVPBState *s = PCI_VPB(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); pci_map_irq_fn mapfn; int i; + memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 1ULL << 32); + memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 1ULL << 32); + + pci_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), dev, "pci", + &s->pci_mem_space, &s->pci_io_space, + PCI_DEVFN(11, 0), TYPE_PCI_BUS); + h->bus = &s->pci_bus; + + object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST); + qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus)); + for (i = 0; i < 4; i++) { sysbus_init_irq(sbd, &s->irq[i]); } @@ -503,8 +503,6 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data) dc->reset = pci_vpb_reset; dc->vmsd = &pci_vpb_vmstate; dc->props = pci_vpb_properties; - /* Reason: object_unref() hangs */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo pci_vpb_info = { @@ -526,19 +524,10 @@ static void pci_realview_init(Object *obj) s->mem_win_size[2] = 0x08000000; } -static void pci_realview_class_init(ObjectClass *class, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(class); - - /* Reason: object_unref() hangs */ - dc->cannot_destroy_with_object_finalize_yet = true; -} - static const TypeInfo pci_realview_info = { .name = "realview_pci", .parent = TYPE_VERSATILE_PCI, .instance_init = pci_realview_init, - .class_init = pci_realview_class_init, }; static void versatile_pci_register_types(void) diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 001293423c..7efc686748 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o # IBM PowerNV -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 3fa722af82..d4bcdb027f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -33,7 +33,11 @@ #include "exec/address-spaces.h" #include "qemu/cutils.h" #include "qapi/visitor.h" +#include "monitor/monitor.h" +#include "hw/intc/intc.h" +#include "hw/ipmi/ipmi.h" +#include "hw/ppc/xics.h" #include "hw/ppc/pnv_xscom.h" #include "hw/isa/isa.h" @@ -215,6 +219,55 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) servers_prop, sizeof(servers_prop)))); } +static void powernv_populate_icp(PnvChip *chip, void *fdt, uint32_t pir, + uint32_t nr_threads) +{ + uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12); + char *name; + const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; + uint32_t irange[2], i, rsize; + uint64_t *reg; + int offset; + + irange[0] = cpu_to_be32(pir); + irange[1] = cpu_to_be32(nr_threads); + + rsize = sizeof(uint64_t) * 2 * nr_threads; + reg = g_malloc(rsize); + for (i = 0; i < nr_threads; i++) { + reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000)); + reg[i * 2 + 1] = cpu_to_be64(0x1000); + } + + name = g_strdup_printf("interrupt-controller@%"PRIX64, addr); + offset = fdt_add_subnode(fdt, 0, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); + _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize))); + _FDT((fdt_setprop_string(fdt, offset, "device_type", + "PowerPC-External-Interrupt-Presentation"))); + _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0))); + _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges", + irange, sizeof(irange)))); + _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1))); + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0))); + g_free(reg); +} + +static int pnv_chip_lpc_offset(PnvChip *chip, void *fdt) +{ + char *name; + int offset; + + name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x", + (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE); + offset = fdt_path_offset(fdt, name); + g_free(name); + return offset; +} + static void powernv_populate_chip(PnvChip *chip, void *fdt) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); @@ -224,10 +277,24 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt) pnv_xscom_populate(chip, fdt, 0); + /* The default LPC bus of a multichip system is on chip 0. It's + * recognized by the firmware (skiboot) using a "primary" + * property. + */ + if (chip->chip_id == 0x0) { + int lpc_offset = pnv_chip_lpc_offset(chip, fdt); + + _FDT((fdt_setprop(fdt, lpc_offset, "primary", NULL, 0))); + } + for (i = 0; i < chip->nr_cores; i++) { PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); powernv_create_core_node(chip, pnv_core, fdt); + + /* Interrupt Control Presenters (ICP). One per core. */ + powernv_populate_icp(chip, fdt, pnv_core->pir, + CPU_CORE(pnv_core)->nr_threads); } if (chip->ram_size) { @@ -237,6 +304,127 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt) g_free(typename); } +static void powernv_populate_rtc(ISADevice *d, void *fdt, int lpc_off) +{ + uint32_t io_base = d->ioport_id; + uint32_t io_regs[] = { + cpu_to_be32(1), + cpu_to_be32(io_base), + cpu_to_be32(2) + }; + char *name; + int node; + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + _FDT(node); + g_free(name); + + _FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)))); + _FDT((fdt_setprop_string(fdt, node, "compatible", "pnpPNP,b00"))); +} + +static void powernv_populate_serial(ISADevice *d, void *fdt, int lpc_off) +{ + const char compatible[] = "ns16550\0pnpPNP,501"; + uint32_t io_base = d->ioport_id; + uint32_t io_regs[] = { + cpu_to_be32(1), + cpu_to_be32(io_base), + cpu_to_be32(8) + }; + char *name; + int node; + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + _FDT(node); + g_free(name); + + _FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)))); + _FDT((fdt_setprop(fdt, node, "compatible", compatible, + sizeof(compatible)))); + + _FDT((fdt_setprop_cell(fdt, node, "clock-frequency", 1843200))); + _FDT((fdt_setprop_cell(fdt, node, "current-speed", 115200))); + _FDT((fdt_setprop_cell(fdt, node, "interrupts", d->isairq[0]))); + _FDT((fdt_setprop_cell(fdt, node, "interrupt-parent", + fdt_get_phandle(fdt, lpc_off)))); + + /* This is needed by Linux */ + _FDT((fdt_setprop_string(fdt, node, "device_type", "serial"))); +} + +static void powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off) +{ + const char compatible[] = "bt\0ipmi-bt"; + uint32_t io_base; + uint32_t io_regs[] = { + cpu_to_be32(1), + 0, /* 'io_base' retrieved from the 'ioport' property of 'isa-ipmi-bt' */ + cpu_to_be32(3) + }; + uint32_t irq; + char *name; + int node; + + io_base = object_property_get_int(OBJECT(d), "ioport", &error_fatal); + io_regs[1] = cpu_to_be32(io_base); + + irq = object_property_get_int(OBJECT(d), "irq", &error_fatal); + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + _FDT(node); + g_free(name); + + fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); + fdt_setprop(fdt, node, "compatible", compatible, sizeof(compatible)); + + /* Mark it as reserved to avoid Linux trying to claim it */ + _FDT((fdt_setprop_string(fdt, node, "status", "reserved"))); + _FDT((fdt_setprop_cell(fdt, node, "interrupts", irq))); + _FDT((fdt_setprop_cell(fdt, node, "interrupt-parent", + fdt_get_phandle(fdt, lpc_off)))); +} + +typedef struct ForeachPopulateArgs { + void *fdt; + int offset; +} ForeachPopulateArgs; + +static int powernv_populate_isa_device(DeviceState *dev, void *opaque) +{ + ForeachPopulateArgs *args = opaque; + ISADevice *d = ISA_DEVICE(dev); + + if (object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC)) { + powernv_populate_rtc(d, args->fdt, args->offset); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_ISA_SERIAL)) { + powernv_populate_serial(d, args->fdt, args->offset); + } else if (object_dynamic_cast(OBJECT(dev), "isa-ipmi-bt")) { + powernv_populate_ipmi_bt(d, args->fdt, args->offset); + } else { + error_report("unknown isa device %s@i%x", qdev_fw_name(dev), + d->ioport_id); + } + + return 0; +} + +static void powernv_populate_isa(ISABus *bus, void *fdt, int lpc_offset) +{ + ForeachPopulateArgs args = { + .fdt = fdt, + .offset = lpc_offset, + }; + + /* ISA devices are not necessarily parented to the ISA bus so we + * can not use object_child_foreach() */ + qbus_walk_children(BUS(bus), powernv_populate_isa_device, + NULL, NULL, NULL, &args); +} + static void *powernv_create_fdt(MachineState *machine) { const char plat_compat[] = "qemu,powernv\0ibm,powernv"; @@ -245,6 +433,7 @@ static void *powernv_create_fdt(MachineState *machine) char *buf; int off; int i; + int lpc_offset; fdt = g_malloc0(FDT_MAX_SIZE); _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); @@ -284,16 +473,49 @@ static void *powernv_create_fdt(MachineState *machine) for (i = 0; i < pnv->num_chips; i++) { powernv_populate_chip(pnv->chips[i], fdt); } + + /* Populate ISA devices on chip 0 */ + lpc_offset = pnv_chip_lpc_offset(pnv->chips[0], fdt); + powernv_populate_isa(pnv->isa_bus, fdt, lpc_offset); + + if (pnv->bmc) { + pnv_bmc_populate_sensors(pnv->bmc, fdt); + } + return fdt; } +static void pnv_powerdown_notify(Notifier *n, void *opaque) +{ + PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); + + if (pnv->bmc) { + pnv_bmc_powerdown(pnv->bmc); + } +} + static void ppc_powernv_reset(void) { MachineState *machine = MACHINE(qdev_get_machine()); + PnvMachineState *pnv = POWERNV_MACHINE(machine); void *fdt; + Object *obj; qemu_devices_reset(); + /* OpenPOWER systems have a BMC, which can be defined on the + * command line with: + * + * -device ipmi-bmc-sim,id=bmc0 + * + * This is the internal simulator but it could also be an external + * BMC. + */ + obj = object_resolve_path_type("", TYPE_IPMI_BMC, NULL); + if (obj) { + pnv->bmc = IPMI_BMC(obj); + } + fdt = powernv_create_fdt(machine); /* Pack resulting tree */ @@ -302,29 +524,6 @@ static void ppc_powernv_reset(void) cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); } -/* If we don't use the built-in LPC interrupt deserializer, we need - * to provide a set of qirqs for the ISA bus or things will go bad. - * - * Most machines using pre-Naples chips (without said deserializer) - * have a CPLD that will collect the SerIRQ and shoot them as a - * single level interrupt to the P8 chip. So let's setup a hook - * for doing just that. - * - * Note: The actual interrupt input isn't emulated yet, this will - * come with the PSI bridge model. - */ -static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) -{ - /* We don't yet emulate the PSI bridge which provides the external - * interrupt, so just drop interrupts on the floor - */ -} - -static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) -{ - /* XXX TODO */ -} - static ISABus *pnv_isa_create(PnvChip *chip) { PnvLpcController *lpc = &chip->lpc; @@ -339,16 +538,7 @@ static ISABus *pnv_isa_create(PnvChip *chip) isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &error_fatal); - /* Not all variants have a working serial irq decoder. If not, - * handling of LPC interrupts becomes a platform issue (some - * platforms have a CPLD to do it). - */ - if (pcc->chip_type == PNV_CHIP_POWER8NVL) { - irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS); - } else { - irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip, - ISA_NUM_IRQS); - } + irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS); isa_bus_irqs(isa_bus, irqs); return isa_bus; @@ -457,6 +647,11 @@ static void ppc_powernv_init(MachineState *machine) /* Create an RTC ISA device too */ rtc_init(pnv->isa_bus, 2000, NULL); + + /* OpenPOWER systems use a IPMI SEL Event message to notify the + * host to powerdown */ + pnv->powerdown_notifier.notify = pnv_powerdown_notify; + qemu_register_powerdown_notifier(&pnv->powerdown_notifier); } /* @@ -638,6 +833,52 @@ static void pnv_chip_init(Object *obj) object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC); object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); + + object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI); + object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL); + object_property_add_const_link(OBJECT(&chip->psi), "xics", + OBJECT(qdev_get_machine()), &error_abort); + + object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC); + object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL); + object_property_add_const_link(OBJECT(&chip->occ), "psi", + OBJECT(&chip->psi), &error_abort); + + /* The LPC controller needs PSI to generate interrupts */ + object_property_add_const_link(OBJECT(&chip->lpc), "psi", + OBJECT(&chip->psi), &error_abort); +} + +static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + char *typename = pnv_core_typename(pcc->cpu_model); + size_t typesize = object_type_get_instance_size(typename); + int i, j; + char *name; + XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); + + name = g_strdup_printf("icp-%x", chip->chip_id); + memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio); + g_free(name); + + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip)); + + /* Map the ICP registers for each thread */ + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + int core_hwid = CPU_CORE(pnv_core)->core_id; + + for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { + uint32_t pir = pcc->core_pir(chip, core_hwid) + j; + PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir)); + + memory_region_add_subregion(&chip->icp_mmio, pir << 12, &icp->mmio); + } + } + + g_free(typename); } static void pnv_chip_realize(DeviceState *dev, Error **errp) @@ -691,6 +932,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(pnv_core), pcc->core_pir(chip, core_hwid), "pir", &error_fatal); + object_property_add_const_link(OBJECT(pnv_core), "xics", + qdev_get_machine(), &error_fatal); object_property_set_bool(OBJECT(pnv_core), true, "realized", &error_fatal); object_unref(OBJECT(pnv_core)); @@ -708,6 +951,32 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&chip->lpc), true, "realized", &error_fatal); pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs); + + /* Interrupt Management Area. This is the memory region holding + * all the Interrupt Control Presenter (ICP) registers */ + pnv_chip_icp_realize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip->psi), PNV_PSIHB_BASE(chip), + "bar", &error_fatal); + object_property_set_bool(OBJECT(&chip->psi), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs); + + /* Create the simplified OCC model */ + object_property_set_bool(OBJECT(&chip->occ), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs); } static Property pnv_chip_properties[] = { @@ -723,6 +992,7 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = pnv_chip_realize; dc->props = pnv_chip_properties; dc->desc = "PowerNV Chip"; @@ -737,6 +1007,70 @@ static const TypeInfo pnv_chip_info = { .abstract = true, }; +static ICSState *pnv_ics_get(XICSFabric *xi, int irq) +{ + PnvMachineState *pnv = POWERNV_MACHINE(xi); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + if (ics_valid_irq(&pnv->chips[i]->psi.ics, irq)) { + return &pnv->chips[i]->psi.ics; + } + } + return NULL; +} + +static void pnv_ics_resend(XICSFabric *xi) +{ + PnvMachineState *pnv = POWERNV_MACHINE(xi); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + ics_resend(&pnv->chips[i]->psi.ics); + } +} + +static PowerPCCPU *ppc_get_vcpu_by_pir(int pir) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (env->spr_cb[SPR_PIR].default_value == pir) { + return cpu; + } + } + + return NULL; +} + +static ICPState *pnv_icp_get(XICSFabric *xi, int pir) +{ + PowerPCCPU *cpu = ppc_get_vcpu_by_pir(pir); + + return cpu ? ICP(cpu->intc) : NULL; +} + +static void pnv_pic_print_info(InterruptStatsProvider *obj, + Monitor *mon) +{ + PnvMachineState *pnv = POWERNV_MACHINE(obj); + int i; + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + icp_pic_print_info(ICP(cpu->intc), mon); + } + + for (i = 0; i < pnv->num_chips; i++) { + ics_pic_print_info(&pnv->chips[i]->psi.ics, mon); + } +} + static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -787,6 +1121,8 @@ static void powernv_machine_class_props_init(ObjectClass *oc) static void powernv_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); + InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); mc->desc = "IBM PowerNV (Non-Virtualized)"; mc->init = ppc_powernv_init; @@ -797,6 +1133,10 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data) mc->no_parallel = 1; mc->default_boot_order = NULL; mc->default_ram_size = 1 * G_BYTE; + xic->icp_get = pnv_icp_get; + xic->ics_get = pnv_ics_get; + xic->ics_resend = pnv_ics_resend; + ispc->print_info = pnv_pic_print_info; powernv_machine_class_props_init(oc); } @@ -807,6 +1147,11 @@ static const TypeInfo powernv_machine_info = { .instance_size = sizeof(PnvMachineState), .instance_init = powernv_machine_initfn, .class_init = powernv_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XICS_FABRIC }, + { TYPE_INTERRUPT_STATS_PROVIDER }, + { }, + }, }; static void powernv_machine_register_types(void) diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c new file mode 100644 index 0000000000..7b60b4c360 --- /dev/null +++ b/hw/ppc/pnv_bmc.c @@ -0,0 +1,122 @@ +/* + * QEMU PowerNV, BMC related functions + * + * Copyright (c) 2016-2017, IBM Corporation. + * + * 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/>. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "target/ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/ipmi/ipmi.h" +#include "hw/ppc/fdt.h" + +#include "hw/ppc/pnv.h" + +#include <libfdt.h> + +/* TODO: include definition in ipmi.h */ +#define IPMI_SDR_FULL_TYPE 1 + +/* + * OEM SEL Event data packet sent by BMC in response of a Read Event + * Message Buffer command + */ +typedef struct OemSel { + /* SEL header */ + uint8_t id[2]; + uint8_t type; + uint8_t timestamp[4]; + uint8_t manuf_id[3]; + + /* OEM SEL data (6 bytes) follows */ + uint8_t netfun; + uint8_t cmd; + uint8_t data[4]; +} OemSel; + +#define SOFT_OFF 0x00 +#define SOFT_REBOOT 0x01 + +static void pnv_gen_oem_sel(IPMIBmc *bmc, uint8_t reboot) +{ + /* IPMI SEL Event are 16 bytes long */ + OemSel sel = { + .id = { 0x55 , 0x55 }, + .type = 0xC0, /* OEM */ + .manuf_id = { 0x0, 0x0, 0x0 }, + .timestamp = { 0x0, 0x0, 0x0, 0x0 }, + .netfun = 0x3A, /* IBM */ + .cmd = 0x04, /* AMI OEM SEL Power Notification */ + .data = { reboot, 0xFF, 0xFF, 0xFF }, + }; + + ipmi_bmc_gen_event(bmc, (uint8_t *) &sel, 0 /* do not log the event */); +} + +void pnv_bmc_powerdown(IPMIBmc *bmc) +{ + pnv_gen_oem_sel(bmc, SOFT_OFF); +} + +void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt) +{ + int offset; + int i; + const struct ipmi_sdr_compact *sdr; + uint16_t nextrec; + + offset = fdt_add_subnode(fdt, 0, "/bmc"); + _FDT(offset); + + _FDT((fdt_setprop_string(fdt, offset, "name", "bmc"))); + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); + + offset = fdt_add_subnode(fdt, offset, "sensors"); + _FDT(offset); + + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); + + for (i = 0; !ipmi_bmc_sdr_find(bmc, i, &sdr, &nextrec); i++) { + int off; + char *name; + + if (sdr->header.rec_type != IPMI_SDR_COMPACT_TYPE && + sdr->header.rec_type != IPMI_SDR_FULL_TYPE) { + continue; + } + + name = g_strdup_printf("sensor@%x", sdr->sensor_owner_number); + off = fdt_add_subnode(fdt, offset, name); + _FDT(off); + g_free(name); + + _FDT((fdt_setprop_cell(fdt, off, "reg", sdr->sensor_owner_number))); + _FDT((fdt_setprop_string(fdt, off, "name", "sensor"))); + _FDT((fdt_setprop_string(fdt, off, "compatible", "ibm,ipmi-sensor"))); + _FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-reading-type", + sdr->reading_type))); + _FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-id", + sdr->entity_id))); + _FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-instance", + sdr->entity_instance))); + _FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-type", + sdr->sensor_type))); + } +} diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index d79d530b48..1b7ec70f03 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -25,6 +25,7 @@ #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/xics.h" static void powernv_cpu_reset(void *opaque) { @@ -110,23 +111,37 @@ static const MemoryRegionOps pnv_core_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_core_realize_child(Object *child, Error **errp) +static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp) { Error *local_err = NULL; CPUState *cs = CPU(child); PowerPCCPU *cpu = POWERPC_CPU(cs); + Object *obj; + + obj = object_new(TYPE_PNV_ICP); + object_property_add_child(OBJECT(cpu), "icp", obj, NULL); + object_property_add_const_link(obj, "xics", OBJECT(xi), &error_abort); + object_property_set_bool(obj, true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } object_property_set_bool(child, true, "realized", &local_err); if (local_err) { + object_unparent(obj); error_propagate(errp, local_err); return; } powernv_cpu_init(cpu, &local_err); if (local_err) { + object_unparent(obj); error_propagate(errp, local_err); return; } + + xics_cpu_setup(xi, cpu, ICP(obj)); } static void pnv_core_realize(DeviceState *dev, Error **errp) @@ -140,6 +155,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) void *obj; int i, j; char name[32]; + Object *xi; + + xi = object_property_get_link(OBJECT(dev), "xics", &local_err); + if (!xi) { + error_setg(errp, "%s: required link 'xics' not found: %s", + __func__, error_get_pretty(local_err)); + return; + } pc->threads = g_malloc0(size * cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { @@ -160,7 +183,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) for (j = 0; j < cc->nr_threads; j++) { obj = pc->threads + j * size; - pnv_core_realize_child(obj, &local_err); + pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err); if (local_err) { goto err; } diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 78db52415b..f03a80a29b 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -92,14 +92,6 @@ enum { #define LPC_HC_REGS_OPB_SIZE 0x00001000 -/* - * TODO: the "primary" cell should only be added on chip 0. This is - * how skiboot chooses the default LPC controller on multichip - * systems. - * - * It would be easly done if we can change the populate() interface to - * replace the PnvXScomInterface parameter by a PnvChip one - */ static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) { const char compat[] = "ibm,power8-lpc\0ibm,lpc"; @@ -119,7 +111,6 @@ static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); - _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); return 0; } @@ -250,6 +241,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void pnv_lpc_eval_irqs(PnvLpcController *lpc) +{ + bool lpc_to_opb_irq = false; + + /* Update LPC controller to OPB line */ + if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { + uint32_t irqs; + + irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; + lpc_to_opb_irq = (irqs != 0); + } + + /* We don't honor the polarity register, it's pointless and unused + * anyway + */ + if (lpc_to_opb_irq) { + lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; + } else { + lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; + } + + /* Update OPB internal latch */ + lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; + + /* Reflect the interrupt */ + pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0); +} + static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) { PnvLpcController *lpc = opaque; @@ -300,12 +319,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, break; case LPC_HC_IRQSER_CTRL: lpc->lpc_hc_irqser_ctrl = val; + pnv_lpc_eval_irqs(lpc); break; case LPC_HC_IRQMASK: lpc->lpc_hc_irqmask = val; + pnv_lpc_eval_irqs(lpc); break; case LPC_HC_IRQSTAT: lpc->lpc_hc_irqstat &= ~val; + pnv_lpc_eval_irqs(lpc); break; case LPC_HC_ERROR_ADDRESS: break; @@ -363,14 +385,15 @@ static void opb_master_write(void *opaque, hwaddr addr, switch (addr) { case OPB_MASTER_LS_IRQ_STAT: lpc->opb_irq_stat &= ~val; + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_MASK: - /* XXX Filter out reserved bits */ lpc->opb_irq_mask = val; + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_POL: - /* XXX Filter out reserved bits */ lpc->opb_irq_pol = val; + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_INPUT: /* Read only */ @@ -398,6 +421,8 @@ static const MemoryRegionOps opb_master_ops = { static void pnv_lpc_realize(DeviceState *dev, Error **errp) { PnvLpcController *lpc = PNV_LPC(dev); + Object *obj; + Error *error = NULL; /* Reg inits */ lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; @@ -441,6 +466,15 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev), &pnv_lpc_xscom_ops, lpc, "xscom-lpc", PNV_XSCOM_LPC_SIZE); + + /* get PSI object from chip */ + obj = object_property_get_link(OBJECT(dev), "psi", &error); + if (!obj) { + error_setg(errp, "%s: required link 'psi' not found: %s", + __func__, error_get_pretty(error)); + return; + } + lpc->psi = PNV_PSI(obj); } static void pnv_lpc_class_init(ObjectClass *klass, void *data) @@ -470,3 +504,53 @@ static void pnv_lpc_register_types(void) } type_init(pnv_lpc_register_types) + +/* If we don't use the built-in LPC interrupt deserializer, we need + * to provide a set of qirqs for the ISA bus or things will go bad. + * + * Most machines using pre-Naples chips (without said deserializer) + * have a CPLD that will collect the SerIRQ and shoot them as a + * single level interrupt to the P8 chip. So let's setup a hook + * for doing just that. + */ +static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) +{ + PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); + uint32_t old_state = pnv->cpld_irqstate; + PnvLpcController *lpc = PNV_LPC(opaque); + + if (level) { + pnv->cpld_irqstate |= 1u << n; + } else { + pnv->cpld_irqstate &= ~(1u << n); + } + + if (pnv->cpld_irqstate != old_state) { + pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_EXTERNAL, pnv->cpld_irqstate != 0); + } +} + +static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) +{ + PnvLpcController *lpc = PNV_LPC(opaque); + + /* The Naples HW latches the 1 levels, clearing is done by SW */ + if (level) { + lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n; + pnv_lpc_eval_irqs(lpc); + } +} + +qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, + int nirqs) +{ + /* Not all variants have a working serial irq decoder. If not, + * handling of LPC interrupts becomes a platform issue (some + * platforms have a CPLD to do it). + */ + if (chip_type == PNV_CHIP_POWER8NVL) { + return qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, nirqs); + } else { + return qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, lpc, nirqs); + } +} diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c new file mode 100644 index 0000000000..04880f26d6 --- /dev/null +++ b/hw/ppc/pnv_occ.c @@ -0,0 +1,136 @@ +/* + * QEMU PowerPC PowerNV Emulation of a few OCC related registers + * + * Copyright (c) 2015-2017, IBM Corporation. + * + * 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/>. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "target/ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" + +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_occ.h" + +#define OCB_OCI_OCCMISC 0x4020 +#define OCB_OCI_OCCMISC_AND 0x4021 +#define OCB_OCI_OCCMISC_OR 0x4022 + +static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) +{ + bool irq_state; + + val &= 0xffff000000000000ull; + + occ->occmisc = val; + irq_state = !!(val >> 63); + pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state); +} + +static uint64_t pnv_occ_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvOCC *occ = PNV_OCC(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case OCB_OCI_OCCMISC: + val = occ->occmisc; + break; + default: + qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } + return val; +} + +static void pnv_occ_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvOCC *occ = PNV_OCC(opaque); + uint32_t offset = addr >> 3; + + switch (offset) { + case OCB_OCI_OCCMISC_AND: + pnv_occ_set_misc(occ, occ->occmisc & val); + break; + case OCB_OCI_OCCMISC_OR: + pnv_occ_set_misc(occ, occ->occmisc | val); + break; + case OCB_OCI_OCCMISC: + pnv_occ_set_misc(occ, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } +} + +static const MemoryRegionOps pnv_occ_xscom_ops = { + .read = pnv_occ_xscom_read, + .write = pnv_occ_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + + +static void pnv_occ_realize(DeviceState *dev, Error **errp) +{ + PnvOCC *occ = PNV_OCC(dev); + Object *obj; + Error *error = NULL; + + occ->occmisc = 0; + + /* get PSI object from chip */ + obj = object_property_get_link(OBJECT(dev), "psi", &error); + if (!obj) { + error_setg(errp, "%s: required link 'psi' not found: %s", + __func__, error_get_pretty(error)); + return; + } + occ->psi = PNV_PSI(obj); + + /* XScom region for OCC registers */ + pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), &pnv_occ_xscom_ops, + occ, "xscom-occ", PNV_XSCOM_OCC_SIZE); +} + +static void pnv_occ_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_occ_realize; +} + +static const TypeInfo pnv_occ_type_info = { + .name = TYPE_PNV_OCC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvOCC), + .class_init = pnv_occ_class_init, +}; + +static void pnv_occ_register_types(void) +{ + type_register_static(&pnv_occ_type_info); +} + +type_init(pnv_occ_register_types) diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c new file mode 100644 index 0000000000..2bf5bfe3fd --- /dev/null +++ b/hw/ppc/pnv_psi.c @@ -0,0 +1,571 @@ +/* + * QEMU PowerPC PowerNV Processor Service Interface (PSI) model + * + * Copyright (c) 2015-2017, IBM Corporation. + * + * 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 "qemu/osdep.h" +#include "hw/hw.h" +#include "target/ppc/cpu.h" +#include "qemu/log.h" +#include "qapi/error.h" + +#include "exec/address-spaces.h" + +#include "hw/ppc/fdt.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_psi.h" + +#include <libfdt.h> + +#define PSIHB_XSCOM_FIR_RW 0x00 +#define PSIHB_XSCOM_FIR_AND 0x01 +#define PSIHB_XSCOM_FIR_OR 0x02 +#define PSIHB_XSCOM_FIRMASK_RW 0x03 +#define PSIHB_XSCOM_FIRMASK_AND 0x04 +#define PSIHB_XSCOM_FIRMASK_OR 0x05 +#define PSIHB_XSCOM_FIRACT0 0x06 +#define PSIHB_XSCOM_FIRACT1 0x07 + +/* Host Bridge Base Address Register */ +#define PSIHB_XSCOM_BAR 0x0a +#define PSIHB_BAR_EN 0x0000000000000001ull + +/* FSP Base Address Register */ +#define PSIHB_XSCOM_FSPBAR 0x0b + +/* PSI Host Bridge Control/Status Register */ +#define PSIHB_XSCOM_CR 0x0e +#define PSIHB_CR_FSP_CMD_ENABLE 0x8000000000000000ull +#define PSIHB_CR_FSP_MMIO_ENABLE 0x4000000000000000ull +#define PSIHB_CR_FSP_IRQ_ENABLE 0x1000000000000000ull +#define PSIHB_CR_FSP_ERR_RSP_ENABLE 0x0800000000000000ull +#define PSIHB_CR_PSI_LINK_ENABLE 0x0400000000000000ull +#define PSIHB_CR_FSP_RESET 0x0200000000000000ull +#define PSIHB_CR_PSIHB_RESET 0x0100000000000000ull +#define PSIHB_CR_PSI_IRQ 0x0000800000000000ull +#define PSIHB_CR_FSP_IRQ 0x0000400000000000ull +#define PSIHB_CR_FSP_LINK_ACTIVE 0x0000200000000000ull +#define PSIHB_CR_IRQ_CMD_EXPECT 0x0000010000000000ull + /* and more ... */ + +/* PSIHB Status / Error Mask Register */ +#define PSIHB_XSCOM_SEMR 0x0f + +/* XIVR, to signal interrupts to the CEC firmware. more XIVR below. */ +#define PSIHB_XSCOM_XIVR_FSP 0x10 +#define PSIHB_XIVR_SERVER_SH 40 +#define PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH) +#define PSIHB_XIVR_PRIO_SH 32 +#define PSIHB_XIVR_PRIO_MSK (0xffull << PSIHB_XIVR_PRIO_SH) +#define PSIHB_XIVR_SRC_SH 29 +#define PSIHB_XIVR_SRC_MSK (0x7ull << PSIHB_XIVR_SRC_SH) +#define PSIHB_XIVR_PENDING 0x01000000ull + +/* PSI Host Bridge Set Control/ Status Register */ +#define PSIHB_XSCOM_SCR 0x12 + +/* PSI Host Bridge Clear Control/ Status Register */ +#define PSIHB_XSCOM_CCR 0x13 + +/* DMA Upper Address Register */ +#define PSIHB_XSCOM_DMA_UPADD 0x14 + +/* Interrupt Status */ +#define PSIHB_XSCOM_IRQ_STAT 0x15 +#define PSIHB_IRQ_STAT_OCC 0x0000001000000000ull +#define PSIHB_IRQ_STAT_FSI 0x0000000800000000ull +#define PSIHB_IRQ_STAT_LPCI2C 0x0000000400000000ull +#define PSIHB_IRQ_STAT_LOCERR 0x0000000200000000ull +#define PSIHB_IRQ_STAT_EXT 0x0000000100000000ull + +/* remaining XIVR */ +#define PSIHB_XSCOM_XIVR_OCC 0x16 +#define PSIHB_XSCOM_XIVR_FSI 0x17 +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18 +#define PSIHB_XSCOM_XIVR_LOCERR 0x19 +#define PSIHB_XSCOM_XIVR_EXT 0x1a + +/* Interrupt Requester Source Compare Register */ +#define PSIHB_XSCOM_IRSN 0x1b +#define PSIHB_IRSN_COMP_SH 45 +#define PSIHB_IRSN_COMP_MSK (0x7ffffull << PSIHB_IRSN_COMP_SH) +#define PSIHB_IRSN_IRQ_MUX 0x0000000800000000ull +#define PSIHB_IRSN_IRQ_RESET 0x0000000400000000ull +#define PSIHB_IRSN_DOWNSTREAM_EN 0x0000000200000000ull +#define PSIHB_IRSN_UPSTREAM_EN 0x0000000100000000ull +#define PSIHB_IRSN_COMPMASK_SH 13 +#define PSIHB_IRSN_COMPMASK_MSK (0x7ffffull << PSIHB_IRSN_COMPMASK_SH) + +#define PSIHB_BAR_MASK 0x0003fffffff00000ull +#define PSIHB_FSPBAR_MASK 0x0003ffff00000000ull + +static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar) +{ + MemoryRegion *sysmem = get_system_memory(); + uint64_t old = psi->regs[PSIHB_XSCOM_BAR]; + + psi->regs[PSIHB_XSCOM_BAR] = bar & (PSIHB_BAR_MASK | PSIHB_BAR_EN); + + /* Update MR, always remove it first */ + if (old & PSIHB_BAR_EN) { + memory_region_del_subregion(sysmem, &psi->regs_mr); + } + + /* Then add it back if needed */ + if (bar & PSIHB_BAR_EN) { + uint64_t addr = bar & PSIHB_BAR_MASK; + memory_region_add_subregion(sysmem, addr, &psi->regs_mr); + } +} + +static void pnv_psi_update_fsp_mr(PnvPsi *psi) +{ + /* TODO: Update FSP MR if/when we support FSP BAR */ +} + +static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr) +{ + uint64_t old = psi->regs[PSIHB_XSCOM_CR]; + + psi->regs[PSIHB_XSCOM_CR] = cr; + + /* Check some bit changes */ + if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) { + pnv_psi_update_fsp_mr(psi); + } +} + +static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val) +{ + ICSState *ics = &psi->ics; + + /* In this model we ignore the up/down enable bits for now + * as SW doesn't use them (other than setting them at boot). + * We ignore IRQ_MUX, its meaning isn't clear and we don't use + * it and finally we ignore reset (XXX fix that ?) + */ + psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK | + PSIHB_IRSN_IRQ_MUX | + PSIHB_IRSN_IRQ_RESET | + PSIHB_IRSN_DOWNSTREAM_EN | + PSIHB_IRSN_UPSTREAM_EN); + + /* We ignore the compare mask as well, our ICS emulation is too + * simplistic to make any use if it, and we extract the offset + * from the compare value + */ + ics->offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH; +} + +/* + * FSP and PSI interrupts are muxed under the same number. + */ +static const uint32_t xivr_regs[] = { + [PSIHB_IRQ_PSI] = PSIHB_XSCOM_XIVR_FSP, + [PSIHB_IRQ_FSP] = PSIHB_XSCOM_XIVR_FSP, + [PSIHB_IRQ_OCC] = PSIHB_XSCOM_XIVR_OCC, + [PSIHB_IRQ_FSI] = PSIHB_XSCOM_XIVR_FSI, + [PSIHB_IRQ_LPC_I2C] = PSIHB_XSCOM_XIVR_LPCI2C, + [PSIHB_IRQ_LOCAL_ERR] = PSIHB_XSCOM_XIVR_LOCERR, + [PSIHB_IRQ_EXTERNAL] = PSIHB_XSCOM_XIVR_EXT, +}; + +static const uint32_t stat_regs[] = { + [PSIHB_IRQ_PSI] = PSIHB_XSCOM_CR, + [PSIHB_IRQ_FSP] = PSIHB_XSCOM_CR, + [PSIHB_IRQ_OCC] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_IRQ_FSI] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_IRQ_LPC_I2C] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_IRQ_LOCAL_ERR] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_IRQ_EXTERNAL] = PSIHB_XSCOM_IRQ_STAT, +}; + +static const uint64_t stat_bits[] = { + [PSIHB_IRQ_PSI] = PSIHB_CR_PSI_IRQ, + [PSIHB_IRQ_FSP] = PSIHB_CR_FSP_IRQ, + [PSIHB_IRQ_OCC] = PSIHB_IRQ_STAT_OCC, + [PSIHB_IRQ_FSI] = PSIHB_IRQ_STAT_FSI, + [PSIHB_IRQ_LPC_I2C] = PSIHB_IRQ_STAT_LPCI2C, + [PSIHB_IRQ_LOCAL_ERR] = PSIHB_IRQ_STAT_LOCERR, + [PSIHB_IRQ_EXTERNAL] = PSIHB_IRQ_STAT_EXT, +}; + +void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state) +{ + ICSState *ics = &psi->ics; + uint32_t xivr_reg; + uint32_t stat_reg; + uint32_t src; + bool masked; + + if (irq > PSIHB_IRQ_EXTERNAL) { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq); + return; + } + + xivr_reg = xivr_regs[irq]; + stat_reg = stat_regs[irq]; + + src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; + if (state) { + psi->regs[stat_reg] |= stat_bits[irq]; + /* TODO: optimization, check mask here. That means + * re-evaluating when unmasking + */ + qemu_irq_raise(ics->qirqs[src]); + } else { + psi->regs[stat_reg] &= ~stat_bits[irq]; + + /* FSP and PSI are muxed so don't lower if either is still set */ + if (stat_reg != PSIHB_XSCOM_CR || + !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) { + qemu_irq_lower(ics->qirqs[src]); + } else { + state = true; + } + } + + /* Note about the emulation of the pending bit: This isn't + * entirely correct. The pending bit should be cleared when the + * EOI has been received. However, we don't have callbacks on EOI + * (especially not under KVM) so no way to emulate that properly, + * so instead we just set that bit as the logical "output" of the + * XIVR (ie pending & !masked) + * + * CLG: We could define a new ICS object with a custom eoi() + * handler to clear the pending bit. But I am not sure this would + * be useful for the software anyhow. + */ + masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK; + if (state && !masked) { + psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING; + } else { + psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING; + } +} + +static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val) +{ + ICSState *ics = &psi->ics; + uint16_t server; + uint8_t prio; + uint8_t src; + + psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) | + (val & (PSIHB_XIVR_SERVER_MSK | + PSIHB_XIVR_PRIO_MSK | + PSIHB_XIVR_SRC_MSK)); + val = psi->regs[reg]; + server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH; + prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH; + src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; + + if (src >= PSI_NUM_INTERRUPTS) { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", src); + return; + } + + /* Remove pending bit if the IRQ is masked */ + if ((psi->regs[reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK) { + psi->regs[reg] &= ~PSIHB_XIVR_PENDING; + } + + /* The low order 2 bits are the link pointer (Type II interrupts). + * Shift back to get a valid IRQ server. + */ + server >>= 2; + + /* Now because of source remapping, weird things can happen + * if you change the source number dynamically, our simple ICS + * doesn't deal with remapping. So we just poke a different + * ICS entry based on what source number was written. This will + * do for now but a more accurate implementation would instead + * use a fixed server/prio and a remapper of the generated irq. + */ + ics_simple_write_xive(ics, src, server, prio, prio); +} + +static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio) +{ + uint64_t val = 0xffffffffffffffffull; + + switch (offset) { + case PSIHB_XSCOM_FIR_RW: + case PSIHB_XSCOM_FIRACT0: + case PSIHB_XSCOM_FIRACT1: + case PSIHB_XSCOM_BAR: + case PSIHB_XSCOM_FSPBAR: + case PSIHB_XSCOM_CR: + case PSIHB_XSCOM_XIVR_FSP: + case PSIHB_XSCOM_XIVR_OCC: + case PSIHB_XSCOM_XIVR_FSI: + case PSIHB_XSCOM_XIVR_LPCI2C: + case PSIHB_XSCOM_XIVR_LOCERR: + case PSIHB_XSCOM_XIVR_EXT: + case PSIHB_XSCOM_IRQ_STAT: + case PSIHB_XSCOM_SEMR: + case PSIHB_XSCOM_DMA_UPADD: + case PSIHB_XSCOM_IRSN: + val = psi->regs[offset]; + break; + default: + qemu_log_mask(LOG_UNIMP, "PSI: read at Ox%" PRIx32 "\n", offset); + } + return val; +} + +static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val, + bool mmio) +{ + switch (offset) { + case PSIHB_XSCOM_FIR_RW: + case PSIHB_XSCOM_FIRACT0: + case PSIHB_XSCOM_FIRACT1: + case PSIHB_XSCOM_SEMR: + case PSIHB_XSCOM_DMA_UPADD: + psi->regs[offset] = val; + break; + case PSIHB_XSCOM_FIR_OR: + psi->regs[PSIHB_XSCOM_FIR_RW] |= val; + break; + case PSIHB_XSCOM_FIR_AND: + psi->regs[PSIHB_XSCOM_FIR_RW] &= val; + break; + case PSIHB_XSCOM_BAR: + /* Only XSCOM can write this one */ + if (!mmio) { + pnv_psi_set_bar(psi, val); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: invalid write of BAR\n"); + } + break; + case PSIHB_XSCOM_FSPBAR: + psi->regs[PSIHB_XSCOM_FSPBAR] = val & PSIHB_FSPBAR_MASK; + pnv_psi_update_fsp_mr(psi); + break; + case PSIHB_XSCOM_CR: + pnv_psi_set_cr(psi, val); + break; + case PSIHB_XSCOM_SCR: + pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val); + break; + case PSIHB_XSCOM_CCR: + pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val); + break; + case PSIHB_XSCOM_XIVR_FSP: + case PSIHB_XSCOM_XIVR_OCC: + case PSIHB_XSCOM_XIVR_FSI: + case PSIHB_XSCOM_XIVR_LPCI2C: + case PSIHB_XSCOM_XIVR_LOCERR: + case PSIHB_XSCOM_XIVR_EXT: + pnv_psi_set_xivr(psi, offset, val); + break; + case PSIHB_XSCOM_IRQ_STAT: + /* Read only */ + qemu_log_mask(LOG_GUEST_ERROR, "PSI: invalid write of IRQ_STAT\n"); + break; + case PSIHB_XSCOM_IRSN: + pnv_psi_set_irsn(psi, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "PSI: write at Ox%" PRIx32 "\n", offset); + } +} + +/* + * The values of the registers when accessed through the MMIO region + * follow the relation : xscom = (mmio + 0x50) >> 3 + */ +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + return pnv_psi_reg_read(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, true); +} + +static void pnv_psi_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + pnv_psi_reg_write(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, val, true); +} + +static const MemoryRegionOps psi_mmio_ops = { + .read = pnv_psi_mmio_read, + .write = pnv_psi_mmio_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_psi_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + return pnv_psi_reg_read(opaque, addr >> 3, false); +} + +static void pnv_psi_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + pnv_psi_reg_write(opaque, addr >> 3, val, false); +} + +static const MemoryRegionOps pnv_psi_xscom_ops = { + .read = pnv_psi_xscom_read, + .write = pnv_psi_xscom_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + } +}; + +static void pnv_psi_init(Object *obj) +{ + PnvPsi *psi = PNV_PSI(obj); + + object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE); + object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL); +} + +static const uint8_t irq_to_xivr[] = { + PSIHB_XSCOM_XIVR_FSP, + PSIHB_XSCOM_XIVR_OCC, + PSIHB_XSCOM_XIVR_FSI, + PSIHB_XSCOM_XIVR_LPCI2C, + PSIHB_XSCOM_XIVR_LOCERR, + PSIHB_XSCOM_XIVR_EXT, +}; + +static void pnv_psi_realize(DeviceState *dev, Error **errp) +{ + PnvPsi *psi = PNV_PSI(dev); + ICSState *ics = &psi->ics; + Object *obj; + Error *err = NULL; + unsigned int i; + + obj = object_property_get_link(OBJECT(dev), "xics", &err); + if (!obj) { + error_setg(errp, "%s: required link 'xics' not found: %s", + __func__, error_get_pretty(err)); + return; + } + + /* Create PSI interrupt control source */ + object_property_add_const_link(OBJECT(ics), "xics", obj, &error_abort); + object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(ics), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + for (i = 0; i < ics->nr_irqs; i++) { + ics_set_irq_type(ics, i, true); + } + + /* XSCOM region for PSI registers */ + pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops, + psi, "xscom-psi", PNV_XSCOM_PSIHB_SIZE); + + /* Initialize MMIO region */ + memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi, + "psihb", PNV_PSIHB_SIZE); + + /* Default BAR for MMIO region */ + pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); + + /* Default sources in XIVR */ + for (i = 0; i < PSI_NUM_INTERRUPTS; i++) { + uint8_t xivr = irq_to_xivr[i]; + psi->regs[xivr] = PSIHB_XIVR_PRIO_MSK | + ((uint64_t) i << PSIHB_XIVR_SRC_SH); + } +} + +static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) +{ + const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x"; + char *name; + int offset; + uint32_t lpc_pcba = PNV_XSCOM_PSIHB_BASE; + uint32_t reg[] = { + cpu_to_be32(lpc_pcba), + cpu_to_be32(PNV_XSCOM_PSIHB_SIZE) + }; + + name = g_strdup_printf("psihb@%x", lpc_pcba); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); + _FDT((fdt_setprop(fdt, offset, "compatible", compat, + sizeof(compat)))); + return 0; +} + +static Property pnv_psi_properties[] = { + DEFINE_PROP_UINT64("bar", PnvPsi, bar, 0), + DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_psi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xdc->populate = pnv_psi_populate; + + dc->realize = pnv_psi_realize; + dc->props = pnv_psi_properties; +} + +static const TypeInfo pnv_psi_info = { + .name = TYPE_PNV_PSI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PnvPsi), + .instance_init = pnv_psi_init, + .class_init = pnv_psi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_psi_register_types(void) +{ + type_register_static(&pnv_psi_info); +} + +type_init(pnv_psi_register_types) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 35db949dbc..80d12d005c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -40,6 +40,7 @@ #include "kvm_ppc.h" #include "migration/migration.h" #include "mmu-hash64.h" +#include "mmu-book3s-v3.h" #include "qom/cpu.h" #include "hw/boards.h" @@ -96,66 +97,40 @@ #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) -static int try_create_xics(sPAPRMachineState *spapr, const char *type_ics, - const char *type_icp, int nr_servers, - int nr_irqs, Error **errp) +static ICSState *spapr_ics_create(sPAPRMachineState *spapr, + const char *type_ics, + int nr_irqs, Error **errp) { - XICSFabric *xi = XICS_FABRIC(spapr); Error *err = NULL, *local_err = NULL; - ICSState *ics = NULL; - int i; + Object *obj; - ics = ICS_SIMPLE(object_new(type_ics)); - object_property_add_child(OBJECT(spapr), "ics", OBJECT(ics), NULL); - object_property_set_int(OBJECT(ics), nr_irqs, "nr-irqs", &err); - object_property_add_const_link(OBJECT(ics), "xics", OBJECT(xi), NULL); - object_property_set_bool(OBJECT(ics), true, "realized", &local_err); + obj = object_new(type_ics); + object_property_add_child(OBJECT(spapr), "ics", obj, NULL); + object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort); + object_property_set_int(obj, nr_irqs, "nr-irqs", &err); + object_property_set_bool(obj, true, "realized", &local_err); error_propagate(&err, local_err); if (err) { - goto error; - } - - spapr->icps = g_malloc0(nr_servers * sizeof(ICPState)); - spapr->nr_servers = nr_servers; - - for (i = 0; i < nr_servers; i++) { - ICPState *icp = &spapr->icps[i]; - - object_initialize(icp, sizeof(*icp), type_icp); - object_property_add_child(OBJECT(spapr), "icp[*]", OBJECT(icp), NULL); - object_property_add_const_link(OBJECT(icp), "xics", OBJECT(xi), NULL); - object_property_set_bool(OBJECT(icp), true, "realized", &err); - if (err) { - goto error; - } - object_unref(OBJECT(icp)); + error_propagate(errp, err); + return NULL; } - spapr->ics = ics; - return 0; - -error: - error_propagate(errp, err); - if (ics) { - object_unparent(OBJECT(ics)); - } - return -1; + return ICS_SIMPLE(obj); } -static int xics_system_init(MachineState *machine, - int nr_servers, int nr_irqs, Error **errp) +static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) { - int rc = -1; + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); if (kvm_enabled()) { Error *err = NULL; if (machine_kernel_irqchip_allowed(machine) && - !xics_kvm_init(SPAPR_MACHINE(machine), errp)) { - rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_KVM, - TYPE_KVM_ICP, nr_servers, nr_irqs, &err); + !xics_kvm_init(spapr, errp)) { + spapr->icp_type = TYPE_KVM_ICP; + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, &err); } - if (machine_kernel_irqchip_required(machine) && rc < 0) { + if (machine_kernel_irqchip_required(machine) && !spapr->ics) { error_reportf_err(err, "kernel_irqchip requested but unavailable: "); } else { @@ -163,13 +138,11 @@ static int xics_system_init(MachineState *machine, } } - if (rc < 0) { - xics_spapr_init(SPAPR_MACHINE(machine), errp); - rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_SIMPLE, - TYPE_ICP, nr_servers, nr_irqs, errp); + if (!spapr->ics) { + xics_spapr_init(spapr, errp); + spapr->icp_type = TYPE_ICP; + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, errp); } - - return rc; } static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, @@ -226,6 +199,85 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, CPUState *cs) return ret; } +/* Populate the "ibm,pa-features" property */ +static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset, + bool legacy_guest) +{ + uint8_t pa_features_206[] = { 6, 0, + 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; + uint8_t pa_features_207[] = { 24, 0, + 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00 }; + uint8_t pa_features_300[] = { 66, 0, + /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */ + /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, SSO, 5: LE|CFAR|EB|LSQ */ + 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */ + /* 6: DS207 */ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ + /* 16: Vector */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ + /* 18: Vec. Scalar, 20: Vec. XOR, 22: HTM */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 18 - 23 */ + /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */ + /* 30: MMR, 32: LE atomic, 34: EBB + ext EBB */ + 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */ + /* 36: SPR SO, 38: Copy/Paste, 40: Radix MMU */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 36 - 41 */ + /* 42: PM, 44: PC RA, 46: SC vec'd */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */ + /* 48: SIMD, 50: QP BFP, 52: String */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */ + /* 54: DecFP, 56: DecI, 58: SHA */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */ + /* 60: NM atomic, 62: RNG */ + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */ + }; + uint8_t *pa_features; + size_t pa_size; + + switch (POWERPC_MMU_VER(env->mmu_model)) { + case POWERPC_MMU_VER_2_06: + pa_features = pa_features_206; + pa_size = sizeof(pa_features_206); + break; + case POWERPC_MMU_VER_2_07: + pa_features = pa_features_207; + pa_size = sizeof(pa_features_207); + break; + case POWERPC_MMU_VER_3_00: + pa_features = pa_features_300; + pa_size = sizeof(pa_features_300); + break; + default: + return; + } + + if (env->ci_large_pages) { + /* + * Note: we keep CI large pages off by default because a 64K capable + * guest provisioned with large pages might otherwise try to map a qemu + * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages + * even if that qemu runs on a 4k host. + * We dd this bit back here if we are confident this is not an issue + */ + pa_features[3] |= 0x20; + } + if (kvmppc_has_cap_htm() && pa_size > 24) { + pa_features[24] |= 0x80; /* Transactional memory support */ + } + if (legacy_guest && pa_size > 40) { + /* Workaround for broken kernels that attempt (guest) radix + * mode when they can't handle it, if they see the radix bit set + * in pa-features. So hide it from them. */ + pa_features[40 + 2] &= ~0x80; /* Radix MMU */ + } + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); +} + static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) { int ret = 0, offset, cpus_offset; @@ -236,6 +288,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; DeviceClass *dc = DEVICE_GET_CLASS(cs); int index = ppc_get_vcpu_dt_id(cpu); int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu)); @@ -277,6 +330,9 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) if (ret < 0) { return ret; } + + spapr_populate_pa_features(env, fdt, offset, + spapr->cas_legacy_guest_workaround); } return ret; } @@ -378,67 +434,6 @@ static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt) return 0; } -/* Populate the "ibm,pa-features" property */ -static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset) -{ - uint8_t pa_features_206[] = { 6, 0, - 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; - uint8_t pa_features_207[] = { 24, 0, - 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x80, 0x00, 0x80, 0x00, 0x00, 0x00 }; - /* Currently we don't advertise any of the "new" ISAv3.00 functionality */ - uint8_t pa_features_300[] = { 64, 0, - 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */ - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ - 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24 - 29 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 - 35 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 36 - 41 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 - 47 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 48 - 53 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 54 - 59 */ - 0x00, 0x00, 0x00, 0x00 }; /* 60 - 63 */ - - uint8_t *pa_features; - size_t pa_size; - - switch (POWERPC_MMU_VER(env->mmu_model)) { - case POWERPC_MMU_VER_2_06: - pa_features = pa_features_206; - pa_size = sizeof(pa_features_206); - break; - case POWERPC_MMU_VER_2_07: - pa_features = pa_features_207; - pa_size = sizeof(pa_features_207); - break; - case POWERPC_MMU_VER_3_00: - pa_features = pa_features_300; - pa_size = sizeof(pa_features_300); - break; - default: - return; - } - - if (env->ci_large_pages) { - /* - * Note: we keep CI large pages off by default because a 64K capable - * guest provisioned with large pages might otherwise try to map a qemu - * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages - * even if that qemu runs on a 4k host. - * We dd this bit back here if we are confident this is not an issue - */ - pa_features[3] |= 0x20; - } - if (kvmppc_has_cap_htm() && pa_size > 24) { - pa_features[24] |= 0x80; /* Transactional memory support */ - } - - _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); -} - static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, sPAPRMachineState *spapr) { @@ -459,6 +454,8 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; int drc_index; + uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; + int i; drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index); if (drc) { @@ -533,7 +530,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, page_sizes_prop, page_sizes_prop_size))); } - spapr_populate_pa_features(env, fdt, offset); + spapr_populate_pa_features(env, fdt, offset, false); _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", cs->cpu_index / vcpus_per_socket))); @@ -544,6 +541,17 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs)); _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); + + if (pcc->radix_page_info) { + for (i = 0; i < pcc->radix_page_info->count; i++) { + radix_AP_encodings[i] = + cpu_to_be32(pcc->radix_page_info->entries[i]); + } + _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings", + radix_AP_encodings, + pcc->radix_page_info->count * + sizeof(radix_AP_encodings[0])))); + } } static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) @@ -842,6 +850,33 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) spapr_dt_rtas_tokens(fdt, rtas); } +/* Prepare ibm,arch-vec-5-platform-support, which indicates the MMU features + * that the guest may request and thus the valid values for bytes 24..26 of + * option vector 5: */ +static void spapr_dt_ov5_platform_support(void *fdt, int chosen) +{ + char val[2 * 3] = { + 24, 0x00, /* Hash/Radix, filled in below. */ + 25, 0x00, /* Hash options: Segment Tables == no, GTSE == no. */ + 26, 0x40, /* Radix options: GTSE == yes. */ + }; + + if (kvm_enabled()) { + if (kvmppc_has_cap_mmu_radix() && kvmppc_has_cap_mmu_hash_v3()) { + val[1] = 0x80; /* OV5_MMU_BOTH */ + } else if (kvmppc_has_cap_mmu_radix()) { + val[1] = 0x40; /* OV5_MMU_RADIX_300 */ + } else { + val[1] = 0x00; /* Hash */ + } + } else { + /* TODO: TCG case, hash */ + val[1] = 0x00; + } + _FDT(fdt_setprop(fdt, chosen, "ibm,arch-vec-5-platform-support", + val, sizeof(val))); +} + static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) { MachineState *machine = MACHINE(spapr); @@ -895,6 +930,8 @@ static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); } + spapr_dt_ov5_platform_support(fdt, chosen); + g_free(stdout_path); g_free(bootlist); } @@ -933,6 +970,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, void *fdt; sPAPRPHBState *phb; char *buf; + int smt = kvmppc_smt_threads(); fdt = g_malloc0(FDT_MAX_SIZE); _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); @@ -972,7 +1010,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2)); /* /interrupt controller */ - spapr_dt_xics(spapr->nr_servers, fdt, PHANDLE_XICP); + spapr_dt_xics(DIV_ROUND_UP(max_cpus * smt, smp_threads), fdt, PHANDLE_XICP); ret = spapr_populate_memory(spapr, fdt); if (ret < 0) { @@ -1100,7 +1138,7 @@ static int get_htab_fd(sPAPRMachineState *spapr) return spapr->htab_fd; } -static void close_htab_fd(sPAPRMachineState *spapr) +void close_htab_fd(sPAPRMachineState *spapr) { if (spapr->htab_fd >= 0) { close(spapr->htab_fd); @@ -1227,6 +1265,19 @@ static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, } } +void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) +{ + spapr_reallocate_hpt(spapr, + spapr_hpt_shift_for_ramsize(MACHINE(spapr)->maxram_size), + &error_fatal); + if (spapr->vrma_adjust) { + spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), + spapr->htab_shift); + } + /* We're setting up a hash table, so that means we're not radix */ + spapr->patb_entry = 0; +} + static void find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) { bool matched = false; @@ -1255,17 +1306,14 @@ static void ppc_spapr_reset(void) /* Check for unknown sysbus devices */ foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); - spapr->patb_entry = 0; - - /* Allocate and/or reset the hash page table */ - spapr_reallocate_hpt(spapr, - spapr_hpt_shift_for_ramsize(machine->maxram_size), - &error_fatal); - - /* Update the RMA size if necessary */ - if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), - spapr->htab_shift); + if (kvm_enabled() && kvmppc_has_cap_mmu_radix()) { + /* If using KVM with radix mode available, VCPUs can be started + * without a HPT because KVM will start them in radix mode. + * Set the GR bit in PATB so that we know there is no HPT. */ + spapr->patb_entry = PATBE1_GR; + } else { + spapr->patb_entry = 0; + spapr_setup_hpt_and_vrma(spapr); } qemu_devices_reset(); @@ -1333,13 +1381,13 @@ static void spapr_create_nvram(sPAPRMachineState *spapr) static void spapr_rtc_create(sPAPRMachineState *spapr) { - DeviceState *dev = qdev_create(NULL, TYPE_SPAPR_RTC); - - qdev_init_nofail(dev); - spapr->rtc = dev; - - object_property_add_alias(qdev_get_machine(), "rtc-time", - OBJECT(spapr->rtc), "date", NULL); + object_initialize(&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC); + object_property_add_child(OBJECT(spapr), "rtc", OBJECT(&spapr->rtc), + &error_fatal); + object_property_set_bool(OBJECT(&spapr->rtc), true, "realized", + &error_fatal); + object_property_add_alias(OBJECT(spapr), "rtc-time", OBJECT(&spapr->rtc), + "date", &error_fatal); } /* Returns whether we want to use VGA or not */ @@ -1366,9 +1414,10 @@ static int spapr_post_load(void *opaque, int version_id) int err = 0; if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) { - int i; - for (i = 0; i < spapr->nr_servers; i++) { - icp_resend(&spapr->icps[i]); + CPUState *cs; + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + icp_resend(ICP(cpu->intc)); } } @@ -1377,7 +1426,7 @@ static int spapr_post_load(void *opaque, int version_id) * So when migrating from those versions, poke the incoming offset * value into the RTC device */ if (version_id < 3) { - err = spapr_rtc_import_offset(spapr->rtc, spapr->rtc_offset); + err = spapr_rtc_import_offset(&spapr->rtc, spapr->rtc_offset); } return err; @@ -1990,7 +2039,6 @@ static void ppc_spapr_init(MachineState *machine) hwaddr node0_size = spapr_node0_size(); long load_limit, fw_size; char *filename; - int smt = kvmppc_smt_threads(); msi_nonbroken = true; @@ -2041,8 +2089,7 @@ static void ppc_spapr_init(MachineState *machine) load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; /* Set up Interrupt Controller before we create the VCPUs */ - xics_system_init(machine, DIV_ROUND_UP(max_cpus * smt, smp_threads), - XICS_IRQS_SPAPR, &error_fatal); + xics_system_init(machine, XICS_IRQS_SPAPR, &error_fatal); /* Set up containers for ibm,client-set-architecture negotiated options */ spapr->ov5 = spapr_ovec_new(); @@ -2054,6 +2101,11 @@ static void ppc_spapr_init(MachineState *machine) } spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); + if (kvmppc_has_cap_mmu_radix()) { + /* KVM always allows GTSE with radix... */ + spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE); + } + /* ... but not with hash (currently). */ /* advertise support for dedicated HP event source to guests */ if (spapr->use_hotplug_event_source) { @@ -2281,10 +2333,12 @@ static void ppc_spapr_init(MachineState *machine) qemu_register_boot_set(spapr_boot_set, spapr); - /* to stop and start vmclock */ if (kvm_enabled()) { + /* to stop and start vmclock */ qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change, &spapr->tb); + + kvmppc_spapr_enable_inkernel_multitce(); } } @@ -3030,21 +3084,23 @@ static void spapr_ics_resend(XICSFabric *dev) ics_resend(spapr->ics); } -static ICPState *spapr_icp_get(XICSFabric *xi, int server) +static ICPState *spapr_icp_get(XICSFabric *xi, int cpu_dt_id) { - sPAPRMachineState *spapr = SPAPR_MACHINE(xi); + PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); - return (server < spapr->nr_servers) ? &spapr->icps[server] : NULL; + return cpu ? ICP(cpu->intc) : NULL; } static void spapr_pic_print_info(InterruptStatsProvider *obj, Monitor *mon) { sPAPRMachineState *spapr = SPAPR_MACHINE(obj); - int i; + CPUState *cs; - for (i = 0; i < spapr->nr_servers; i++) { - icp_pic_print_info(&spapr->icps[i], mon); + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + icp_pic_print_info(ICP(cpu->intc), mon); } ics_pic_print_info(spapr->ics, mon); @@ -3158,18 +3214,37 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* + * pseries-2.10 + */ +static void spapr_machine_2_10_instance_options(MachineState *machine) +{ +} + +static void spapr_machine_2_10_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE(2_10, "2.10", true); + +/* * pseries-2.9 */ +#define SPAPR_COMPAT_2_9 \ + HW_COMPAT_2_9 + static void spapr_machine_2_9_instance_options(MachineState *machine) { + spapr_machine_2_10_instance_options(machine); } static void spapr_machine_2_9_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_2_10_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_9); } -DEFINE_SPAPR_MACHINE(2_9, "2.9", true); +DEFINE_SPAPR_MACHINE(2_9, "2.9", false); /* * pseries-2.8 diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 6883f0991a..4389ef4c2a 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -80,8 +80,6 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, } } - xics_cpu_setup(XICS_FABRIC(spapr), cpu); - qemu_register_reset(spapr_cpu_reset, cpu); spapr_cpu_reset(cpu); } @@ -129,6 +127,7 @@ static void spapr_cpu_core_unrealizefn(DeviceState *dev, Error **errp) PowerPCCPU *cpu = POWERPC_CPU(cs); spapr_cpu_destroy(cpu); + object_unparent(cpu->intc); cpu_remove_sync(cs); object_unparent(obj); } @@ -141,18 +140,32 @@ static void spapr_cpu_core_realize_child(Object *child, Error **errp) sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); CPUState *cs = CPU(child); PowerPCCPU *cpu = POWERPC_CPU(cs); + Object *obj; + + obj = object_new(spapr->icp_type); + object_property_add_child(OBJECT(cpu), "icp", obj, NULL); + object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort); + object_property_set_bool(obj, true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } object_property_set_bool(child, true, "realized", &local_err); if (local_err) { + object_unparent(obj); error_propagate(errp, local_err); return; } spapr_cpu_init(spapr, cpu, &local_err); if (local_err) { + object_unparent(obj); error_propagate(errp, local_err); return; } + + xics_cpu_setup(XICS_FABRIC(spapr), cpu, ICP(obj)); } static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 24a5758e62..f0b28d8112 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -422,7 +422,7 @@ static void spapr_init_maina(struct rtas_event_log_v6_maina *maina, maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA); maina->hdr.section_length = cpu_to_be16(sizeof(*maina)); /* FIXME: section version, subtype and creator id? */ - spapr_rtc_read(spapr->rtc, &tm, NULL); + spapr_rtc_read(&spapr->rtc, &tm, NULL); year = tm.tm_year + 1900; maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24) | (to_bcd(year % 100) << 16) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index f05a90ed2c..9f18f75b88 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -12,6 +12,8 @@ #include "trace.h" #include "kvm_ppc.h" #include "hw/ppc/spapr_ovec.h" +#include "qemu/error-report.h" +#include "mmu-book3s-v3.h" struct SPRSyncState { int spr; @@ -878,6 +880,137 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } +static target_ulong h_clean_slb(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", + opcode, " (H_CLEAN_SLB)"); + return H_FUNCTION; +} + +static target_ulong h_invalidate_pid(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", + opcode, " (H_INVALIDATE_PID)"); + return H_FUNCTION; +} + +static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr, + uint64_t patbe_old, uint64_t patbe_new) +{ + /* + * We have 4 Options: + * HASH->HASH || RADIX->RADIX || NOTHING->RADIX : Do Nothing + * HASH->RADIX : Free HPT + * RADIX->HASH : Allocate HPT + * NOTHING->HASH : Allocate HPT + * Note: NOTHING implies the case where we said the guest could choose + * later and so assumed radix and now it's called H_REG_PROC_TBL + */ + + if ((patbe_old & PATBE1_GR) == (patbe_new & PATBE1_GR)) { + /* We assume RADIX, so this catches all the "Do Nothing" cases */ + } else if (!(patbe_old & PATBE1_GR)) { + /* HASH->RADIX : Free HPT */ + g_free(spapr->htab); + spapr->htab = NULL; + spapr->htab_shift = 0; + close_htab_fd(spapr); + } else if (!(patbe_new & PATBE1_GR)) { + /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ + spapr_setup_hpt_and_vrma(spapr); + } + return; +} + +#define FLAGS_MASK 0x01FULL +#define FLAG_MODIFY 0x10 +#define FLAG_REGISTER 0x08 +#define FLAG_RADIX 0x04 +#define FLAG_HASH_PROC_TBL 0x02 +#define FLAG_GTSE 0x01 + +static target_ulong h_register_process_table(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong proc_tbl = args[1]; + target_ulong page_size = args[2]; + target_ulong table_size = args[3]; + uint64_t cproc; + + if (flags & ~FLAGS_MASK) { /* Check no reserved bits are set */ + return H_PARAMETER; + } + if (flags & FLAG_MODIFY) { + if (flags & FLAG_REGISTER) { + if (flags & FLAG_RADIX) { /* Register new RADIX process table */ + if (proc_tbl & 0xfff || proc_tbl >> 60) { + return H_P2; + } else if (page_size) { + return H_P3; + } else if (table_size > 24) { + return H_P4; + } + cproc = PATBE1_GR | proc_tbl | table_size; + } else { /* Register new HPT process table */ + if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */ + /* TODO - Not Supported */ + /* Technically caused by flag bits => H_PARAMETER */ + return H_PARAMETER; + } else { /* Hash with SLB */ + if (proc_tbl >> 38) { + return H_P2; + } else if (page_size & ~0x7) { + return H_P3; + } else if (table_size > 24) { + return H_P4; + } + } + cproc = (proc_tbl << 25) | page_size << 5 | table_size; + } + + } else { /* Deregister current process table */ + /* Set to benign value: (current GR) | 0. This allows + * deregistration in KVM to succeed even if the radix bit in flags + * doesn't match the radix bit in the old PATB. */ + cproc = spapr->patb_entry & PATBE1_GR; + } + } else { /* Maintain current registration */ + if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATBE1_GR)) { + /* Technically caused by flag bits => H_PARAMETER */ + return H_PARAMETER; /* Existing Process Table Mismatch */ + } + cproc = spapr->patb_entry; + } + + /* Check if we need to setup OR free the hpt */ + spapr_check_setup_free_hpt(spapr, spapr->patb_entry, cproc); + + spapr->patb_entry = cproc; /* Save new process table */ + if ((flags & FLAG_RADIX) || (flags & FLAG_HASH_PROC_TBL)) { + /* Use Process TBL */ + env->spr[SPR_LPCR] |= LPCR_UPRT; + } else { + env->spr[SPR_LPCR] &= ~LPCR_UPRT; + } + if (flags & FLAG_GTSE) { /* Partition Uses Guest Translation Shootdwn */ + env->spr[SPR_LPCR] |= LPCR_GTSE; + } else { + env->spr[SPR_LPCR] &= ~LPCR_GTSE; + } + + if (kvm_enabled()) { + return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX, + flags & FLAG_GTSE, cproc); + } + return H_SUCCESS; +} + #define H_SIGNAL_SYS_RESET_ALL -1 #define H_SIGNAL_SYS_RESET_ALLBUTSELF -2 @@ -929,7 +1062,8 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, uint32_t max_compat = cpu->max_compat; uint32_t best_compat = 0; int i; - sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates; + sPAPROptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; + bool guest_radix; /* * We scan the supplied table of PVRs looking for two things @@ -980,7 +1114,15 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, /* For the future use: here @ov_table points to the first option vector */ ov_table = list; + ov1_guest = spapr_ovec_parse_vector(ov_table, 1); ov5_guest = spapr_ovec_parse_vector(ov_table, 5); + if (spapr_ovec_test(ov5_guest, OV5_MMU_BOTH)) { + error_report("guest requested hash and radix MMU, which is invalid."); + exit(EXIT_FAILURE); + } + /* The radix/hash bit in byte 24 requires special handling: */ + guest_radix = spapr_ovec_test(ov5_guest, OV5_MMU_RADIX_300); + spapr_ovec_clear(ov5_guest, OV5_MMU_RADIX_300); /* NOTE: there are actually a number of ov5 bits where input from the * guest is always zero, and the platform/QEMU enables them independently @@ -999,7 +1141,23 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, ov5_updates = spapr_ovec_new(); spapr->cas_reboot = spapr_ovec_diff(ov5_updates, ov5_cas_old, spapr->ov5_cas); - + /* Now that processing is finished, set the radix/hash bit for the + * guest if it requested a valid mode; otherwise terminate the boot. */ + if (guest_radix) { + if (kvm_enabled() && !kvmppc_has_cap_mmu_radix()) { + error_report("Guest requested unavailable MMU mode (radix)."); + exit(EXIT_FAILURE); + } + spapr_ovec_set(spapr->ov5_cas, OV5_MMU_RADIX_300); + } else { + if (kvm_enabled() && kvmppc_has_cap_mmu_radix() + && !kvmppc_has_cap_mmu_hash_v3()) { + error_report("Guest requested unavailable MMU mode (hash)."); + exit(EXIT_FAILURE); + } + } + spapr->cas_legacy_guest_workaround = !spapr_ovec_test(ov1_guest, + OV1_PPC_3_00); if (!spapr->cas_reboot) { spapr->cas_reboot = (spapr_h_cas_compose_response(spapr, args[1], args[2], @@ -1009,6 +1167,13 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, if (spapr->cas_reboot) { qemu_system_reset_request(); + } else { + /* If ppc_spapr_reset() did not set up a HPT but one is necessary + * (because the guest isn't going to use radix) then set it up here. */ + if ((spapr->patb_entry & PATBE1_GR) && !guest_radix) { + /* legacy hash or new hash: */ + spapr_setup_hpt_and_vrma(spapr); + } } return H_SUCCESS; @@ -1084,6 +1249,11 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_PAGE_INIT, h_page_init); spapr_register_hypercall(H_SET_MODE, h_set_mode); + /* In Memory Table MMU h-calls */ + spapr_register_hypercall(H_CLEAN_SLB, h_clean_slb); + spapr_register_hypercall(H_INVALIDATE_PID, h_invalidate_pid); + spapr_register_hypercall(H_REGISTER_PROC_TBL, h_register_process_table); + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate * here between the "CI" and the "CACHE" variants, they will use whatever * mapping attributes qemu is using. When using KVM, the kernel will diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index ae30bbe30f..29c80bb3c8 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -79,15 +79,16 @@ static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce) static uint64_t *spapr_tce_alloc_table(uint32_t liobn, uint32_t page_shift, + uint64_t bus_offset, uint32_t nb_table, int *fd, bool need_vfio) { uint64_t *table = NULL; - uint64_t window_size = (uint64_t)nb_table << page_shift; - if (kvm_enabled() && !(window_size >> 32)) { - table = kvmppc_create_spapr_tce(liobn, window_size, fd, need_vfio); + if (kvm_enabled()) { + table = kvmppc_create_spapr_tce(liobn, page_shift, bus_offset, nb_table, + fd, need_vfio); } if (!table) { @@ -342,6 +343,7 @@ void spapr_tce_table_enable(sPAPRTCETable *tcet, tcet->nb_table = nb_table; tcet->table = spapr_tce_alloc_table(tcet->liobn, tcet->page_shift, + tcet->bus_offset, tcet->nb_table, &tcet->fd, tcet->need_vfio); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 98c52e411f..e7567e2e8f 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -50,8 +50,6 @@ #include "sysemu/hostmem.h" #include "sysemu/numa.h" -#include "hw/vfio/vfio.h" - /* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ #define RTAS_QUERY_FN 0 #define RTAS_CHANGE_FN 1 @@ -1771,6 +1769,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } /* DMA setup */ + if ((sphb->page_size_mask & qemu_getrampagesize()) == 0) { + error_report("System page size 0x%lx is not enabled in page_size_mask " + "(0x%"PRIx64"). Performance may be slow", + qemu_getrampagesize(), sphb->page_size_mask); + } + for (i = 0; i < windows_supported; ++i) { tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]); if (!tcet) { diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index 3a17ac42e4..00a4e4c717 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -33,19 +33,8 @@ #include "qapi-event.h" #include "qemu/cutils.h" -#define SPAPR_RTC(obj) \ - OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) - -typedef struct sPAPRRTCState sPAPRRTCState; -struct sPAPRRTCState { - /*< private >*/ - SysBusDevice parent_obj; - int64_t ns_offset; -}; - -void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns) +void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns) { - sPAPRRTCState *rtc = SPAPR_RTC(dev); int64_t host_ns = qemu_clock_get_ns(rtc_clock); int64_t guest_ns; time_t guest_s; @@ -63,16 +52,12 @@ void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns) } } -int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset) +int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset) { - sPAPRRTCState *rtc; - - if (!dev) { + if (!rtc) { return -ENODEV; } - rtc = SPAPR_RTC(dev); - rtc->ns_offset = legacy_offset * NANOSECONDS_PER_SECOND; return 0; @@ -91,12 +76,7 @@ static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - if (!spapr->rtc) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - spapr_rtc_read(spapr->rtc, &tm, &ns); + spapr_rtc_read(&spapr->rtc, &tm, &ns); rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, tm.tm_year + 1900); @@ -113,7 +93,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRRTCState *rtc; + sPAPRRTCState *rtc = &spapr->rtc; struct tm tm; time_t new_s; int64_t host_ns; @@ -123,11 +103,6 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - if (!spapr->rtc) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - tm.tm_year = rtas_ld(args, 0) - 1900; tm.tm_mon = rtas_ld(args, 1) - 1; tm.tm_mday = rtas_ld(args, 2); @@ -144,8 +119,6 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, /* Generate a monitor event for the change */ qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); - rtc = SPAPR_RTC(spapr->rtc); - host_ns = qemu_clock_get_ns(rtc_clock); rtc->ns_offset = (new_s * NANOSECONDS_PER_SECOND) - host_ns; @@ -155,7 +128,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, static void spapr_rtc_qom_date(Object *obj, struct tm *current_tm, Error **errp) { - spapr_rtc_read(DEVICE(obj), current_tm, NULL); + spapr_rtc_read(SPAPR_RTC(obj), current_tm, NULL); } static void spapr_rtc_realize(DeviceState *dev, Error **errp) @@ -200,7 +173,7 @@ static void spapr_rtc_class_init(ObjectClass *oc, void *data) static const TypeInfo spapr_rtc_info = { .name = TYPE_SPAPR_RTC, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DEVICE, .instance_size = sizeof(sPAPRRTCState), .class_init = spapr_rtc_class_init, }; diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 28ea20440e..fb8d640a7e 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -11,11 +11,51 @@ #include "qemu/osdep.h" #include "ccw-device.h" +static void ccw_device_refill_ids(CcwDevice *dev) +{ + SubchDev *sch = dev->sch; + + assert(sch); + + dev->dev_id.cssid = sch->cssid; + dev->dev_id.ssid = sch->ssid; + dev->dev_id.devid = sch->devno; + dev->dev_id.valid = true; + + dev->subch_id.cssid = sch->cssid; + dev->subch_id.ssid = sch->ssid; + dev->subch_id.devid = sch->schid; + dev->subch_id.valid = true; +} + +static void ccw_device_realize(CcwDevice *dev, Error **errp) +{ + ccw_device_refill_ids(dev); +} + +static Property ccw_device_properties[] = { + DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno), + DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id), + DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ccw_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + CCWDeviceClass *k = CCW_DEVICE_CLASS(klass); + + k->realize = ccw_device_realize; + k->refill_ids = ccw_device_refill_ids; + dc->props = ccw_device_properties; +} + static const TypeInfo ccw_device_info = { .name = TYPE_CCW_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(CcwDevice), .class_size = sizeof(CCWDeviceClass), + .class_init = ccw_device_class_init, .abstract = true, }; diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h index 59ba01b6c5..89c8e5dff7 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h @@ -19,12 +19,19 @@ typedef struct CcwDevice { DeviceState parent_obj; SubchDev *sch; /* <cssid>.<ssid>.<device number> */ - CssDevId bus_id; + /* The user-set busid of the virtual ccw device. */ + CssDevId devno; + /* The actual busid of the virtual ccw device. */ + CssDevId dev_id; + /* The actual busid of the virtual subchannel. */ + CssDevId subch_id; } CcwDevice; typedef struct CCWDeviceClass { DeviceClass parent_class; void (*unplug)(HotplugHandler *, DeviceState *, Error **); + void (*realize)(CcwDevice *, Error **); + void (*refill_ids)(CcwDevice *); } CCWDeviceClass; static inline CcwDevice *to_ccw_dev_fast(DeviceState *d) diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 9a7f7ee60c..b54ac01d37 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -107,6 +107,9 @@ VirtualCssBus *virtual_css_bus_init(void) /* Enable hotplugging */ qbus_set_hotplug_handler(bus, dev, &error_abort); + css_register_io_adapters(CSS_IO_ADAPTER_VIRTIO, true, false, + &error_abort); + return cbus; } diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 37caa98195..c03bb20bc9 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -47,7 +47,6 @@ typedef struct IoAdapter { uint32_t id; uint8_t type; uint8_t isc; - QTAILQ_ENTRY(IoAdapter) sibling; } IoAdapter; typedef struct ChannelSubSys { @@ -61,7 +60,7 @@ typedef struct ChannelSubSys { uint64_t chnmon_area; CssImage *css[MAX_CSSID + 1]; uint8_t default_cssid; - QTAILQ_HEAD(, IoAdapter) io_adapters; + IoAdapter *io_adapters[CSS_IO_ADAPTER_TYPE_NUMS][MAX_ISC + 1]; QTAILQ_HEAD(, IndAddr) indicator_addresses; } ChannelSubSys; @@ -72,7 +71,6 @@ static ChannelSubSys channel_subsys = { .do_crw_mchk = true, .crws_lost = false, .chnmon_active = false, - .io_adapters = QTAILQ_HEAD_INITIALIZER(channel_subsys.io_adapters), .indicator_addresses = QTAILQ_HEAD_INITIALIZER(channel_subsys.indicator_addresses), }; @@ -155,44 +153,67 @@ int css_create_css_image(uint8_t cssid, bool default_image) return 0; } -int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, - bool maskable, uint32_t *id) +uint32_t css_get_adapter_id(CssIoAdapterType type, uint8_t isc) { + if (type >= CSS_IO_ADAPTER_TYPE_NUMS || isc > MAX_ISC || + !channel_subsys.io_adapters[type][isc]) { + return -1; + } + + return channel_subsys.io_adapters[type][isc]->id; +} + +/** + * css_register_io_adapters: Register I/O adapters per ISC during init + * + * @swap: an indication if byte swap is needed. + * @maskable: an indication if the adapter is subject to the mask operation. + * @errp: location to store error information. + */ +void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable, + Error **errp) +{ + uint32_t id; + int ret, isc; IoAdapter *adapter; - bool found = false; - int ret; S390FLICState *fs = s390_get_flic(); S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); - *id = 0; - QTAILQ_FOREACH(adapter, &channel_subsys.io_adapters, sibling) { - if ((adapter->type == type) && (adapter->isc == isc)) { - *id = adapter->id; - found = true; - ret = 0; + /* + * Disallow multiple registrations for the same device type. + * Report an error if registering for an already registered type. + */ + if (channel_subsys.io_adapters[type][0]) { + error_setg(errp, "Adapters for type %d already registered", type); + } + + for (isc = 0; isc <= MAX_ISC; isc++) { + id = (type << 3) | isc; + ret = fsc->register_io_adapter(fs, id, isc, swap, maskable); + if (ret == 0) { + adapter = g_new0(IoAdapter, 1); + adapter->id = id; + adapter->isc = isc; + adapter->type = type; + channel_subsys.io_adapters[type][isc] = adapter; + } else { + error_setg_errno(errp, -ret, "Unexpected error %d when " + "registering adapter %d", ret, id); break; } - if (adapter->id >= *id) { - *id = adapter->id + 1; - } - } - if (found) { - goto out; } - adapter = g_new0(IoAdapter, 1); - ret = fsc->register_io_adapter(fs, *id, isc, swap, maskable); - if (ret == 0) { - adapter->id = *id; - adapter->isc = isc; - adapter->type = type; - QTAILQ_INSERT_TAIL(&channel_subsys.io_adapters, adapter, sibling); - } else { - g_free(adapter); - fprintf(stderr, "Unexpected error %d when registering adapter %d\n", - ret, *id); + + /* + * No need to free registered adapters in kvm: kvm will clean up + * when the machine goes away. + */ + if (ret) { + for (isc--; isc >= 0; isc--) { + g_free(channel_subsys.io_adapters[type][isc]); + channel_subsys.io_adapters[type][isc] = NULL; + } } -out: - return ret; + } static void css_clear_io_interrupt(uint16_t subchannel_id, @@ -1894,6 +1915,13 @@ PropertyInfo css_devid_propinfo = { .set = set_css_devid, }; +PropertyInfo css_devid_ro_propinfo = { + .name = "str", + .description = "Read-only identifier of an I/O device in the channel " + "subsystem, example: fe.1.23ab", + .get = get_css_devid, +}; + SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp) { uint16_t schid = 0; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 7978c7d52a..75d3c681a4 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -17,8 +17,10 @@ #include "cpu.h" #include "elf.h" #include "hw/loader.h" +#include "hw/boards.h" #include "hw/s390x/virtio-ccw.h" #include "hw/s390x/css.h" +#include "hw/s390x/ebcdic.h" #include "ipl.h" #include "qemu/error-report.h" @@ -243,12 +245,17 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) ipl->iplb.pbt = S390_IPL_TYPE_CCW; ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; - return true; } else if (sd) { SCSIBus *bus = scsi_bus_from_device(sd); VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev); - CcwDevice *ccw_dev = CCW_DEVICE(scsi_ccw); + CcwDevice *ccw_dev; + + ccw_dev = (CcwDevice *)object_dynamic_cast(OBJECT(scsi_ccw), + TYPE_CCW_DEVICE); + if (!ccw_dev) { /* It might be a PCI device instead */ + return false; + } ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); ipl->iplb.blk0_len = @@ -259,13 +266,39 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; - return true; + } else { + return false; /* unknown device */ } + + if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) { + ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID; + } + return true; } return false; } +int s390_ipl_set_loadparm(uint8_t *loadparm) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + char *lp = object_property_get_str(OBJECT(machine), "loadparm", NULL); + + if (lp) { + int i; + + /* lp is an uppercase string without leading/embedded spaces */ + for (i = 0; i < 8 && lp[i]; i++) { + loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]]; + } + + g_free(lp); + return 0; + } + + return -1; +} + static int load_netboot_image(Error **errp) { S390IPLState *ipl = get_ipl_device(); diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 46930e4c64..8a705e0428 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -57,6 +57,8 @@ struct IplBlockQemuScsi { } QEMU_PACKED; typedef struct IplBlockQemuScsi IplBlockQemuScsi; +#define DIAG308_FLAGS_LP_VALID 0x80 + union IplParameterBlock { struct { uint32_t len; @@ -82,6 +84,7 @@ union IplParameterBlock { } QEMU_PACKED; typedef union IplParameterBlock IplParameterBlock; +int s390_ipl_set_loadparm(uint8_t *loadparm); void s390_ipl_update_diag308(IplParameterBlock *iplb); void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 69b0291e8a..a8a1bab50a 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -23,15 +23,17 @@ #include "hw/pci/msi.h" #include "qemu/error-report.h" -/* #define DEBUG_S390PCI_BUS */ -#ifdef DEBUG_S390PCI_BUS -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "S390pci-bus: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) +#ifndef DEBUG_S390PCI_BUS +#define DEBUG_S390PCI_BUS 0 #endif +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_S390PCI_BUS) { \ + fprintf(stderr, "S390pci-bus: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + S390pciState *s390_get_phb(void) { static S390pciState *phb; @@ -579,6 +581,9 @@ static int s390_pcihost_init(SysBusDevice *dev) s->bus_no = 0; QTAILQ_INIT(&s->pending_sei); QTAILQ_INIT(&s->zpci_devs); + + css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false, &error_abort); + return 0; } diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index dcbf4820c9..cf142a3e68 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -30,7 +30,6 @@ #define FH_MASK_INDEX 0x0000ffff #define FH_SHM_VFIO 0x00010000 #define FH_SHM_EMUL 0x00020000 -#define S390_PCIPT_ADAPTER 2 #define ZPCI_MAX_FID 0xffffffff #define ZPCI_MAX_UID 0xffff #define UID_UNDEFINED 0 diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index d2a8c0a083..314a9cbad4 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -20,15 +20,17 @@ #include "qemu/error-report.h" #include "sysemu/hw_accel.h" -/* #define DEBUG_S390PCI_INST */ -#ifdef DEBUG_S390PCI_INST -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "s390pci-inst: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) +#ifndef DEBUG_S390PCI_INST +#define DEBUG_S390PCI_INST 0 #endif +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_S390PCI_INST) { \ + fprintf(stderr, "s390pci-inst: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + static void s390_set_status_code(CPUS390XState *env, uint8_t r, uint64_t status_code) { @@ -731,12 +733,10 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) { int ret, len; + uint8_t isc = FIB_DATA_ISC(ldl_p(&fib.data)); - ret = css_register_io_adapter(S390_PCIPT_ADAPTER, - FIB_DATA_ISC(ldl_p(&fib.data)), true, false, - &pbdev->routes.adapter.adapter_id); - assert(ret == 0); - + pbdev->routes.adapter.adapter_id = css_get_adapter_id( + CSS_IO_ADAPTER_PCI, isc); pbdev->summary_ind = get_indicator(ldq_p(&fib.aisb), sizeof(uint64_t)); len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long); pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len); @@ -755,7 +755,7 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data)); pbdev->routes.adapter.ind_addr = ldq_p(&fib.aibv); pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_p(&fib.data)); - pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data)); + pbdev->isc = isc; pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data)); pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data)); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 40914fde6f..fdd4384ff0 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -113,12 +113,13 @@ static void ccw_init(MachineState *machine) s390_sclp_init(); s390_memory_init(machine->ram_size); + s390_flic_init(); + /* get a BUS */ css_bus = virtual_css_bus_init(); s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, machine->initrd_filename, "s390-ccw.img", "s390-netboot.img", true); - s390_flic_init(); dev = qdev_create(NULL, TYPE_S390_PCI_HOST_BRIDGE); object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE, @@ -273,6 +274,36 @@ bool cpu_model_allowed(void) return true; } +static char *machine_get_loadparm(Object *obj, Error **errp) +{ + S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + + return g_memdup(ms->loadparm, sizeof(ms->loadparm)); +} + +static void machine_set_loadparm(Object *obj, const char *val, Error **errp) +{ + S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + int i; + + for (i = 0; i < sizeof(ms->loadparm) && val[i]; i++) { + uint8_t c = toupper(val[i]); /* mimic HMC */ + + if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '.') || + (c == ' ')) { + ms->loadparm[i] = c; + } else { + error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)", + c, c); + return; + } + } + + for (; i < sizeof(ms->loadparm); i++) { + ms->loadparm[i] = ' '; /* pad right with spaces */ + } +} + static inline void s390_machine_initfn(Object *obj) { object_property_add_bool(obj, "aes-key-wrap", @@ -290,6 +321,13 @@ static inline void s390_machine_initfn(Object *obj) "enable/disable DEA key wrapping using the CPACF wrapping key", NULL); object_property_set_bool(obj, true, "dea-key-wrap", NULL); + object_property_add_str(obj, "loadparm", + machine_get_loadparm, machine_set_loadparm, NULL); + object_property_set_description(obj, "loadparm", + "Up to 8 chars in set of [A-Za-z0-9. ] (lower case chars converted" + " to upper case) to pass to machine loader, boot manager," + " and guest kernel", + NULL); } static const TypeInfo ccw_machine_info = { @@ -336,6 +374,9 @@ static const TypeInfo ccw_machine_info = { } \ type_init(ccw_machine_register_##suffix) +#define CCW_COMPAT_2_9 \ + HW_COMPAT_2_9 + #define CCW_COMPAT_2_8 \ HW_COMPAT_2_8 \ {\ @@ -402,14 +443,26 @@ static const TypeInfo ccw_machine_info = { .value = "0",\ }, +static void ccw_machine_2_10_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_2_10_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(2_10, "2.10", true); + static void ccw_machine_2_9_instance_options(MachineState *machine) { + ccw_machine_2_10_instance_options(machine); } static void ccw_machine_2_9_class_options(MachineClass *mc) { + ccw_machine_2_10_class_options(mc); + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_9); } -DEFINE_CCW_MACHINE(2_9, "2.9", true); +DEFINE_CCW_MACHINE(2_9, "2.9", false); static void ccw_machine_2_8_instance_options(MachineState *machine) { diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index e741da1141..b4f6dd58dd 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -23,6 +23,7 @@ #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" #include "hw/s390x/s390-pci-bus.h" +#include "hw/s390x/ipl.h" static inline SCLPDevice *get_sclp_device(void) { @@ -57,6 +58,7 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) int cpu_count = 0; int rnsize, rnmax; int slots = MIN(machine->ram_slots, s390_get_memslot_count(kvm_state)); + IplParameterBlock *ipib = s390_ipl_get_iplb(); CPU_FOREACH(cpu) { cpu_count++; @@ -129,6 +131,13 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->rnmax2 = cpu_to_be64(rnmax); } + if (ipib && ipib->flags & DIAG308_FLAGS_LP_VALID) { + memcpy(&read_info->loadparm, &ipib->loadparm, + sizeof(read_info->loadparm)); + } else { + s390_ipl_set_loadparm(read_info->loadparm); + } + sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 00b3bde4e9..e7167e3d05 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -616,10 +616,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) dev->routes.adapter.ind_offset = ind_bit; dev->routes.adapter.summary_offset = 7; cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); - ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, - dev->thinint_isc, true, false, - &dev->routes.adapter.adapter_id); - assert(ret == 0); + dev->routes.adapter.adapter_id = css_get_adapter_id( + CSS_IO_ADAPTER_VIRTIO, + dev->thinint_isc); sch->thinint_active = ((dev->indicators != NULL) && (dev->summary_indicator != NULL)); sch->curr_status.scsw.count = ccw.count - len; @@ -680,7 +679,8 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) { VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); CcwDevice *ccw_dev = CCW_DEVICE(dev); - SubchDev *sch = css_create_virtual_sch(ccw_dev->bus_id, errp); + CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); + SubchDev *sch = css_create_virtual_sch(ccw_dev->devno, errp); Error *err = NULL; if (!sch) { @@ -689,8 +689,7 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) if (!virtio_ccw_rev_max(dev) && dev->force_revision_1) { error_setg(&err, "Invalid value of property max_rev " "(is %d expected >= 1)", virtio_ccw_rev_max(dev)); - error_propagate(errp, err); - return; + goto out_err; } sch->driver_data = dev; @@ -705,7 +704,7 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) trace_virtio_ccw_new_device( sch->cssid, sch->ssid, sch->schid, sch->devno, - ccw_dev->bus_id.valid ? "user-configured" : "auto-configured"); + ccw_dev->devno.valid ? "user-configured" : "auto-configured"); if (!kvm_eventfds_enabled()) { dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; @@ -713,13 +712,23 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) if (k->realize) { k->realize(dev, &err); + if (err) { + goto out_err; + } } + + ck->realize(ccw_dev, &err); if (err) { - error_propagate(errp, err); - css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); - ccw_dev->sch = NULL; - g_free(sch); + goto out_err; } + + return; + +out_err: + error_propagate(errp, err); + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + ccw_dev->sch = NULL; + g_free(sch); } static int virtio_ccw_exit(VirtioCcwDevice *dev) @@ -1261,12 +1270,17 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); CcwDevice *ccw_dev = CCW_DEVICE(d); + CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); SubchDev *s = ccw_dev->sch; VirtIODevice *vdev = virtio_ccw_get_vdev(s); int len; s->driver_data = dev; subch_device_load(s, f); + /* Re-fill subch_id after loading the subchannel states.*/ + if (ck->refill_ids) { + ck->refill_ids(ccw_dev); + } len = qemu_get_be32(f); if (len != 0) { dev->indicators = get_indicator(qemu_get_be64(f), len); @@ -1293,9 +1307,9 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) dev->thinint_isc = qemu_get_byte(f); dev->revision = qemu_get_be32(f); if (s->thinint_active) { - return css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, - dev->thinint_isc, true, false, - &dev->routes.adapter.adapter_id); + dev->routes.adapter.adapter_id = css_get_adapter_id( + CSS_IO_ADAPTER_VIRTIO, + dev->thinint_isc); } return 0; @@ -1354,7 +1368,6 @@ static void virtio_ccw_device_unplugged(DeviceState *d) /**************** Virtio-ccw Bus Device Descriptions *******************/ static Property virtio_ccw_net_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1383,7 +1396,6 @@ static const TypeInfo virtio_ccw_net = { }; static Property virtio_ccw_blk_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1412,7 +1424,6 @@ static const TypeInfo virtio_ccw_blk = { }; static Property virtio_ccw_serial_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1441,7 +1452,6 @@ static const TypeInfo virtio_ccw_serial = { }; static Property virtio_ccw_balloon_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1470,7 +1480,6 @@ static const TypeInfo virtio_ccw_balloon = { }; static Property virtio_ccw_scsi_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1500,7 +1509,6 @@ static const TypeInfo virtio_ccw_scsi = { #ifdef CONFIG_VHOST_SCSI static Property vhost_ccw_scsi_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), @@ -1538,7 +1546,6 @@ static void virtio_ccw_rng_instance_init(Object *obj) } static Property virtio_ccw_rng_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1567,7 +1574,6 @@ static const TypeInfo virtio_ccw_rng = { }; static Property virtio_ccw_crypto_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1694,7 +1700,6 @@ static const TypeInfo virtio_ccw_bus_info = { #ifdef CONFIG_VIRTFS static Property virtio_ccw_9p_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1743,7 +1748,6 @@ static const TypeInfo virtio_ccw_9p_info = { #ifdef CONFIG_VHOST_VSOCK static Property vhost_vsock_ccw_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), @@ -1757,9 +1761,7 @@ static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + error_propagate(errp, err); } static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index c491ece1f2..f53bc179da 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -233,9 +233,11 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) } } - virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output, + virtio_scsi_common_realize(dev, vhost_dummy_handle_output, - vhost_dummy_handle_output); + vhost_dummy_handle_output, + vhost_dummy_handle_output, + &err); if (err != NULL) { error_propagate(errp, err); goto close_fd; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index bd62d08251..46a3e3f280 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -841,10 +841,11 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { .load_request = virtio_scsi_load_request, }; -void virtio_scsi_common_realize(DeviceState *dev, Error **errp, +void virtio_scsi_common_realize(DeviceState *dev, VirtIOHandleOutput ctrl, VirtIOHandleOutput evt, - VirtIOHandleOutput cmd) + VirtIOHandleOutput cmd, + Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev); @@ -878,9 +879,11 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) VirtIOSCSI *s = VIRTIO_SCSI(dev); Error *err = NULL; - virtio_scsi_common_realize(dev, &err, virtio_scsi_handle_ctrl, + virtio_scsi_common_realize(dev, + virtio_scsi_handle_ctrl, virtio_scsi_handle_event, - virtio_scsi_handle_cmd); + virtio_scsi_handle_cmd, + &err); if (err != NULL) { error_propagate(errp, err); return; diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 6d06968f8b..8f520cec1c 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -277,8 +277,15 @@ static void r2d_init(MachineState *machine) sysbus_connect_irq(busdev, 2, irq[PCI_INTC]); sysbus_connect_irq(busdev, 3, irq[PCI_INTD]); - sm501_init(address_space_mem, 0x10000000, SM501_VRAM_SIZE, - irq[SM501], serial_hds[2]); + dev = qdev_create(NULL, "sysbus-sm501"); + busdev = SYS_BUS_DEVICE(dev); + qdev_prop_set_uint32(dev, "vram-size", SM501_VRAM_SIZE); + qdev_prop_set_uint32(dev, "base", 0x10000000); + qdev_prop_set_ptr(dev, "chr-state", serial_hds[2]); + qdev_init_nofail(dev); + sysbus_mmio_map(busdev, 0, 0x10000000); + sysbus_mmio_map(busdev, 1, 0x13e00000); + sysbus_connect_irq(busdev, 0, irq[SM501]); /* onboard CF (True IDE mode, Master only). */ dinfo = drive_get(IF_IDE, 0, 0); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 873cd7df9a..5f022cc08d 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -491,7 +491,6 @@ static void tcx_init(hwaddr addr, qemu_irq irq, int vram_size, int width, qdev_prop_set_uint16(dev, "width", width); qdev_prop_set_uint16(dev, "height", height); qdev_prop_set_uint16(dev, "depth", depth); - qdev_prop_set_uint64(dev, "prom_addr", addr); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); @@ -544,7 +543,6 @@ static void cg3_init(hwaddr addr, qemu_irq irq, int vram_size, int width, qdev_prop_set_uint16(dev, "width", width); qdev_prop_set_uint16(dev, "height", height); qdev_prop_set_uint16(dev, "depth", depth); - qdev_prop_set_uint64(dev, "prom-addr", addr); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c index 0c189348ae..a2ec3920f8 100644 --- a/hw/timer/exynos4210_mct.c +++ b/hw/timer/exynos4210_mct.c @@ -53,6 +53,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu/main-loop.h" @@ -1372,8 +1373,9 @@ break; case L0_TCNTO: case L1_TCNTO: case L0_ICNTO: case L1_ICNTO: case L0_FRCNTO: case L1_FRCNTO: - fprintf(stderr, "\n[exynos4210.mct: write to RO register " - TARGET_FMT_plx "]\n\n", offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.mct: write to RO register " TARGET_FMT_plx, + offset); break; case L0_INT_CSTAT: case L1_INT_CSTAT: diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c index f5765075c7..87f63f057e 100644 --- a/hw/timer/exynos4210_pwm.c +++ b/hw/timer/exynos4210_pwm.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" @@ -252,9 +253,9 @@ static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset, break; default: - fprintf(stderr, - "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n", - offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.pwm: bad read offset " TARGET_FMT_plx, + offset); break; } return value; @@ -343,9 +344,9 @@ static void exynos4210_pwm_write(void *opaque, hwaddr offset, break; default: - fprintf(stderr, - "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n", - offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.pwm: bad write offset " TARGET_FMT_plx, + offset); break; } diff --git a/hw/timer/exynos4210_rtc.c b/hw/timer/exynos4210_rtc.c index 1a648c5d9e..4607833e3e 100644 --- a/hw/timer/exynos4210_rtc.c +++ b/hw/timer/exynos4210_rtc.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" @@ -370,9 +371,9 @@ static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset, break; default: - fprintf(stderr, - "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n", - offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.rtc: bad read offset " TARGET_FMT_plx, + offset); break; } return value; @@ -433,9 +434,9 @@ static void exynos4210_rtc_write(void *opaque, hwaddr offset, if (value > TICNT_THRESHOLD) { s->reg_ticcnt = value; } else { - fprintf(stderr, - "[exynos4210.rtc: bad TICNT value %u ]\n", - (uint32_t)value); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.rtc: bad TICNT value %u", + (uint32_t)value); } break; @@ -500,9 +501,9 @@ static void exynos4210_rtc_write(void *opaque, hwaddr offset, break; default: - fprintf(stderr, - "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n", - offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.rtc: bad write offset " TARGET_FMT_plx, + offset); break; } diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 24f1608b4b..5939b273b9 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -762,9 +762,7 @@ static void usb_set_attached(Object *obj, bool value, Error **errp) if (value) { usb_device_attach(dev, &err); - if (err) { - error_propagate(errp, err); - } + error_propagate(errp, err); } else { usb_device_detach(dev); } diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 8e676e6c96..370b3d9387 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -33,7 +33,7 @@ #include "qapi/qmp/qint.h" #include "qapi/qmp/qstring.h" -#include <xen/io/ring.h> +#include "hw/xen/io/ring.h" #include <xen/io/usbif.h> /* diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f3ba9b9007..a8f12eeb35 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -119,6 +119,9 @@ void vfio_region_write(void *opaque, hwaddr addr, case 4: buf.dword = cpu_to_le32(data); break; + case 8: + buf.qword = cpu_to_le64(data); + break; default: hw_error("vfio: unsupported write size, %d bytes", size); break; @@ -173,6 +176,9 @@ uint64_t vfio_region_read(void *opaque, case 4: data = le32_to_cpu(buf.dword); break; + case 8: + data = le64_to_cpu(buf.qword); + break; default: hw_error("vfio: unsupported read size, %d bytes", size); break; @@ -190,6 +196,14 @@ const MemoryRegionOps vfio_region_ops = { .read = vfio_region_read, .write = vfio_region_write, .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, }; /* @@ -478,8 +492,13 @@ static void vfio_listener_region_add(MemoryListener *listener, giommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; giommu->container = container; - giommu->n.notify = vfio_iommu_map_notify; - giommu->n.notifier_flags = IOMMU_NOTIFIER_ALL; + llend = int128_add(int128_make64(section->offset_within_region), + section->size); + llend = int128_sub(llend, int128_one()); + iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, + IOMMU_NOTIFIER_ALL, + section->offset_within_region, + int128_get64(llend)); QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); @@ -550,7 +569,8 @@ static void vfio_listener_region_del(MemoryListener *listener, VFIOGuestIOMMU *giommu; QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { - if (giommu->iommu == section->mr) { + if (giommu->iommu == section->mr && + giommu->n.start == section->offset_within_region) { memory_region_unregister_iommu_notifier(giommu->iommu, &giommu->n); QLIST_REMOVE(giommu, giommu_next); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 03a3d01549..32aca77701 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2625,8 +2625,8 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (!(~vdev->host.domain || ~vdev->host.bus || ~vdev->host.slot || ~vdev->host.function)) { error_setg(errp, "No provided host device"); - error_append_hint(errp, "Use -vfio-pci,host=DDDD:BB:DD.F " - "or -vfio-pci,sysfsdev=PATH_TO_DEVICE\n"); + error_append_hint(errp, "Use -device vfio-pci,host=DDDD:BB:DD.F " + "or -device vfio-pci,sysfsdev=PATH_TO_DEVICE\n"); return; } vdev->vbasedev.sysfsdev = diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 6926eedd3f..1f7a7c1ae1 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -11,8 +11,11 @@ virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u" # hw/virtio/virtio-rng.c virtio_rng_guest_not_ready(void *rng) "rng %p: guest not ready" +virtio_rng_cpu_is_stopped(void *rng, int size) "rng %p: cpu is stopped, dropping %d bytes" +virtio_rng_popped(void *rng) "rng %p: elem popped" virtio_rng_pushed(void *rng, size_t len) "rng %p: %zd bytes pushed" virtio_rng_request(void *rng, size_t size, unsigned quota) "rng %p: %zd bytes requested, %u bytes quota left" +virtio_rng_vm_state_change(void *rng, int running, int state) "rng %p: state change to running %d state %d" # hw/virtio/virtio-balloon.c # diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 613494dcc2..0001e60b77 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -736,14 +736,20 @@ static void vhost_iommu_region_add(MemoryListener *listener, struct vhost_dev *dev = container_of(listener, struct vhost_dev, iommu_listener); struct vhost_iommu *iommu; + Int128 end; if (!memory_region_is_iommu(section->mr)) { return; } iommu = g_malloc0(sizeof(*iommu)); - iommu->n.notify = vhost_iommu_unmap_notify; - iommu->n.notifier_flags = IOMMU_NOTIFIER_UNMAP; + end = int128_add(int128_make64(section->offset_within_region), + section->size); + end = int128_sub(end, int128_one()); + iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify, + IOMMU_NOTIFIER_UNMAP, + section->offset_within_region, + int128_get64(end)); iommu->mr = section->mr; iommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; @@ -765,7 +771,8 @@ static void vhost_iommu_region_del(MemoryListener *listener, } QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) { - if (iommu->mr == section->mr) { + if (iommu->mr == section->mr && + iommu->n.start == section->offset_within_region) { memory_region_unregister_iommu_notifier(iommu->mr, &iommu->n); QLIST_REMOVE(iommu, iommu_next); diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 9639f4e89b..a6ee501051 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -53,6 +53,15 @@ static void chr_read(void *opaque, const void *buf, size_t size) return; } + /* we can't modify the virtqueue until + * our state is fully synced + */ + + if (!runstate_check(RUN_STATE_RUNNING)) { + trace_virtio_rng_cpu_is_stopped(vrng, size); + return; + } + vrng->quota_remaining -= size; offset = 0; @@ -61,6 +70,7 @@ static void chr_read(void *opaque, const void *buf, size_t size) if (!elem) { break; } + trace_virtio_rng_popped(vrng); len = iov_from_buf(elem->in_sg, elem->in_num, 0, buf + offset, size - offset); offset += len; @@ -120,17 +130,21 @@ static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) return f; } -static int virtio_rng_post_load(void *opaque, int version_id) +static void virtio_rng_vm_state_change(void *opaque, int running, + RunState state) { VirtIORNG *vrng = opaque; + trace_virtio_rng_vm_state_change(vrng, running, state); + /* We may have an element ready but couldn't process it due to a quota - * limit. Make sure to try again after live migration when the quota may - * have been reset. + * limit or because CPU was stopped. Make sure to try again when the + * CPU restart. */ - virtio_rng_process(vrng); - return 0; + if (running && is_guest_ready(vrng)) { + virtio_rng_process(vrng); + } } static void check_rate_limit(void *opaque) @@ -198,6 +212,9 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp) vrng->rate_limit_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, check_rate_limit, vrng); vrng->activate_timer = true; + + vrng->vmstate = qemu_add_vm_change_state_handler(virtio_rng_vm_state_change, + vrng); } static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp) @@ -205,6 +222,7 @@ static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIORNG *vrng = VIRTIO_RNG(dev); + qemu_del_vm_change_state_handler(vrng->vmstate); timer_del(vrng->rate_limit_timer); timer_free(vrng->rate_limit_timer); virtio_cleanup(vdev); @@ -218,7 +236,6 @@ static const VMStateDescription vmstate_virtio_rng = { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, - .post_load = virtio_rng_post_load, }; static Property virtio_rng_properties[] = { diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs index 4be3ec9c77..64a70bc6cb 100644 --- a/hw/xen/Makefile.objs +++ b/hw/xen/Makefile.objs @@ -1,5 +1,5 @@ # xen backend driver support -common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o xen_pvdev.o +common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o xen_pvdev.o xen-common.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_graphics.o xen_pt_msi.o diff --git a/hw/xen/trace-events b/hw/xen/trace-events index c4fb6f1aea..5615dce2c1 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -11,3 +11,4 @@ xen_map_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: % xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64 xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x" xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x" +xen_domid_restrict(int err) "err: %u" diff --git a/xen-common.c b/hw/xen/xen-common.c index fd2c92847e..ae76150e8a 100644 --- a/xen-common.c +++ b/hw/xen/xen-common.c @@ -25,6 +25,10 @@ do { } while (0) #endif +xc_interface *xen_xc; +xenforeignmemory_handle *xen_fmem; +xendevicemodel_handle *xen_dmod; + static int store_dev_info(int domid, Chardev *cs, const char *string) { struct xs_handle *xs = NULL; @@ -125,6 +129,13 @@ static int xen_init(MachineState *ms) xc_interface_close(xen_xc); return -1; } + xen_dmod = xendevicemodel_open(0, 0); + if (xen_dmod == NULL) { + xen_pv_printf(NULL, 0, "can't open xen devicemodel interface\n"); + xenforeignmemory_close(xen_fmem); + xc_interface_close(xen_xc); + return -1; + } qemu_add_vm_change_state_handler(xen_change_state_handler, NULL); global_state_set_optional(); diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 6c21c37d68..c85f1637e4 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -43,8 +43,6 @@ BusState *xen_sysbus; /* ------------------------------------------------------------- */ /* public */ -xc_interface *xen_xc = NULL; -xenforeignmemory_handle *xen_fmem = NULL; struct xs_handle *xenstore = NULL; const char *xen_protocol; @@ -585,6 +583,9 @@ void xen_be_register_common(void) xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("qdisk", &xen_blkdev_ops); +#ifdef CONFIG_VIRTFS + xen_be_register("9pfs", &xen_9pfs_ops); +#endif #ifdef CONFIG_USB_LIBUSB xen_be_register("qusb", &xen_usb_ops); #endif diff --git a/include/block/block.h b/include/block/block.h index 5ddc0cf21b..862eb56fc7 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -294,7 +294,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, const char *backing_file); int bdrv_get_backing_file_depth(BlockDriverState *bs); void bdrv_refresh_filename(BlockDriverState *bs); -int bdrv_truncate(BdrvChild *child, int64_t offset); +int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp); int64_t bdrv_nb_sectors(BlockDriverState *bs); int64_t bdrv_getlength(BlockDriverState *bs); int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); @@ -434,6 +434,8 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int64_t sector_num, int nb_sectors, int *pnum); bool bdrv_is_read_only(BlockDriverState *bs); +int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); +int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs); int bdrv_media_changed(BlockDriverState *bs); @@ -509,7 +511,7 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, char *options, uint64_t img_size, int flags, - Error **errp, bool quiet); + bool quiet, Error **errp); /* Returns the alignment in bytes that is required so that no bounce buffer * is required throughout the stack */ diff --git a/include/block/block_int.h b/include/block/block_int.h index 59400bd848..87739405d5 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -196,7 +196,7 @@ struct BlockDriver { int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs); const char *protocol_name; - int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); + int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset, Error **errp); int64_t (*bdrv_getlength)(BlockDriverState *bs); bool has_variable_length; @@ -252,7 +252,7 @@ struct BlockDriver { * Returns 0 for completed check, -errno for internal errors. * The check results are stored in result. */ - int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result, + int (*bdrv_check)(BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix); int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, @@ -454,13 +454,13 @@ struct BdrvChildRole { /* Returns a name that is supposedly more useful for human users than the * node name for identifying the node in question (in particular, a BB * name), or NULL if the parent can't provide a better name. */ - const char* (*get_name)(BdrvChild *child); + const char *(*get_name)(BdrvChild *child); /* Returns a malloced string that describes the parent of the child for a * human reader. This could be a node-name, BlockBackend name, qdev ID or * QOM path of the device owning the BlockBackend, job type and ID etc. The * caller is responsible for freeing the memory. */ - char* (*get_parent_desc)(BdrvChild *child); + char *(*get_parent_desc)(BdrvChild *child); /* * If this pair of functions is implemented, the parent doesn't issue new @@ -805,16 +805,16 @@ void commit_start(const char *job_id, BlockDriverState *bs, * a node name should be autogenerated. * @cb: Completion function for the job. * @opaque: Opaque pointer value passed to @cb. - * @errp: Error object. * @auto_complete: Auto complete the job. + * @errp: Error object. * */ void commit_active_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, int creation_flags, int64_t speed, BlockdevOnError on_error, const char *filter_node_name, - BlockCompletionFunc *cb, void *opaque, Error **errp, - bool auto_complete); + BlockCompletionFunc *cb, void *opaque, + bool auto_complete, Error **errp); /* * mirror_start: * @job_id: The id of the newly-created job, or %NULL to use the diff --git a/include/crypto/block.h b/include/crypto/block.h index b6971de921..4a053a3ffa 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -30,23 +30,23 @@ typedef struct QCryptoBlock QCryptoBlock; * and QCryptoBlockOpenOptions in qapi/crypto.json */ typedef ssize_t (*QCryptoBlockReadFunc)(QCryptoBlock *block, + void *opaque, size_t offset, uint8_t *buf, size_t buflen, - Error **errp, - void *opaque); + Error **errp); typedef ssize_t (*QCryptoBlockInitFunc)(QCryptoBlock *block, + void *opaque, size_t headerlen, - Error **errp, - void *opaque); + Error **errp); typedef ssize_t (*QCryptoBlockWriteFunc)(QCryptoBlock *block, + void *opaque, size_t offset, const uint8_t *buf, size_t buflen, - Error **errp, - void *opaque); + Error **errp); /** * qcrypto_block_has_format: diff --git a/include/exec/memory.h b/include/exec/memory.h index f20b191793..99e0f54d86 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -55,6 +55,8 @@ typedef enum { IOMMU_RW = 3, } IOMMUAccessFlags; +#define IOMMU_ACCESS_FLAG(r, w) (((r) ? IOMMU_RO : 0) | ((w) ? IOMMU_WO : 0)) + struct IOMMUTLBEntry { AddressSpace *target_as; hwaddr iova; @@ -77,13 +79,30 @@ typedef enum { #define IOMMU_NOTIFIER_ALL (IOMMU_NOTIFIER_MAP | IOMMU_NOTIFIER_UNMAP) +struct IOMMUNotifier; +typedef void (*IOMMUNotify)(struct IOMMUNotifier *notifier, + IOMMUTLBEntry *data); + struct IOMMUNotifier { - void (*notify)(struct IOMMUNotifier *notifier, IOMMUTLBEntry *data); + IOMMUNotify notify; IOMMUNotifierFlag notifier_flags; + /* Notify for address space range start <= addr <= end */ + hwaddr start; + hwaddr end; QLIST_ENTRY(IOMMUNotifier) node; }; typedef struct IOMMUNotifier IOMMUNotifier; +static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, + IOMMUNotifierFlag flags, + hwaddr start, hwaddr end) +{ + n->notify = fn; + n->notifier_flags = flags; + n->start = start; + n->end = end; +} + /* New-style MMIO accessors can indicate that the transaction failed. * A zero (MEMTX_OK) response means success; anything else is a failure * of some kind. The memory subsystem will bitwise-OR together results @@ -174,6 +193,8 @@ struct MemoryRegionIOMMUOps { void (*notify_flag_changed)(MemoryRegion *iommu, IOMMUNotifierFlag old_flags, IOMMUNotifierFlag new_flags); + /* Set this up to provide customized IOMMU replay function */ + void (*replay)(MemoryRegion *iommu, IOMMUNotifier *notifier); }; typedef struct CoalescedMemoryRange CoalescedMemoryRange; @@ -222,6 +243,9 @@ struct MemoryRegion { IOMMUNotifierFlag iommu_notify_flags; }; +#define IOMMU_NOTIFIER_FOREACH(n, mr) \ + QLIST_FOREACH((n), &(mr)->iommu_notify, node) + /** * MemoryListener: callbacks structure for updates to the physical memory map * @@ -668,6 +692,21 @@ void memory_region_notify_iommu(MemoryRegion *mr, IOMMUTLBEntry entry); /** + * memory_region_notify_one: notify a change in an IOMMU translation + * entry to a single notifier + * + * This works just like memory_region_notify_iommu(), but it only + * notifies a specific notifier, not all of them. + * + * @notifier: the notifier to be notified + * @entry: the new entry in the IOMMU translation table. The entry + * replaces all old entries for the same virtual I/O address range. + * Deleted entries have .@perm == 0. + */ +void memory_region_notify_one(IOMMUNotifier *notifier, + IOMMUTLBEntry *entry); + +/** * memory_region_register_iommu_notifier: register a notifier for changes to * IOMMU translation entries. * @@ -693,6 +732,14 @@ void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n, bool is_write); /** + * memory_region_iommu_replay_all: replay existing IOMMU translations + * to all the notifiers registered. + * + * @mr: the memory region to observe + */ +void memory_region_iommu_replay_all(MemoryRegion *mr); + +/** * memory_region_unregister_iommu_notifier: unregister a notifier for * changes to IOMMU translation entries. * @@ -871,6 +918,53 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, */ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client); + +/** + * memory_region_snapshot_and_clear_dirty: Get a snapshot of the dirty + * bitmap and clear it. + * + * Creates a snapshot of the dirty bitmap, clears the dirty bitmap and + * returns the snapshot. The snapshot can then be used to query dirty + * status, using memory_region_snapshot_get_dirty. Unlike + * memory_region_test_and_clear_dirty this allows to query the same + * page multiple times, which is especially useful for display updates + * where the scanlines often are not page aligned. + * + * The dirty bitmap region which gets copyed into the snapshot (and + * cleared afterwards) can be larger than requested. The boundaries + * are rounded up/down so complete bitmap longs (covering 64 pages on + * 64bit hosts) can be copied over into the bitmap snapshot. Which + * isn't a problem for display updates as the extra pages are outside + * the visible area, and in case the visible area changes a full + * display redraw is due anyway. Should other use cases for this + * function emerge we might have to revisit this implementation + * detail. + * + * Use g_free to release DirtyBitmapSnapshot. + * + * @mr: the memory region being queried. + * @addr: the address (relative to the start of the region) being queried. + * @size: the size of the range being queried. + * @client: the user of the logging information; typically %DIRTY_MEMORY_VGA. + */ +DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, + hwaddr addr, + hwaddr size, + unsigned client); + +/** + * memory_region_snapshot_get_dirty: Check whether a range of bytes is dirty + * in the specified dirty bitmap snapshot. + * + * @mr: the memory region being queried. + * @snap: the dirty bitmap snapshot + * @addr: the address (relative to the start of the region) being queried. + * @size: the size of the range being queried. + */ +bool memory_region_snapshot_get_dirty(MemoryRegion *mr, + DirtyBitmapSnapshot *snap, + hwaddr addr, hwaddr size); + /** * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with * any external TLBs (e.g. kvm) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index b05dc84ab9..dbe2f08d47 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -53,7 +53,7 @@ static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset) } long qemu_getrampagesize(void); -ram_addr_t last_ram_offset(void); +unsigned long last_ram_page(void); RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, bool share, const char *mem_path, Error **errp); @@ -260,7 +260,7 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, rcu_read_unlock(); - xen_modified_memory(start, length); + xen_hvm_modified_memory(start, length); } #if !defined(_WIN32) @@ -314,7 +314,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, rcu_read_unlock(); - xen_modified_memory(start, pages << TARGET_PAGE_BITS); + xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); } else { uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; /* @@ -343,6 +343,13 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, unsigned client); +DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty + (ram_addr_t start, ram_addr_t length, unsigned client); + +bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, + ram_addr_t start, + ram_addr_t length); + static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, ram_addr_t length) { @@ -354,11 +361,13 @@ static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, static inline uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, + RAMBlock *rb, ram_addr_t start, ram_addr_t length, - int64_t *real_dirty_pages) + uint64_t *real_dirty_pages) { ram_addr_t addr; + start = rb->offset + start; unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); uint64_t num_dirty = 0; @@ -411,7 +420,5 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, return num_dirty; } - -void migration_bitmap_extend(ram_addr_t old, ram_addr_t new); #endif #endif diff --git a/include/glib-compat.h b/include/glib-compat.h index 863c8cf73d..fcffcd3f07 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -217,6 +217,12 @@ static inline void g_hash_table_add(GHashTable *hash_table, gpointer key) { g_hash_table_replace(hash_table, key, key); } + +static inline gboolean g_hash_table_contains(GHashTable *hash_table, + gpointer key) +{ + return g_hash_table_lookup_extended(hash_table, key, NULL, NULL); +} #endif #ifndef g_assert_true diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index dbec0c1598..4c5fc66a1e 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -20,6 +20,7 @@ #include "hw/i2c/aspeed_i2c.h" #include "hw/ssi/aspeed_smc.h" #include "hw/watchdog/wdt_aspeed.h" +#include "hw/net/ftgmac100.h" #define ASPEED_SPIS_NUM 2 @@ -39,6 +40,7 @@ typedef struct AspeedSoCState { AspeedSMCState spi[ASPEED_SPIS_NUM]; AspeedSDMCState sdmc; AspeedWDTState wdt; + FTGMAC100State ftgmac100; } AspeedSoCState; #define TYPE_ASPEED_SOC "aspeed-soc" diff --git a/include/hw/compat.h b/include/hw/compat.h index 5d5be91daf..846b90eb67 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -1,6 +1,9 @@ #ifndef HW_COMPAT_H #define HW_COMPAT_H +#define HW_COMPAT_2_9 \ + /* empty */ + #define HW_COMPAT_2_8 \ {\ .driver = "fw_cfg_mem",\ diff --git a/include/hw/devices.h b/include/hw/devices.h index 7475b714de..861ddea8af 100644 --- a/include/hw/devices.h +++ b/include/hw/devices.h @@ -62,9 +62,4 @@ void tc6393xb_gpio_out_set(TC6393xbState *s, int line, qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s); qemu_irq tc6393xb_l3v_get(TC6393xbState *s); -/* sm501.c */ -void sm501_init(struct MemoryRegion *address_space_mem, uint32_t base, - uint32_t local_mem_bytes, qemu_irq irq, - Chardev *chr); - #endif diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index fe645aa93a..3e51876b75 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -63,6 +63,7 @@ typedef union VTD_IR_TableEntry VTD_IR_TableEntry; typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress; typedef struct VTDIrq VTDIrq; typedef struct VTD_MSIMessage VTD_MSIMessage; +typedef struct IntelIOMMUNotifierNode IntelIOMMUNotifierNode; /* Context-Entry */ struct VTDContextEntry { @@ -83,6 +84,8 @@ struct VTDAddressSpace { uint8_t devfn; AddressSpace as; MemoryRegion iommu; + MemoryRegion root; + MemoryRegion sys_alias; MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ IntelIOMMUState *iommu_state; VTDContextCacheEntry context_cache_entry; @@ -247,6 +250,11 @@ struct VTD_MSIMessage { /* When IR is enabled, all MSI/MSI-X data bits should be zero */ #define VTD_IR_MSI_DATA (0) +struct IntelIOMMUNotifierNode { + VTDAddressSpace *vtd_as; + QLIST_ENTRY(IntelIOMMUNotifierNode) next; +}; + /* The iommu (DMAR) device state struct */ struct IntelIOMMUState { X86IOMMUState x86_iommu; @@ -284,6 +292,8 @@ struct IntelIOMMUState { MemoryRegionIOMMUOps iommu_ops; GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ + /* list of registered notifiers */ + QLIST_HEAD(, IntelIOMMUNotifierNode) notifiers_list; /* interrupt remapping */ bool intr_enabled; /* Whether guest enabled IR */ diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 91b83b5bb0..0affe5a4d8 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -259,4 +259,8 @@ struct ipmi_sdr_compact { typedef uint8_t ipmi_sdr_compact_buffer[sizeof(struct ipmi_sdr_compact)]; +int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, + const struct ipmi_sdr_compact **sdr, uint16_t *nextrec); +void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log); + #endif diff --git a/include/hw/net/cadence_gem.h b/include/hw/net/cadence_gem.h index c469ffe69b..35de622063 100644 --- a/include/hw/net/cadence_gem.h +++ b/include/hw/net/cadence_gem.h @@ -50,6 +50,7 @@ typedef struct CadenceGEMState { uint8_t num_priority_queues; uint8_t num_type1_screeners; uint8_t num_type2_screeners; + uint32_t revision; /* GEM registers backing store */ uint32_t regs[CADENCE_GEM_MAXREG]; diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h new file mode 100644 index 0000000000..d9bc589fbf --- /dev/null +++ b/include/hw/net/ftgmac100.h @@ -0,0 +1,64 @@ +/* + * Faraday FTGMAC100 Gigabit Ethernet + * + * Copyright (C) 2016-2017, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef FTGMAC100_H +#define FTGMAC100_H + +#define TYPE_FTGMAC100 "ftgmac100" +#define FTGMAC100(obj) OBJECT_CHECK(FTGMAC100State, (obj), TYPE_FTGMAC100) + +#include "hw/sysbus.h" +#include "net/net.h" + +typedef struct FTGMAC100State { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + NICState *nic; + NICConf conf; + qemu_irq irq; + MemoryRegion iomem; + + uint8_t *frame; + + uint32_t irq_state; + uint32_t isr; + uint32_t ier; + uint32_t rx_enabled; + uint32_t rx_ring; + uint32_t rx_descriptor; + uint32_t tx_ring; + uint32_t tx_descriptor; + uint32_t math[2]; + uint32_t rbsr; + uint32_t itc; + uint32_t aptcr; + uint32_t dblac; + uint32_t revr; + uint32_t fear1; + uint32_t tpafcr; + uint32_t maccr; + uint32_t phycr; + uint32_t phydata; + uint32_t fcr; + + + uint32_t phy_status; + uint32_t phy_control; + uint32_t phy_advertise; + uint32_t phy_int; + uint32_t phy_int_mask; + + bool aspeed; + uint32_t txdes0_edotr; + uint32_t rxdes0_edorr; +} FTGMAC100State; + +#endif diff --git a/include/hw/net/mii.h b/include/hw/net/mii.h index 9fdd7bbe75..6ce48a6d78 100644 --- a/include/hw/net/mii.h +++ b/include/hw/net/mii.h @@ -22,13 +22,20 @@ #define MII_H /* PHY registers */ -#define MII_BMCR 0 -#define MII_BMSR 1 -#define MII_PHYID1 2 -#define MII_PHYID2 3 -#define MII_ANAR 4 -#define MII_ANLPAR 5 -#define MII_ANER 6 +#define MII_BMCR 0 /* Basic mode control register */ +#define MII_BMSR 1 /* Basic mode status register */ +#define MII_PHYID1 2 /* ID register 1 */ +#define MII_PHYID2 3 /* ID register 2 */ +#define MII_ANAR 4 /* Autonegotiation advertisement */ +#define MII_ANLPAR 5 /* Autonegotiation lnk partner abilities */ +#define MII_ANER 6 /* Autonegotiation expansion */ +#define MII_ANNP 7 /* Autonegotiation next page */ +#define MII_ANLPRNP 8 /* Autonegotiation link partner rx next page */ +#define MII_CTRL1000 9 /* 1000BASE-T control */ +#define MII_STAT1000 10 /* 1000BASE-T status */ +#define MII_MDDACR 13 /* MMD access control */ +#define MII_MDDAADR 14 /* MMD access address data */ +#define MII_EXTSTAT 15 /* Extended Status */ #define MII_NSR 16 #define MII_LBREMR 17 #define MII_REC 18 @@ -38,19 +45,33 @@ /* PHY registers fields */ #define MII_BMCR_RESET (1 << 15) #define MII_BMCR_LOOPBACK (1 << 14) -#define MII_BMCR_SPEED (1 << 13) -#define MII_BMCR_AUTOEN (1 << 12) -#define MII_BMCR_FD (1 << 8) +#define MII_BMCR_SPEED100 (1 << 13) /* LSB of Speed (100) */ +#define MII_BMCR_SPEED MII_BMCR_SPEED100 +#define MII_BMCR_AUTOEN (1 << 12) /* Autonegotiation enable */ +#define MII_BMCR_PDOWN (1 << 11) /* Enable low power state */ +#define MII_BMCR_ISOLATE (1 << 10) /* Isolate data paths from MII */ +#define MII_BMCR_ANRESTART (1 << 9) /* Auto negotiation restart */ +#define MII_BMCR_FD (1 << 8) /* Set duplex mode */ +#define MII_BMCR_CTST (1 << 7) /* Collision test */ +#define MII_BMCR_SPEED1000 (1 << 6) /* MSB of Speed (1000) */ -#define MII_BMSR_100TX_FD (1 << 14) -#define MII_BMSR_100TX_HD (1 << 13) -#define MII_BMSR_10T_FD (1 << 12) -#define MII_BMSR_10T_HD (1 << 11) -#define MII_BMSR_MFPS (1 << 6) -#define MII_BMSR_AN_COMP (1 << 5) -#define MII_BMSR_AUTONEG (1 << 3) -#define MII_BMSR_LINK_ST (1 << 2) +#define MII_BMSR_100TX_FD (1 << 14) /* Can do 100mbps, full-duplex */ +#define MII_BMSR_100TX_HD (1 << 13) /* Can do 100mbps, half-duplex */ +#define MII_BMSR_10T_FD (1 << 12) /* Can do 10mbps, full-duplex */ +#define MII_BMSR_10T_HD (1 << 11) /* Can do 10mbps, half-duplex */ +#define MII_BMSR_100T2_FD (1 << 10) /* Can do 100mbps T2, full-duplex */ +#define MII_BMSR_100T2_HD (1 << 9) /* Can do 100mbps T2, half-duplex */ +#define MII_BMSR_EXTSTAT (1 << 8) /* Extended status in register 15 */ +#define MII_BMSR_MFPS (1 << 6) /* MII Frame Preamble Suppression */ +#define MII_BMSR_AN_COMP (1 << 5) /* Auto-negotiation complete */ +#define MII_BMSR_RFAULT (1 << 4) /* Remote fault */ +#define MII_BMSR_AUTONEG (1 << 3) /* Able to do auto-negotiation */ +#define MII_BMSR_LINK_ST (1 << 2) /* Link status */ +#define MII_BMSR_JABBER (1 << 1) /* Jabber detected */ +#define MII_BMSR_EXTCAP (1 << 0) /* Ext-reg capability */ +#define MII_ANAR_PAUSE_ASYM (1 << 11) /* Try for asymetric pause */ +#define MII_ANAR_PAUSE (1 << 10) /* Try for pause */ #define MII_ANAR_TXFD (1 << 8) #define MII_ANAR_TX (1 << 7) #define MII_ANAR_10FD (1 << 6) @@ -58,17 +79,31 @@ #define MII_ANAR_CSMACD (1 << 0) #define MII_ANLPAR_ACK (1 << 14) +#define MII_ANLPAR_PAUSEASY (1 << 11) /* can pause asymmetrically */ +#define MII_ANLPAR_PAUSE (1 << 10) /* can pause */ #define MII_ANLPAR_TXFD (1 << 8) #define MII_ANLPAR_TX (1 << 7) #define MII_ANLPAR_10FD (1 << 6) #define MII_ANLPAR_10 (1 << 5) #define MII_ANLPAR_CSMACD (1 << 0) +#define MII_ANER_NWAY (1 << 0) /* Can do N-way auto-nego */ + +#define MII_CTRL1000_FULL (1 << 9) /* 1000BASE-T full duplex */ +#define MII_CTRL1000_HALF (1 << 8) /* 1000BASE-T half duplex */ + +#define MII_STAT1000_FULL (1 << 11) /* 1000BASE-T full duplex */ +#define MII_STAT1000_HALF (1 << 10) /* 1000BASE-T half duplex */ + /* List of vendor identifiers */ /* RealTek 8201 */ #define RTL8201CP_PHYID1 0x0000 #define RTL8201CP_PHYID2 0x8201 +/* RealTek 8211E */ +#define RTL8211E_PHYID1 0x001c +#define RTL8211E_PHYID2 0xc915 + /* National Semiconductor DP83848 */ #define DP83848_PHYID1 0x2000 #define DP83848_PHYID2 0x5c90 diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index d22ad8dd3b..3752ddc93a 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -207,6 +207,9 @@ #define PCI_VENDOR_ID_MARVELL 0x11ab +#define PCI_VENDOR_ID_SILICON_MOTION 0x126f +#define PCI_DEVICE_ID_SM501 0x0501 + #define PCI_VENDOR_ID_ENSONIQ 0x1274 #define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index df98a72006..c1288f974d 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -22,6 +22,8 @@ #include "hw/boards.h" #include "hw/sysbus.h" #include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv_psi.h" +#include "hw/ppc/pnv_occ.h" #define TYPE_PNV_CHIP "powernv-chip" #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) @@ -54,8 +56,11 @@ typedef struct PnvChip { MemoryRegion xscom_mmio; MemoryRegion xscom; AddressSpace xscom_as; + MemoryRegion icp_mmio; PnvLpcController lpc; + PnvPsi psi; + PnvOCC occ; } PnvChip; typedef struct PnvChipClass { @@ -91,18 +96,30 @@ typedef struct PnvChipClass { OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9) /* - * This generates a HW chip id depending on an index: + * This generates a HW chip id depending on an index, as found on a + * two socket system with dual chip modules : * * 0x0, 0x1, 0x10, 0x11 * * 4 chips should be the maximum + * + * TODO: use a machine property to define the chip ids */ #define PNV_CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1)) +/* + * Converts back a HW chip id to an index. This is useful to calculate + * the MMIO addresses of some controllers which depend on the chip id. + */ +#define PNV_CHIP_INDEX(chip) \ + (((chip)->chip_id >> 2) * 2 + ((chip)->chip_id & 0x3)) + #define TYPE_POWERNV_MACHINE MACHINE_TYPE_NAME("powernv") #define POWERNV_MACHINE(obj) \ OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE) +typedef struct IPMIBmc IPMIBmc; + typedef struct PnvMachineState { /*< private >*/ MachineState parent_obj; @@ -114,16 +131,54 @@ typedef struct PnvMachineState { PnvChip **chips; ISABus *isa_bus; + uint32_t cpld_irqstate; + + IPMIBmc *bmc; + Notifier powerdown_notifier; } PnvMachineState; #define PNV_FDT_ADDR 0x01000000 #define PNV_TIMEBASE_FREQ 512000000ULL /* + * BMC helpers + */ +void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt); +void pnv_bmc_powerdown(IPMIBmc *bmc); + +/* * POWER8 MMIO base addresses */ #define PNV_XSCOM_SIZE 0x800000000ull #define PNV_XSCOM_BASE(chip) \ (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE) +/* + * XSCOM 0x20109CA defines the ICP BAR: + * + * 0:29 : bits 14 to 43 of address to define 1 MB region. + * 30 : 1 to enable ICP to receive loads/stores against its BAR region + * 31:63 : Constant 0 + * + * Usually defined as : + * + * 0xffffe00200000000 -> 0x0003ffff80000000 + * 0xffffe00600000000 -> 0x0003ffff80100000 + * 0xffffe02200000000 -> 0x0003ffff80800000 + * 0xffffe02600000000 -> 0x0003ffff80900000 + */ +#define PNV_ICP_SIZE 0x0000000000100000ull +#define PNV_ICP_BASE(chip) \ + (0x0003ffff80000000ull + (uint64_t) PNV_CHIP_INDEX(chip) * PNV_ICP_SIZE) + + +#define PNV_PSIHB_SIZE 0x0000000000100000ull +#define PNV_PSIHB_BASE(chip) \ + (0x0003fffe80000000ull + (uint64_t)PNV_CHIP_INDEX(chip) * PNV_PSIHB_SIZE) + +#define PNV_PSIHB_FSP_SIZE 0x0000000100000000ull +#define PNV_PSIHB_FSP_BASE(chip) \ + (0x0003ffe000000000ull + (uint64_t)PNV_CHIP_INDEX(chip) * \ + PNV_PSIHB_FSP_SIZE) + #endif /* _PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 38e5506975..ccf969af94 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -23,6 +23,8 @@ #define PNV_LPC(obj) \ OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC) +typedef struct PnvPsi PnvPsi; + typedef struct PnvLpcController { DeviceState parent; @@ -62,6 +64,12 @@ typedef struct PnvLpcController { /* XSCOM registers */ MemoryRegion xscom_regs; + + /* PSI to generate interrupts */ + PnvPsi *psi; } PnvLpcController; +qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, + int nirqs); + #endif /* _PPC_PNV_LPC_H */ diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h new file mode 100644 index 0000000000..f8ec330abf --- /dev/null +++ b/include/hw/ppc/pnv_occ.h @@ -0,0 +1,38 @@ +/* + * QEMU PowerPC PowerNV Emulation of a few OCC related registers + * + * Copyright (c) 2015-2017, IBM Corporation. + * + * 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 _PPC_PNV_OCC_H +#define _PPC_PNV_OCC_H + +#define TYPE_PNV_OCC "pnv-occ" +#define PNV_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV_OCC) + +typedef struct PnvPsi PnvPsi; + +typedef struct PnvOCC { + DeviceState xd; + + /* OCC Misc interrupt */ + uint64_t occmisc; + + PnvPsi *psi; + + MemoryRegion xscom_regs; +} PnvOCC; + +#endif /* _PPC_PNV_OCC_H */ diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h new file mode 100644 index 0000000000..11d83e43f8 --- /dev/null +++ b/include/hw/ppc/pnv_psi.h @@ -0,0 +1,67 @@ +/* + * QEMU PowerPC PowerNV Processor Service Interface (PSI) model + * + * Copyright (c) 2015-2017, IBM Corporation. + * + * 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 _PPC_PNV_PSI_H +#define _PPC_PNV_PSI_H + +#include "hw/sysbus.h" +#include "hw/ppc/xics.h" + +#define TYPE_PNV_PSI "pnv-psi" +#define PNV_PSI(obj) \ + OBJECT_CHECK(PnvPsi, (obj), TYPE_PNV_PSI) + +#define PSIHB_XSCOM_MAX 0x20 + +typedef struct XICSState XICSState; + +typedef struct PnvPsi { + SysBusDevice parent; + + MemoryRegion regs_mr; + uint64_t bar; + + /* FSP region not supported */ + /* MemoryRegion fsp_mr; */ + uint64_t fsp_bar; + + /* Interrupt generation */ + ICSState ics; + + /* Registers */ + uint64_t regs[PSIHB_XSCOM_MAX]; + + MemoryRegion xscom_regs; +} PnvPsi; + +/* The PSI and FSP interrupts are muxed on the same IRQ number */ +typedef enum PnvPsiIrq { + PSIHB_IRQ_PSI, /* internal use only */ + PSIHB_IRQ_FSP, /* internal use only */ + PSIHB_IRQ_OCC, + PSIHB_IRQ_FSI, + PSIHB_IRQ_LPC_I2C, + PSIHB_IRQ_LOCAL_ERR, + PSIHB_IRQ_EXTERNAL, +} PnvPsiIrq; + +#define PSI_NUM_INTERRUPTS 6 + +extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state); + +#endif /* _PPC_PNV_PSI_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 0faa1847bf..3757b2cab9 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -60,6 +60,12 @@ typedef struct PnvXScomInterfaceClass { #define PNV_XSCOM_LPC_BASE 0xb0020 #define PNV_XSCOM_LPC_SIZE 0x4 +#define PNV_XSCOM_PSIHB_BASE 0x2010900 +#define PNV_XSCOM_PSIHB_SIZE 0x20 + +#define PNV_XSCOM_OCC_BASE 0x0066000 +#define PNV_XSCOM_OCC_SIZE 0x6000 + extern void pnv_xscom_realize(PnvChip *chip, Error **errp); extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 808aac8703..5802f888c3 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -20,6 +20,18 @@ typedef struct sPAPREventSource sPAPREventSource; #define SPAPR_TIMEBASE_FREQ 512000000ULL +#define TYPE_SPAPR_RTC "spapr-rtc" + +#define SPAPR_RTC(obj) \ + OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) + +typedef struct sPAPRRTCState sPAPRRTCState; +struct sPAPRRTCState { + /*< private >*/ + DeviceState parent_obj; + int64_t ns_offset; +}; + typedef struct sPAPRMachineClass sPAPRMachineClass; #define TYPE_SPAPR_MACHINE "spapr-machine" @@ -58,7 +70,7 @@ struct sPAPRMachineState { QLIST_HEAD(, sPAPRPHBState) phbs; struct sPAPRNVRAM *nvram; ICSState *ics; - DeviceState *rtc; + sPAPRRTCState rtc; void *htab; uint32_t htab_shift; @@ -77,6 +89,7 @@ struct sPAPRMachineState { sPAPROptionVector *ov5; /* QEMU-supported option vectors */ sPAPROptionVector *ov5_cas; /* negotiated (via CAS) option vectors */ bool cas_reboot; + bool cas_legacy_guest_workaround; Notifier epow_notifier; QTAILQ_HEAD(, sPAPREventLogEntry) pending_events; @@ -95,8 +108,7 @@ struct sPAPRMachineState { char *kvm_type; MemoryHotplugState hotplug_memory; - uint32_t nr_servers; - ICPState *icps; + const char *icp_type; }; #define H_SUCCESS 0 @@ -349,6 +361,9 @@ struct sPAPRMachineState { #define H_XIRR_X 0x2FC #define H_RANDOM 0x300 #define H_SET_MODE 0x31C +#define H_CLEAN_SLB 0x374 +#define H_INVALIDATE_PID 0x378 +#define H_REGISTER_PROC_TBL 0x37C #define H_SIGNAL_SYS_RESET 0x380 #define MAX_HCALL_OPCODE H_SIGNAL_SYS_RESET @@ -593,6 +608,8 @@ void spapr_dt_events(sPAPRMachineState *sm, void *fdt); int spapr_h_cas_compose_response(sPAPRMachineState *sm, target_ulong addr, target_ulong size, sPAPROptionVector *ov5_updates); +void close_htab_fd(sPAPRMachineState *spapr); +void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(sPAPRTCETable *tcet, uint32_t page_shift, uint64_t bus_offset, @@ -629,11 +646,10 @@ struct sPAPRConfigureConnectorState { void spapr_ccs_reset_hook(void *opaque); -#define TYPE_SPAPR_RTC "spapr-rtc" -#define TYPE_SPAPR_RNG "spapr-rng" +void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns); +int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset); -void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns); -int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset); +#define TYPE_SPAPR_RNG "spapr-rng" int spapr_rng_populate_dt(void *fdt); diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index 355a34411f..f088833204 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -43,11 +43,19 @@ typedef struct sPAPROptionVector sPAPROptionVector; #define OV_BIT(byte, bit) ((byte - 1) * BITS_PER_BYTE + bit) +/* option vector 1 */ +#define OV1_PPC_3_00 OV_BIT(3, 0) /* guest supports PowerPC 3.00? */ + /* option vector 5 */ #define OV5_DRCONF_MEMORY OV_BIT(2, 2) #define OV5_FORM1_AFFINITY OV_BIT(5, 0) #define OV5_HP_EVT OV_BIT(6, 5) +/* ISA 3.00 MMU features: */ +#define OV5_MMU_BOTH OV_BIT(24, 0) /* Radix and hash */ +#define OV5_MMU_RADIX_300 OV_BIT(24, 1) /* 1=Radix only, 0=Hash only */ +#define OV5_MMU_RADIX_GTSE OV_BIT(26, 1) /* Radix GTSE */ + /* interfaces */ sPAPROptionVector *spapr_ovec_new(void); sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig); diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 9a5e715fe5..c215dc72a4 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -28,7 +28,7 @@ #ifndef XICS_H #define XICS_H -#include "hw/sysbus.h" +#include "hw/qdev.h" #define XICS_IPI 0x2 #define XICS_BUID 0x1 @@ -41,10 +41,12 @@ */ typedef struct ICPStateClass ICPStateClass; typedef struct ICPState ICPState; +typedef struct PnvICPState PnvICPState; typedef struct ICSStateClass ICSStateClass; typedef struct ICSState ICSState; typedef struct ICSIRQState ICSIRQState; typedef struct XICSFabric XICSFabric; +typedef struct PowerPCCPU PowerPCCPU; #define TYPE_ICP "icp" #define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP) @@ -52,6 +54,9 @@ typedef struct XICSFabric XICSFabric; #define TYPE_KVM_ICP "icp-kvm" #define KVM_ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_KVM_ICP) +#define TYPE_PNV_ICP "pnv-icp" +#define PNV_ICP(obj) OBJECT_CHECK(PnvICPState, (obj), TYPE_PNV_ICP) + #define ICP_CLASS(klass) \ OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP) #define ICP_GET_CLASS(obj) \ @@ -60,6 +65,7 @@ typedef struct XICSFabric XICSFabric; struct ICPStateClass { DeviceClass parent_class; + void (*realize)(DeviceState *dev, Error **errp); void (*pre_save)(ICPState *s); int (*post_load)(ICPState *s, int version_id); void (*cpu_setup)(ICPState *icp, PowerPCCPU *cpu); @@ -80,6 +86,13 @@ struct ICPState { XICSFabric *xics; }; +struct PnvICPState { + ICPState parent_obj; + + MemoryRegion mmio; + uint32_t links[3]; +}; + #define TYPE_ICS_BASE "ics-base" #define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_BASE) @@ -168,12 +181,10 @@ void spapr_dt_xics(int nr_servers, void *fdt, uint32_t phandle); qemu_irq xics_get_qirq(XICSFabric *xi, int irq); ICPState *xics_icp_get(XICSFabric *xi, int server); -void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu); +void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp); void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu); /* Internal XICS interfaces */ -int xics_get_cpu_index_by_dt_id(int cpu_dt_id); - void icp_set_cppr(ICPState *icp, uint8_t cppr); void icp_set_mfrr(ICPState *icp, uint8_t mfrr); uint32_t icp_accept(ICPState *ss); diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index b44b476765..4bf86b0ad8 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -113,19 +113,6 @@ typedef struct DeviceClass { * TODO remove once we're there */ bool cannot_instantiate_with_device_add_yet; - /* - * Does this device model survive object_unref(object_new(TNAME))? - * All device models should, and this flag shouldn't exist. Some - * devices crash in object_new(), some crash or hang in - * object_unref(). Makes introspecting properties with - * qmp_device_list_properties() dangerous. Bad, because it's used - * by -device FOO,help. This flag serves to protect that code. - * It should never be set without a comment explaining why it is - * set. - * TODO remove once we're there - */ - bool cannot_destroy_with_object_finalize_yet; - bool hotpluggable; /* callbacks */ @@ -386,7 +373,8 @@ Object *qdev_get_machine(void); /* FIXME: make this a link<> */ void qdev_set_parent_bus(DeviceState *dev, BusState *bus); -extern int qdev_hotplug; +extern bool qdev_hotplug; +extern bool qdev_hot_removed; char *qdev_get_dev_path(DeviceState *dev); diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 7ac315331a..1d69fa7a8f 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -188,7 +188,8 @@ void qdev_prop_set_chr(DeviceState *dev, const char *name, Chardev *value); void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value); void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockBackend *value, Error **errp); -void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value); +void qdev_prop_set_macaddr(DeviceState *dev, const char *name, + const uint8_t *value); void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); /* FIXME: Remove opaque pointer properties. */ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index c96c862057..f1f0d7f07a 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -23,6 +23,8 @@ #define MAX_CSSID 255 #define MAX_CHPID 255 +#define MAX_ISC 7 + #define MAX_CIWS 62 #define VIRTUAL_CSSID 0xfe @@ -124,9 +126,15 @@ void css_generate_css_crws(uint8_t cssid); void css_clear_sei_pending(void); void css_adapter_interrupt(uint8_t isc); -#define CSS_IO_ADAPTER_VIRTIO 1 -int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, - bool maskable, uint32_t *id); +typedef enum { + CSS_IO_ADAPTER_VIRTIO = 0, + CSS_IO_ADAPTER_PCI = 1, + CSS_IO_ADAPTER_TYPE_NUMS, +} CssIoAdapterType; + +uint32_t css_get_adapter_id(CssIoAdapterType type, uint8_t isc); +void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable, + Error **errp); #ifndef CONFIG_USER_ONLY SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, @@ -172,6 +180,11 @@ extern PropertyInfo css_devid_propinfo; #define DEFINE_PROP_CSS_DEV_ID(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, css_devid_propinfo, CssDevId) +extern PropertyInfo css_devid_ro_propinfo; + +#define DEFINE_PROP_CSS_DEV_ID_RO(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, css_devid_ro_propinfo, CssDevId) + /** * Create a subchannel for the given bus id. * diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 6ecae00386..7b8a3e4d74 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -28,6 +28,7 @@ typedef struct S390CcwMachineState { /*< public >*/ bool aes_key_wrap; bool dea_key_wrap; + uint8_t loadparm[8]; } S390CcwMachineState; typedef struct S390CcwMachineClass { diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index f3a98a3261..f3ffdceca4 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -72,6 +72,8 @@ struct virtio_gpu_conf { uint64_t max_hostmem; uint32_t max_outputs; uint32_t flags; + uint32_t xres; + uint32_t yres; }; struct virtio_gpu_ctrl_command { diff --git a/include/hw/virtio/virtio-rng.h b/include/hw/virtio/virtio-rng.h index 2d40abdbdb..922dce7cac 100644 --- a/include/hw/virtio/virtio-rng.h +++ b/include/hw/virtio/virtio-rng.h @@ -45,6 +45,8 @@ typedef struct VirtIORNG { QEMUTimer *rate_limit_timer; int64_t quota_remaining; bool activate_timer; + + VMChangeStateEntry *vmstate; } VirtIORNG; #endif diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 8ae0acaa1f..8c8453cf19 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -135,9 +135,11 @@ static inline void virtio_scsi_release(VirtIOSCSI *s) } } -void virtio_scsi_common_realize(DeviceState *dev, Error **errp, - VirtIOHandleOutput ctrl, VirtIOHandleOutput evt, - VirtIOHandleOutput cmd); +void virtio_scsi_common_realize(DeviceState *dev, + VirtIOHandleOutput ctrl, + VirtIOHandleOutput evt, + VirtIOHandleOutput cmd, + Error **errp); void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp); bool virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq); diff --git a/include/hw/xen/io/ring.h b/include/hw/xen/io/ring.h new file mode 100644 index 0000000000..abbca47687 --- /dev/null +++ b/include/hw/xen/io/ring.h @@ -0,0 +1,482 @@ +/****************************************************************************** + * ring.h + * + * Shared producer-consumer ring macros. + * + * 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. + * + * Tim Deegan and Andrew Warfield November 2004. + */ + +#ifndef __XEN_PUBLIC_IO_RING_H__ +#define __XEN_PUBLIC_IO_RING_H__ + +/* + * When #include'ing this header, you need to provide the following + * declaration upfront: + * - standard integers types (uint8_t, uint16_t, etc) + * They are provided by stdint.h of the standard headers. + * + * In addition, if you intend to use the FLEX macros, you also need to + * provide the following, before invoking the FLEX macros: + * - size_t + * - memcpy + * - grant_ref_t + * These declarations are provided by string.h of the standard headers, + * and grant_table.h from the Xen public headers. + */ + +#if __XEN_INTERFACE_VERSION__ < 0x00030208 +#define xen_mb() mb() +#define xen_rmb() rmb() +#define xen_wmb() wmb() +#endif + +typedef unsigned int RING_IDX; + +/* Round a 32-bit unsigned constant down to the nearest power of two. */ +#define __RD2(_x) (((_x) & 0x00000002) ? 0x2 : ((_x) & 0x1)) +#define __RD4(_x) (((_x) & 0x0000000c) ? __RD2((_x)>>2)<<2 : __RD2(_x)) +#define __RD8(_x) (((_x) & 0x000000f0) ? __RD4((_x)>>4)<<4 : __RD4(_x)) +#define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x)>>8)<<8 : __RD8(_x)) +#define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x)>>16)<<16 : __RD16(_x)) + +/* + * Calculate size of a shared ring, given the total available space for the + * ring and indexes (_sz), and the name tag of the request/response structure. + * A ring contains as many entries as will fit, rounded down to the nearest + * power of two (so we can mask with (size-1) to loop around). + */ +#define __CONST_RING_SIZE(_s, _sz) \ + (__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \ + sizeof(((struct _s##_sring *)0)->ring[0]))) +/* + * The same for passing in an actual pointer instead of a name tag. + */ +#define __RING_SIZE(_s, _sz) \ + (__RD32(((_sz) - (long)(_s)->ring + (long)(_s)) / sizeof((_s)->ring[0]))) + +/* + * Macros to make the correct C datatypes for a new kind of ring. + * + * To make a new ring datatype, you need to have two message structures, + * let's say request_t, and response_t already defined. + * + * In a header where you want the ring datatype declared, you then do: + * + * DEFINE_RING_TYPES(mytag, request_t, response_t); + * + * These expand out to give you a set of types, as you can see below. + * The most important of these are: + * + * mytag_sring_t - The shared ring. + * mytag_front_ring_t - The 'front' half of the ring. + * mytag_back_ring_t - The 'back' half of the ring. + * + * To initialize a ring in your code you need to know the location and size + * of the shared memory area (PAGE_SIZE, for instance). To initialise + * the front half: + * + * mytag_front_ring_t front_ring; + * SHARED_RING_INIT((mytag_sring_t *)shared_page); + * FRONT_RING_INIT(&front_ring, (mytag_sring_t *)shared_page, PAGE_SIZE); + * + * Initializing the back follows similarly (note that only the front + * initializes the shared ring): + * + * mytag_back_ring_t back_ring; + * BACK_RING_INIT(&back_ring, (mytag_sring_t *)shared_page, PAGE_SIZE); + */ + +#define DEFINE_RING_TYPES(__name, __req_t, __rsp_t) \ + \ +/* Shared ring entry */ \ +union __name##_sring_entry { \ + __req_t req; \ + __rsp_t rsp; \ +}; \ + \ +/* Shared ring page */ \ +struct __name##_sring { \ + RING_IDX req_prod, req_event; \ + RING_IDX rsp_prod, rsp_event; \ + union { \ + struct { \ + uint8_t smartpoll_active; \ + } netif; \ + struct { \ + uint8_t msg; \ + } tapif_user; \ + uint8_t pvt_pad[4]; \ + } pvt; \ + uint8_t __pad[44]; \ + union __name##_sring_entry ring[1]; /* variable-length */ \ +}; \ + \ +/* "Front" end's private variables */ \ +struct __name##_front_ring { \ + RING_IDX req_prod_pvt; \ + RING_IDX rsp_cons; \ + unsigned int nr_ents; \ + struct __name##_sring *sring; \ +}; \ + \ +/* "Back" end's private variables */ \ +struct __name##_back_ring { \ + RING_IDX rsp_prod_pvt; \ + RING_IDX req_cons; \ + unsigned int nr_ents; \ + struct __name##_sring *sring; \ +}; \ + \ +/* Syntactic sugar */ \ +typedef struct __name##_sring __name##_sring_t; \ +typedef struct __name##_front_ring __name##_front_ring_t; \ +typedef struct __name##_back_ring __name##_back_ring_t + +/* + * Macros for manipulating rings. + * + * FRONT_RING_whatever works on the "front end" of a ring: here + * requests are pushed on to the ring and responses taken off it. + * + * BACK_RING_whatever works on the "back end" of a ring: here + * requests are taken off the ring and responses put on. + * + * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL. + * This is OK in 1-for-1 request-response situations where the + * requestor (front end) never has more than RING_SIZE()-1 + * outstanding requests. + */ + +/* Initialising empty rings */ +#define SHARED_RING_INIT(_s) do { \ + (_s)->req_prod = (_s)->rsp_prod = 0; \ + (_s)->req_event = (_s)->rsp_event = 1; \ + (void)memset((_s)->pvt.pvt_pad, 0, sizeof((_s)->pvt.pvt_pad)); \ + (void)memset((_s)->__pad, 0, sizeof((_s)->__pad)); \ +} while(0) + +#define FRONT_RING_INIT(_r, _s, __size) do { \ + (_r)->req_prod_pvt = 0; \ + (_r)->rsp_cons = 0; \ + (_r)->nr_ents = __RING_SIZE(_s, __size); \ + (_r)->sring = (_s); \ +} while (0) + +#define BACK_RING_INIT(_r, _s, __size) do { \ + (_r)->rsp_prod_pvt = 0; \ + (_r)->req_cons = 0; \ + (_r)->nr_ents = __RING_SIZE(_s, __size); \ + (_r)->sring = (_s); \ +} while (0) + +/* How big is this ring? */ +#define RING_SIZE(_r) \ + ((_r)->nr_ents) + +/* Number of free requests (for use on front side only). */ +#define RING_FREE_REQUESTS(_r) \ + (RING_SIZE(_r) - ((_r)->req_prod_pvt - (_r)->rsp_cons)) + +/* Test if there is an empty slot available on the front ring. + * (This is only meaningful from the front. ) + */ +#define RING_FULL(_r) \ + (RING_FREE_REQUESTS(_r) == 0) + +/* Test if there are outstanding messages to be processed on a ring. */ +#define RING_HAS_UNCONSUMED_RESPONSES(_r) \ + ((_r)->sring->rsp_prod - (_r)->rsp_cons) + +#ifdef __GNUC__ +#define RING_HAS_UNCONSUMED_REQUESTS(_r) ({ \ + unsigned int req = (_r)->sring->req_prod - (_r)->req_cons; \ + unsigned int rsp = RING_SIZE(_r) - \ + ((_r)->req_cons - (_r)->rsp_prod_pvt); \ + req < rsp ? req : rsp; \ +}) +#else +/* Same as above, but without the nice GCC ({ ... }) syntax. */ +#define RING_HAS_UNCONSUMED_REQUESTS(_r) \ + ((((_r)->sring->req_prod - (_r)->req_cons) < \ + (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) ? \ + ((_r)->sring->req_prod - (_r)->req_cons) : \ + (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) +#endif + +/* Direct access to individual ring elements, by index. */ +#define RING_GET_REQUEST(_r, _idx) \ + (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req)) + +/* + * Get a local copy of a request. + * + * Use this in preference to RING_GET_REQUEST() so all processing is + * done on a local copy that cannot be modified by the other end. + * + * Note that https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 may cause this + * to be ineffective where _req is a struct which consists of only bitfields. + */ +#define RING_COPY_REQUEST(_r, _idx, _req) do { \ + /* Use volatile to force the copy into _req. */ \ + *(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx); \ +} while (0) + +#define RING_GET_RESPONSE(_r, _idx) \ + (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp)) + +/* Loop termination condition: Would the specified index overflow the ring? */ +#define RING_REQUEST_CONS_OVERFLOW(_r, _cons) \ + (((_cons) - (_r)->rsp_prod_pvt) >= RING_SIZE(_r)) + +/* Ill-behaved frontend determination: Can there be this many requests? */ +#define RING_REQUEST_PROD_OVERFLOW(_r, _prod) \ + (((_prod) - (_r)->rsp_prod_pvt) > RING_SIZE(_r)) + +#define RING_PUSH_REQUESTS(_r) do { \ + xen_wmb(); /* back sees requests /before/ updated producer index */ \ + (_r)->sring->req_prod = (_r)->req_prod_pvt; \ +} while (0) + +#define RING_PUSH_RESPONSES(_r) do { \ + xen_wmb(); /* front sees resps /before/ updated producer index */ \ + (_r)->sring->rsp_prod = (_r)->rsp_prod_pvt; \ +} while (0) + +/* + * Notification hold-off (req_event and rsp_event): + * + * When queueing requests or responses on a shared ring, it may not always be + * necessary to notify the remote end. For example, if requests are in flight + * in a backend, the front may be able to queue further requests without + * notifying the back (if the back checks for new requests when it queues + * responses). + * + * When enqueuing requests or responses: + * + * Use RING_PUSH_{REQUESTS,RESPONSES}_AND_CHECK_NOTIFY(). The second argument + * is a boolean return value. True indicates that the receiver requires an + * asynchronous notification. + * + * After dequeuing requests or responses (before sleeping the connection): + * + * Use RING_FINAL_CHECK_FOR_REQUESTS() or RING_FINAL_CHECK_FOR_RESPONSES(). + * The second argument is a boolean return value. True indicates that there + * are pending messages on the ring (i.e., the connection should not be put + * to sleep). + * + * These macros will set the req_event/rsp_event field to trigger a + * notification on the very next message that is enqueued. If you want to + * create batches of work (i.e., only receive a notification after several + * messages have been enqueued) then you will need to create a customised + * version of the FINAL_CHECK macro in your own code, which sets the event + * field appropriately. + */ + +#define RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(_r, _notify) do { \ + RING_IDX __old = (_r)->sring->req_prod; \ + RING_IDX __new = (_r)->req_prod_pvt; \ + xen_wmb(); /* back sees requests /before/ updated producer index */ \ + (_r)->sring->req_prod = __new; \ + xen_mb(); /* back sees new requests /before/ we check req_event */ \ + (_notify) = ((RING_IDX)(__new - (_r)->sring->req_event) < \ + (RING_IDX)(__new - __old)); \ +} while (0) + +#define RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(_r, _notify) do { \ + RING_IDX __old = (_r)->sring->rsp_prod; \ + RING_IDX __new = (_r)->rsp_prod_pvt; \ + xen_wmb(); /* front sees resps /before/ updated producer index */ \ + (_r)->sring->rsp_prod = __new; \ + xen_mb(); /* front sees new resps /before/ we check rsp_event */ \ + (_notify) = ((RING_IDX)(__new - (_r)->sring->rsp_event) < \ + (RING_IDX)(__new - __old)); \ +} while (0) + +#define RING_FINAL_CHECK_FOR_REQUESTS(_r, _work_to_do) do { \ + (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \ + if (_work_to_do) break; \ + (_r)->sring->req_event = (_r)->req_cons + 1; \ + xen_mb(); \ + (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \ +} while (0) + +#define RING_FINAL_CHECK_FOR_RESPONSES(_r, _work_to_do) do { \ + (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \ + if (_work_to_do) break; \ + (_r)->sring->rsp_event = (_r)->rsp_cons + 1; \ + xen_mb(); \ + (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \ +} while (0) + + +/* + * DEFINE_XEN_FLEX_RING_AND_INTF defines two monodirectional rings and + * functions to check if there is data on the ring, and to read and + * write to them. + * + * DEFINE_XEN_FLEX_RING is similar to DEFINE_XEN_FLEX_RING_AND_INTF, but + * does not define the indexes page. As different protocols can have + * extensions to the basic format, this macro allow them to define their + * own struct. + * + * XEN_FLEX_RING_SIZE + * Convenience macro to calculate the size of one of the two rings + * from the overall order. + * + * $NAME_mask + * Function to apply the size mask to an index, to reduce the index + * within the range [0-size]. + * + * $NAME_read_packet + * Function to read data from the ring. The amount of data to read is + * specified by the "size" argument. + * + * $NAME_write_packet + * Function to write data to the ring. The amount of data to write is + * specified by the "size" argument. + * + * $NAME_get_ring_ptr + * Convenience function that returns a pointer to read/write to the + * ring at the right location. + * + * $NAME_data_intf + * Indexes page, shared between frontend and backend. It also + * contains the array of grant refs. + * + * $NAME_queued + * Function to calculate how many bytes are currently on the ring, + * ready to be read. It can also be used to calculate how much free + * space is currently on the ring (XEN_FLEX_RING_SIZE() - + * $NAME_queued()). + */ + +#ifndef XEN_PAGE_SHIFT +/* The PAGE_SIZE for ring protocols and hypercall interfaces is always + * 4K, regardless of the architecture, and page granularity chosen by + * operating systems. + */ +#define XEN_PAGE_SHIFT 12 +#endif +#define XEN_FLEX_RING_SIZE(order) \ + (1UL << ((order) + XEN_PAGE_SHIFT - 1)) + +#define DEFINE_XEN_FLEX_RING(name) \ +static inline RING_IDX name##_mask(RING_IDX idx, RING_IDX ring_size) \ +{ \ + return idx & (ring_size - 1); \ +} \ + \ +static inline unsigned char *name##_get_ring_ptr(unsigned char *buf, \ + RING_IDX idx, \ + RING_IDX ring_size) \ +{ \ + return buf + name##_mask(idx, ring_size); \ +} \ + \ +static inline void name##_read_packet(void *opaque, \ + const unsigned char *buf, \ + size_t size, \ + RING_IDX masked_prod, \ + RING_IDX *masked_cons, \ + RING_IDX ring_size) \ +{ \ + if (*masked_cons < masked_prod || \ + size <= ring_size - *masked_cons) { \ + memcpy(opaque, buf + *masked_cons, size); \ + } else { \ + memcpy(opaque, buf + *masked_cons, ring_size - *masked_cons); \ + memcpy((unsigned char *)opaque + ring_size - *masked_cons, buf, \ + size - (ring_size - *masked_cons)); \ + } \ + *masked_cons = name##_mask(*masked_cons + size, ring_size); \ +} \ + \ +static inline void name##_write_packet(unsigned char *buf, \ + const void *opaque, \ + size_t size, \ + RING_IDX *masked_prod, \ + RING_IDX masked_cons, \ + RING_IDX ring_size) \ +{ \ + if (*masked_prod < masked_cons || \ + size <= ring_size - *masked_prod) { \ + memcpy(buf + *masked_prod, opaque, size); \ + } else { \ + memcpy(buf + *masked_prod, opaque, ring_size - *masked_prod); \ + memcpy(buf, (unsigned char *)opaque + (ring_size - *masked_prod), \ + size - (ring_size - *masked_prod)); \ + } \ + *masked_prod = name##_mask(*masked_prod + size, ring_size); \ +} \ + \ +static inline RING_IDX name##_queued(RING_IDX prod, \ + RING_IDX cons, \ + RING_IDX ring_size) \ +{ \ + RING_IDX size; \ + \ + if (prod == cons) \ + return 0; \ + \ + prod = name##_mask(prod, ring_size); \ + cons = name##_mask(cons, ring_size); \ + \ + if (prod == cons) \ + return ring_size; \ + \ + if (prod > cons) \ + size = prod - cons; \ + else \ + size = ring_size - (cons - prod); \ + return size; \ +} \ + \ +struct name##_data { \ + unsigned char *in; /* half of the allocation */ \ + unsigned char *out; /* half of the allocation */ \ +} + +#define DEFINE_XEN_FLEX_RING_AND_INTF(name) \ +struct name##_data_intf { \ + RING_IDX in_cons, in_prod; \ + \ + uint8_t pad1[56]; \ + \ + RING_IDX out_cons, out_prod; \ + \ + uint8_t pad2[56]; \ + \ + RING_IDX ring_order; \ + grant_ref_t ref[]; \ +}; \ +DEFINE_XEN_FLEX_RING(name) + +#endif /* __XEN_PUBLIC_IO_RING_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index 09c2ce5170..7efcdaa8fe 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -21,6 +21,7 @@ enum xen_mode { extern uint32_t xen_domid; extern enum xen_mode xen_mode; +extern bool xen_domid_restrict; extern bool xen_allowed; @@ -43,7 +44,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory); void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, struct MemoryRegion *mr, Error **errp); -void xen_modified_memory(ram_addr_t start, ram_addr_t length); +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length); void xen_register_framebuffer(struct MemoryRegion *mr); diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h index 4f4799a610..852c2ea64c 100644 --- a/include/hw/xen/xen_backend.h +++ b/include/hw/xen/xen_backend.h @@ -14,8 +14,6 @@ OBJECT_CHECK(XenDevice, (obj), TYPE_XENBACKEND) /* variables */ -extern xc_interface *xen_xc; -extern xenforeignmemory_handle *xen_fmem; extern struct xs_handle *xenstore; extern const char *xen_protocol; extern DeviceState *xen_sysdev; @@ -49,6 +47,9 @@ extern struct XenDevOps xen_console_ops; /* xen_console.c */ extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ +#ifdef CONFIG_VIRTFS +extern struct XenDevOps xen_9pfs_ops; /* xen-9p-backend.c */ +#endif extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ #ifdef CONFIG_USB_LIBUSB extern struct XenDevOps xen_usb_ops; /* xen-usb.c */ diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index dce76ee162..e00ddd7b5b 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -20,12 +20,14 @@ #include "qemu/queue.h" #include "hw/xen/trace.h" +extern xc_interface *xen_xc; + /* * We don't support Xen prior to 4.2.0. */ /* Xen 4.2 through 4.6 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 471 +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40701 typedef xc_interface xenforeignmemory_handle; typedef xc_evtchn xenevtchn_handle; @@ -51,6 +53,7 @@ typedef xc_gnttab xengnttab_handle; xc_gnttab_map_domain_grant_refs(h, c, d, r, p) #define xenforeignmemory_open(l, f) xen_xc +#define xenforeignmemory_close(h) static inline void *xenforeignmemory_map(xc_interface *h, uint32_t dom, int prot, size_t pages, @@ -65,7 +68,7 @@ static inline void *xenforeignmemory_map(xc_interface *h, uint32_t dom, #define xenforeignmemory_unmap(h, p, s) munmap(p, s * XC_PAGE_SIZE) -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 471 */ +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40701 */ #include <xenevtchn.h> #include <xengnttab.h> @@ -73,6 +76,230 @@ static inline void *xenforeignmemory_map(xc_interface *h, uint32_t dom, #endif +extern xenforeignmemory_handle *xen_fmem; + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 + +typedef xc_interface xendevicemodel_handle; + +static inline xendevicemodel_handle *xendevicemodel_open( + struct xentoollog_logger *logger, unsigned int open_flags) +{ + return xen_xc; +} + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40500 + +static inline int xendevicemodel_create_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, int handle_bufioreq, + ioservid_t *id) +{ + return xc_hvm_create_ioreq_server(dmod, domid, handle_bufioreq, + id); +} + +static inline int xendevicemodel_get_ioreq_server_info( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, + xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, + evtchn_port_t *bufioreq_port) +{ + return xc_hvm_get_ioreq_server_info(dmod, domid, id, ioreq_pfn, + bufioreq_pfn, bufioreq_port); +} + +static inline int xendevicemodel_map_io_range_to_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int is_mmio, + uint64_t start, uint64_t end) +{ + return xc_hvm_map_io_range_to_ioreq_server(dmod, domid, id, is_mmio, + start, end); +} + +static inline int xendevicemodel_unmap_io_range_from_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int is_mmio, + uint64_t start, uint64_t end) +{ + return xc_hvm_unmap_io_range_from_ioreq_server(dmod, domid, id, is_mmio, + start, end); +} + +static inline int xendevicemodel_map_pcidev_to_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, + uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) +{ + return xc_hvm_map_pcidev_to_ioreq_server(dmod, domid, id, segment, + bus, device, function); +} + +static inline int xendevicemodel_unmap_pcidev_from_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, + uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) +{ + return xc_hvm_unmap_pcidev_from_ioreq_server(dmod, domid, id, segment, + bus, device, function); +} + +static inline int xendevicemodel_destroy_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id) +{ + return xc_hvm_destroy_ioreq_server(dmod, domid, id); +} + +static inline int xendevicemodel_set_ioreq_server_state( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int enabled) +{ + return xc_hvm_set_ioreq_server_state(dmod, domid, id, enabled); +} + +#endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40500 */ + +static inline int xendevicemodel_set_pci_intx_level( + xendevicemodel_handle *dmod, domid_t domid, uint16_t segment, + uint8_t bus, uint8_t device, uint8_t intx, unsigned int level) +{ + return xc_hvm_set_pci_intx_level(dmod, domid, segment, bus, device, + intx, level); +} + +static inline int xendevicemodel_set_isa_irq_level( + xendevicemodel_handle *dmod, domid_t domid, uint8_t irq, + unsigned int level) +{ + return xc_hvm_set_isa_irq_level(dmod, domid, irq, level); +} + +static inline int xendevicemodel_set_pci_link_route( + xendevicemodel_handle *dmod, domid_t domid, uint8_t link, uint8_t irq) +{ + return xc_hvm_set_pci_link_route(dmod, domid, link, irq); +} + +static inline int xendevicemodel_inject_msi( + xendevicemodel_handle *dmod, domid_t domid, uint64_t msi_addr, + uint32_t msi_data) +{ + return xc_hvm_inject_msi(dmod, domid, msi_addr, msi_data); +} + +static inline int xendevicemodel_track_dirty_vram( + xendevicemodel_handle *dmod, domid_t domid, uint64_t first_pfn, + uint32_t nr, unsigned long *dirty_bitmap) +{ + return xc_hvm_track_dirty_vram(dmod, domid, first_pfn, nr, + dirty_bitmap); +} + +static inline int xendevicemodel_modified_memory( + xendevicemodel_handle *dmod, domid_t domid, uint64_t first_pfn, + uint32_t nr) +{ + return xc_hvm_modified_memory(dmod, domid, first_pfn, nr); +} + +static inline int xendevicemodel_set_mem_type( + xendevicemodel_handle *dmod, domid_t domid, hvmmem_type_t mem_type, + uint64_t first_pfn, uint32_t nr) +{ + return xc_hvm_set_mem_type(dmod, domid, mem_type, first_pfn, nr); +} + +static inline int xendevicemodel_restrict( + xendevicemodel_handle *dmod, domid_t domid) +{ + errno = ENOTTY; + return -1; +} + +static inline int xenforeignmemory_restrict( + xenforeignmemory_handle *fmem, domid_t domid) +{ + errno = ENOTTY; + return -1; +} + +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40900 */ + +#undef XC_WANT_COMPAT_DEVICEMODEL_API +#include <xendevicemodel.h> + +#endif + +extern xendevicemodel_handle *xen_dmod; + +static inline int xen_set_mem_type(domid_t domid, hvmmem_type_t type, + uint64_t first_pfn, uint32_t nr) +{ + return xendevicemodel_set_mem_type(xen_dmod, domid, type, first_pfn, + nr); +} + +static inline int xen_set_pci_intx_level(domid_t domid, uint16_t segment, + uint8_t bus, uint8_t device, + uint8_t intx, unsigned int level) +{ + return xendevicemodel_set_pci_intx_level(xen_dmod, domid, segment, bus, + device, intx, level); +} + +static inline int xen_set_pci_link_route(domid_t domid, uint8_t link, + uint8_t irq) +{ + return xendevicemodel_set_pci_link_route(xen_dmod, domid, link, irq); +} + +static inline int xen_inject_msi(domid_t domid, uint64_t msi_addr, + uint32_t msi_data) +{ + return xendevicemodel_inject_msi(xen_dmod, domid, msi_addr, msi_data); +} + +static inline int xen_set_isa_irq_level(domid_t domid, uint8_t irq, + unsigned int level) +{ + return xendevicemodel_set_isa_irq_level(xen_dmod, domid, irq, level); +} + +static inline int xen_track_dirty_vram(domid_t domid, uint64_t first_pfn, + uint32_t nr, unsigned long *bitmap) +{ + return xendevicemodel_track_dirty_vram(xen_dmod, domid, first_pfn, nr, + bitmap); +} + +static inline int xen_modified_memory(domid_t domid, uint64_t first_pfn, + uint32_t nr) +{ + return xendevicemodel_modified_memory(xen_dmod, domid, first_pfn, nr); +} + +static inline int xen_restrict(domid_t domid) +{ + int rc; + + /* Attempt to restrict devicemodel operations */ + rc = xendevicemodel_restrict(xen_dmod, domid); + trace_xen_domid_restrict(rc ? errno : 0); + + if (rc < 0) { + /* + * If errno is ENOTTY then restriction is not implemented so + * there's no point in trying to restrict other types of + * operation, but it should not be treated as a failure. + */ + if (errno == ENOTTY) { + return 0; + } + + return rc; + } + + /* Restrict foreignmemory operations */ + rc = xenforeignmemory_restrict(xen_fmem, domid); + trace_xen_domid_restrict(rc ? errno : 0); + + return rc; +} + void destroy_hvm_domain(bool reboot); /* shutdown/destroy current domain because of an error */ @@ -99,7 +326,7 @@ static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, #endif /* Xen before 4.6 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 460 +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40600 #ifndef HVM_IOREQSRV_BUFIOREQ_ATOMIC #define HVM_IOREQSRV_BUFIOREQ_ATOMIC 2 @@ -107,8 +334,7 @@ static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, #endif -static inline int xen_get_default_ioreq_server_info(xc_interface *xc, - domid_t dom, +static inline int xen_get_default_ioreq_server_info(domid_t dom, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, evtchn_port_t @@ -117,7 +343,7 @@ static inline int xen_get_default_ioreq_server_info(xc_interface *xc, unsigned long param; int rc; - rc = xc_get_hvm_param(xc, dom, HVM_PARAM_IOREQ_PFN, ¶m); + rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_IOREQ_PFN, ¶m); if (rc < 0) { fprintf(stderr, "failed to get HVM_PARAM_IOREQ_PFN\n"); return -1; @@ -125,7 +351,7 @@ static inline int xen_get_default_ioreq_server_info(xc_interface *xc, *ioreq_pfn = param; - rc = xc_get_hvm_param(xc, dom, HVM_PARAM_BUFIOREQ_PFN, ¶m); + rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_BUFIOREQ_PFN, ¶m); if (rc < 0) { fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_PFN\n"); return -1; @@ -133,7 +359,7 @@ static inline int xen_get_default_ioreq_server_info(xc_interface *xc, *bufioreq_pfn = param; - rc = xc_get_hvm_param(xc, dom, HVM_PARAM_BUFIOREQ_EVTCHN, + rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_BUFIOREQ_EVTCHN, ¶m); if (rc < 0) { fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_EVTCHN\n"); @@ -146,7 +372,7 @@ static inline int xen_get_default_ioreq_server_info(xc_interface *xc, } /* Xen before 4.5 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 450 +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40500 #ifndef HVM_PARAM_BUFIOREQ_EVTCHN #define HVM_PARAM_BUFIOREQ_EVTCHN 26 @@ -156,63 +382,64 @@ static inline int xen_get_default_ioreq_server_info(xc_interface *xc, typedef uint16_t ioservid_t; -static inline void xen_map_memory_section(xc_interface *xc, domid_t dom, +static inline void xen_map_memory_section(domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { } -static inline void xen_unmap_memory_section(xc_interface *xc, domid_t dom, +static inline void xen_unmap_memory_section(domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { } -static inline void xen_map_io_section(xc_interface *xc, domid_t dom, +static inline void xen_map_io_section(domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { } -static inline void xen_unmap_io_section(xc_interface *xc, domid_t dom, +static inline void xen_unmap_io_section(domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { } -static inline void xen_map_pcidev(xc_interface *xc, domid_t dom, +static inline void xen_map_pcidev(domid_t dom, ioservid_t ioservid, PCIDevice *pci_dev) { } -static inline void xen_unmap_pcidev(xc_interface *xc, domid_t dom, +static inline void xen_unmap_pcidev(domid_t dom, ioservid_t ioservid, PCIDevice *pci_dev) { } -static inline void xen_create_ioreq_server(xc_interface *xc, domid_t dom, +static inline void xen_create_ioreq_server(domid_t dom, ioservid_t *ioservid) { } -static inline void xen_destroy_ioreq_server(xc_interface *xc, domid_t dom, +static inline void xen_destroy_ioreq_server(domid_t dom, ioservid_t ioservid) { } -static inline int xen_get_ioreq_server_info(xc_interface *xc, domid_t dom, +static inline int xen_get_ioreq_server_info(domid_t dom, ioservid_t ioservid, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, evtchn_port_t *bufioreq_evtchn) { - return xen_get_default_ioreq_server_info(xc, dom, ioreq_pfn, bufioreq_pfn, + return xen_get_default_ioreq_server_info(dom, ioreq_pfn, + bufioreq_pfn, bufioreq_evtchn); } -static inline int xen_set_ioreq_server_state(xc_interface *xc, domid_t dom, +static inline int xen_set_ioreq_server_state(domid_t dom, ioservid_t ioservid, bool enable) { @@ -224,7 +451,7 @@ static inline int xen_set_ioreq_server_state(xc_interface *xc, domid_t dom, static bool use_default_ioreq_server; -static inline void xen_map_memory_section(xc_interface *xc, domid_t dom, +static inline void xen_map_memory_section(domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { @@ -237,11 +464,11 @@ static inline void xen_map_memory_section(xc_interface *xc, domid_t dom, } trace_xen_map_mmio_range(ioservid, start_addr, end_addr); - xc_hvm_map_io_range_to_ioreq_server(xc, dom, ioservid, 1, - start_addr, end_addr); + xendevicemodel_map_io_range_to_ioreq_server(xen_dmod, dom, ioservid, 1, + start_addr, end_addr); } -static inline void xen_unmap_memory_section(xc_interface *xc, domid_t dom, +static inline void xen_unmap_memory_section(domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { @@ -253,13 +480,12 @@ static inline void xen_unmap_memory_section(xc_interface *xc, domid_t dom, return; } - trace_xen_unmap_mmio_range(ioservid, start_addr, end_addr); - xc_hvm_unmap_io_range_from_ioreq_server(xc, dom, ioservid, 1, - start_addr, end_addr); + xendevicemodel_unmap_io_range_from_ioreq_server(xen_dmod, dom, ioservid, + 1, start_addr, end_addr); } -static inline void xen_map_io_section(xc_interface *xc, domid_t dom, +static inline void xen_map_io_section(domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { @@ -271,13 +497,12 @@ static inline void xen_map_io_section(xc_interface *xc, domid_t dom, return; } - trace_xen_map_portio_range(ioservid, start_addr, end_addr); - xc_hvm_map_io_range_to_ioreq_server(xc, dom, ioservid, 0, - start_addr, end_addr); + xendevicemodel_map_io_range_to_ioreq_server(xen_dmod, dom, ioservid, 0, + start_addr, end_addr); } -static inline void xen_unmap_io_section(xc_interface *xc, domid_t dom, +static inline void xen_unmap_io_section(domid_t dom, ioservid_t ioservid, MemoryRegionSection *section) { @@ -290,11 +515,11 @@ static inline void xen_unmap_io_section(xc_interface *xc, domid_t dom, } trace_xen_unmap_portio_range(ioservid, start_addr, end_addr); - xc_hvm_unmap_io_range_from_ioreq_server(xc, dom, ioservid, 0, - start_addr, end_addr); + xendevicemodel_unmap_io_range_from_ioreq_server(xen_dmod, dom, ioservid, + 0, start_addr, end_addr); } -static inline void xen_map_pcidev(xc_interface *xc, domid_t dom, +static inline void xen_map_pcidev(domid_t dom, ioservid_t ioservid, PCIDevice *pci_dev) { @@ -304,13 +529,13 @@ static inline void xen_map_pcidev(xc_interface *xc, domid_t dom, trace_xen_map_pcidev(ioservid, pci_bus_num(pci_dev->bus), PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); - xc_hvm_map_pcidev_to_ioreq_server(xc, dom, ioservid, - 0, pci_bus_num(pci_dev->bus), - PCI_SLOT(pci_dev->devfn), - PCI_FUNC(pci_dev->devfn)); + xendevicemodel_map_pcidev_to_ioreq_server(xen_dmod, dom, ioservid, 0, + pci_bus_num(pci_dev->bus), + PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); } -static inline void xen_unmap_pcidev(xc_interface *xc, domid_t dom, +static inline void xen_unmap_pcidev(domid_t dom, ioservid_t ioservid, PCIDevice *pci_dev) { @@ -320,17 +545,18 @@ static inline void xen_unmap_pcidev(xc_interface *xc, domid_t dom, trace_xen_unmap_pcidev(ioservid, pci_bus_num(pci_dev->bus), PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); - xc_hvm_unmap_pcidev_from_ioreq_server(xc, dom, ioservid, - 0, pci_bus_num(pci_dev->bus), - PCI_SLOT(pci_dev->devfn), - PCI_FUNC(pci_dev->devfn)); + xendevicemodel_unmap_pcidev_from_ioreq_server(xen_dmod, dom, ioservid, 0, + pci_bus_num(pci_dev->bus), + PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); } -static inline void xen_create_ioreq_server(xc_interface *xc, domid_t dom, +static inline void xen_create_ioreq_server(domid_t dom, ioservid_t *ioservid) { - int rc = xc_hvm_create_ioreq_server(xc, dom, HVM_IOREQSRV_BUFIOREQ_ATOMIC, - ioservid); + int rc = xendevicemodel_create_ioreq_server(xen_dmod, dom, + HVM_IOREQSRV_BUFIOREQ_ATOMIC, + ioservid); if (rc == 0) { trace_xen_ioreq_server_create(*ioservid); @@ -342,7 +568,7 @@ static inline void xen_create_ioreq_server(xc_interface *xc, domid_t dom, trace_xen_default_ioreq_server(); } -static inline void xen_destroy_ioreq_server(xc_interface *xc, domid_t dom, +static inline void xen_destroy_ioreq_server(domid_t dom, ioservid_t ioservid) { if (use_default_ioreq_server) { @@ -350,27 +576,27 @@ static inline void xen_destroy_ioreq_server(xc_interface *xc, domid_t dom, } trace_xen_ioreq_server_destroy(ioservid); - xc_hvm_destroy_ioreq_server(xc, dom, ioservid); + xendevicemodel_destroy_ioreq_server(xen_dmod, dom, ioservid); } -static inline int xen_get_ioreq_server_info(xc_interface *xc, domid_t dom, +static inline int xen_get_ioreq_server_info(domid_t dom, ioservid_t ioservid, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, evtchn_port_t *bufioreq_evtchn) { if (use_default_ioreq_server) { - return xen_get_default_ioreq_server_info(xc, dom, ioreq_pfn, + return xen_get_default_ioreq_server_info(dom, ioreq_pfn, bufioreq_pfn, bufioreq_evtchn); } - return xc_hvm_get_ioreq_server_info(xc, dom, ioservid, - ioreq_pfn, bufioreq_pfn, - bufioreq_evtchn); + return xendevicemodel_get_ioreq_server_info(xen_dmod, dom, ioservid, + ioreq_pfn, bufioreq_pfn, + bufioreq_evtchn); } -static inline int xen_set_ioreq_server_state(xc_interface *xc, domid_t dom, +static inline int xen_set_ioreq_server_state(domid_t dom, ioservid_t ioservid, bool enable) { @@ -379,12 +605,13 @@ static inline int xen_set_ioreq_server_state(xc_interface *xc, domid_t dom, } trace_xen_ioreq_server_state(ioservid, enable); - return xc_hvm_set_ioreq_server_state(xc, dom, ioservid, enable); + return xendevicemodel_set_ioreq_server_state(xen_dmod, dom, ioservid, + enable); } #endif -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 460 +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40600 static inline int xen_xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, unsigned int space, unsigned long idx, @@ -407,7 +634,7 @@ static inline int xen_xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid #endif #ifdef CONFIG_XEN_PV_DOMAIN_BUILD -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 470 +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40700 static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, xen_domain_handle_t handle, uint32_t flags, uint32_t *pdomid) @@ -426,7 +653,7 @@ static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, /* Xen before 4.8 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 480 +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800 typedef void *xengnttab_grant_copy_segment_t; diff --git a/include/migration/migration.h b/include/migration/migration.h index 5720c884f4..ba1a16cbc1 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -128,18 +128,6 @@ struct MigrationIncomingState { MigrationIncomingState *migration_incoming_get_current(void); void migration_incoming_state_destroy(void); -/* - * An outstanding page request, on the source, having been received - * and queued - */ -struct MigrationSrcPageRequest { - RAMBlock *rb; - hwaddr offset; - hwaddr len; - - QSIMPLEQ_ENTRY(MigrationSrcPageRequest) next_req; -}; - struct MigrationState { size_t bytes_xfer; @@ -166,14 +154,9 @@ struct MigrationState int64_t total_time; int64_t downtime; int64_t expected_downtime; - int64_t dirty_pages_rate; - int64_t dirty_bytes_rate; bool enabled_capabilities[MIGRATION_CAPABILITY__MAX]; int64_t xbzrle_cache_size; int64_t setup_time; - int64_t dirty_sync_count; - /* Count of requests incoming from destination */ - int64_t postcopy_requests; /* Flag set once the migration has been asked to enter postcopy */ bool start_postcopy; @@ -186,11 +169,6 @@ struct MigrationState /* Flag set once the migration thread called bdrv_inactivate_all */ bool block_inactive; - /* Queue of outstanding page requests from the destination */ - QemuMutex src_page_req_mutex; - QSIMPLEQ_HEAD(src_page_requests, MigrationSrcPageRequest) src_page_requests; - /* The RAMBlock used in the last src_page_request */ - RAMBlock *last_req_rb; /* The semaphore is used to notify COLO thread that failover is finished */ QemuSemaphore colo_exit_sem; @@ -256,11 +234,11 @@ void remove_migration_state_change_notifier(Notifier *notify); MigrationState *migrate_init(const MigrationParams *params); bool migration_is_blocked(Error **errp); bool migration_in_setup(MigrationState *); -bool migration_is_idle(MigrationState *s); +bool migration_is_idle(void); bool migration_has_finished(MigrationState *); bool migration_has_failed(MigrationState *); /* True if outgoing migration has entered postcopy phase */ -bool migration_in_postcopy(MigrationState *); +bool migration_in_postcopy(void); /* ...and after the device transmission */ bool migration_in_postcopy_after_devices(MigrationState *); MigrationState *migrate_get_current(void); @@ -272,15 +250,14 @@ void migrate_decompress_threads_join(void); uint64_t ram_bytes_remaining(void); uint64_t ram_bytes_transferred(void); uint64_t ram_bytes_total(void); +uint64_t ram_dirty_sync_count(void); +uint64_t ram_dirty_pages_rate(void); +uint64_t ram_postcopy_requests(void); void free_xbzrle_decoded_buf(void); void acct_update_position(QEMUFile *f, size_t size, bool zero); -uint64_t dup_mig_bytes_transferred(void); uint64_t dup_mig_pages_transferred(void); -uint64_t skipped_mig_bytes_transferred(void); -uint64_t skipped_mig_pages_transferred(void); -uint64_t norm_mig_bytes_transferred(void); uint64_t norm_mig_pages_transferred(void); uint64_t xbzrle_mig_bytes_transferred(void); uint64_t xbzrle_mig_pages_transferred(void); @@ -293,8 +270,7 @@ void ram_debug_dump_bitmap(unsigned long *todump, bool expected); /* For outgoing discard bitmap */ int ram_postcopy_send_discard_bitmap(MigrationState *ms); /* For incoming postcopy discard */ -int ram_discard_range(MigrationIncomingState *mis, const char *block_name, - uint64_t start, size_t length); +int ram_discard_range(const char *block_name, uint64_t start, size_t length); int ram_postcopy_incoming_init(MigrationIncomingState *mis); void ram_postcopy_migrated_memory_release(MigrationState *ms); @@ -377,9 +353,8 @@ void savevm_skip_configuration(void); int global_state_store(void); void global_state_store_running(void); -void flush_page_queue(MigrationState *ms); -int ram_save_queue_pages(MigrationState *ms, const char *rbname, - ram_addr_t start, ram_addr_t len); +void migration_page_queue_free(void); +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); uint64_t ram_pagesize_summary(void); PostcopyState postcopy_state_get(void); diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 4834e550f0..f4bf3f1b4e 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -56,7 +56,8 @@ typedef struct SaveVMHandlers { /* This runs outside the iothread lock! */ int (*save_live_setup)(QEMUFile *f, void *opaque); - void (*save_live_pending)(QEMUFile *f, void *opaque, uint64_t max_size, + void (*save_live_pending)(QEMUFile *f, void *opaque, + uint64_t threshold_size, uint64_t *non_postcopiable_pending, uint64_t *postcopiable_pending); LoadStateHandler *load_state; diff --git a/include/net/eth.h b/include/net/eth.h index afeb45be34..09054a506d 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -209,6 +209,7 @@ struct tcp_hdr { #define ETH_P_IPV6 (0x86dd) #define ETH_P_VLAN (0x8100) #define ETH_P_DVLAN (0x88a8) +#define ETH_P_NCSI (0x88f8) #define ETH_P_UNKNOWN (0xffff) #define VLAN_VID_MASK 0x0fff #define IP_HEADER_VERSION_4 (4) diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index 63ea2d0b1e..c318da12d7 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -220,6 +220,8 @@ void bitmap_set(unsigned long *map, long i, long len); void bitmap_set_atomic(unsigned long *map, long i, long len); void bitmap_clear(unsigned long *map, long start, long nr); bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr); +void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, + long nr); unsigned long bitmap_find_next_zero_area(unsigned long *map, unsigned long size, unsigned long start, diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index e0ce9ffb28..18e610083a 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -24,17 +24,9 @@ #define QEMU_NORETURN __attribute__ ((__noreturn__)) -#if QEMU_GNUC_PREREQ(3, 4) #define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -#define QEMU_WARN_UNUSED_RESULT -#endif -#if QEMU_GNUC_PREREQ(4, 0) #define QEMU_SENTINEL __attribute__((sentinel)) -#else -#define QEMU_SENTINEL -#endif #if QEMU_GNUC_PREREQ(4, 3) #define QEMU_ARTIFICIAL __attribute__((always_inline, artificial)) diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index a38be42253..95cf4f4163 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -115,37 +115,7 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) */ static inline int clz32(uint32_t val) { -#if QEMU_GNUC_PREREQ(3, 4) return val ? __builtin_clz(val) : 32; -#else - /* Binary search for the leading one bit. */ - int cnt = 0; - - if (!(val & 0xFFFF0000U)) { - cnt += 16; - val <<= 16; - } - if (!(val & 0xFF000000U)) { - cnt += 8; - val <<= 8; - } - if (!(val & 0xF0000000U)) { - cnt += 4; - val <<= 4; - } - if (!(val & 0xC0000000U)) { - cnt += 2; - val <<= 2; - } - if (!(val & 0x80000000U)) { - cnt++; - val <<= 1; - } - if (!(val & 0x80000000U)) { - cnt++; - } - return cnt; -#endif } /** @@ -168,19 +138,7 @@ static inline int clo32(uint32_t val) */ static inline int clz64(uint64_t val) { -#if QEMU_GNUC_PREREQ(3, 4) return val ? __builtin_clzll(val) : 64; -#else - int cnt = 0; - - if (!(val >> 32)) { - cnt += 32; - } else { - val >>= 32; - } - - return cnt + clz32(val); -#endif } /** @@ -203,39 +161,7 @@ static inline int clo64(uint64_t val) */ static inline int ctz32(uint32_t val) { -#if QEMU_GNUC_PREREQ(3, 4) return val ? __builtin_ctz(val) : 32; -#else - /* Binary search for the trailing one bit. */ - int cnt; - - cnt = 0; - if (!(val & 0x0000FFFFUL)) { - cnt += 16; - val >>= 16; - } - if (!(val & 0x000000FFUL)) { - cnt += 8; - val >>= 8; - } - if (!(val & 0x0000000FUL)) { - cnt += 4; - val >>= 4; - } - if (!(val & 0x00000003UL)) { - cnt += 2; - val >>= 2; - } - if (!(val & 0x00000001UL)) { - cnt++; - val >>= 1; - } - if (!(val & 0x00000001UL)) { - cnt++; - } - - return cnt; -#endif } /** @@ -258,19 +184,7 @@ static inline int cto32(uint32_t val) */ static inline int ctz64(uint64_t val) { -#if QEMU_GNUC_PREREQ(3, 4) return val ? __builtin_ctzll(val) : 64; -#else - int cnt; - - cnt = 0; - if (!((uint32_t)val)) { - cnt += 32; - val >>= 32; - } - - return cnt + ctz32(val); -#endif } /** @@ -322,15 +236,7 @@ static inline int clrsb64(uint64_t val) */ static inline int ctpop8(uint8_t val) { -#if QEMU_GNUC_PREREQ(3, 4) return __builtin_popcount(val); -#else - val = (val & 0x55) + ((val >> 1) & 0x55); - val = (val & 0x33) + ((val >> 2) & 0x33); - val = (val + (val >> 4)) & 0x0f; - - return val; -#endif } /** @@ -339,16 +245,7 @@ static inline int ctpop8(uint8_t val) */ static inline int ctpop16(uint16_t val) { -#if QEMU_GNUC_PREREQ(3, 4) return __builtin_popcount(val); -#else - val = (val & 0x5555) + ((val >> 1) & 0x5555); - val = (val & 0x3333) + ((val >> 2) & 0x3333); - val = (val + (val >> 4)) & 0x0f0f; - val = (val + (val >> 8)) & 0x00ff; - - return val; -#endif } /** @@ -357,16 +254,7 @@ static inline int ctpop16(uint16_t val) */ static inline int ctpop32(uint32_t val) { -#if QEMU_GNUC_PREREQ(3, 4) return __builtin_popcount(val); -#else - val = (val & 0x55555555) + ((val >> 1) & 0x55555555); - val = (val & 0x33333333) + ((val >> 2) & 0x33333333); - val = (val + (val >> 4)) & 0x0f0f0f0f; - val = (val * 0x01010101) >> 24; - - return val; -#endif } /** @@ -375,16 +263,7 @@ static inline int ctpop32(uint32_t val) */ static inline int ctpop64(uint64_t val) { -#if QEMU_GNUC_PREREQ(3, 4) return __builtin_popcountll(val); -#else - val = (val & 0x5555555555555555ULL) + ((val >> 1) & 0x5555555555555555ULL); - val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL); - val = (val + (val >> 4)) & 0x0f0f0f0f0f0f0f0fULL; - val = (val * 0x0101010101010101ULL) >> 56; - - return val; -#endif } /** diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 7842f6d150..af285321b8 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -36,8 +36,9 @@ int inet_ai_family_from_address(InetSocketAddress *addr, Error **errp); InetSocketAddress *inet_parse(const char *str, Error **errp); int inet_connect(const char *str, Error **errp); -int inet_connect_saddr(InetSocketAddress *saddr, Error **errp, - NonBlockingConnectHandler *callback, void *opaque); +int inet_connect_saddr(InetSocketAddress *saddr, + NonBlockingConnectHandler *callback, void *opaque, + Error **errp); NetworkAddressFamily inet_netfamily(int family); @@ -45,8 +46,8 @@ int unix_listen(const char *path, char *ostr, int olen, Error **errp); int unix_connect(const char *path, Error **errp); SocketAddress *socket_parse(const char *str, Error **errp); -int socket_connect(SocketAddress *addr, Error **errp, - NonBlockingConnectHandler *callback, void *opaque); +int socket_connect(SocketAddress *addr, NonBlockingConnectHandler *callback, + void *opaque, Error **errp); int socket_listen(SocketAddress *addr, Error **errp); void socket_listen_cleanup(int fd, Error **errp); int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index e95f28cfec..f08d327aec 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -23,6 +23,7 @@ typedef struct CPUAddressSpace CPUAddressSpace; typedef struct CPUState CPUState; typedef struct DeviceListener DeviceListener; typedef struct DeviceState DeviceState; +typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; typedef struct DisplayChangeListener DisplayChangeListener; typedef struct DisplayState DisplayState; typedef struct DisplaySurface DisplaySurface; diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 7462228ac1..840ad6134c 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -99,7 +99,7 @@ int blk_get_refcnt(BlockBackend *blk); void blk_ref(BlockBackend *blk); void blk_unref(BlockBackend *blk); void blk_remove_all_bs(void); -const char *blk_name(BlockBackend *blk); +const char *blk_name(const BlockBackend *blk); BlockBackend *blk_by_name(const char *name); BlockBackend *blk_next(BlockBackend *blk); bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); @@ -225,7 +225,7 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, int count, BdrvRequestFlags flags); int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, int count); -int blk_truncate(BlockBackend *blk, int64_t offset); +int blk_truncate(BlockBackend *blk, int64_t offset, Error **errp); int blk_pdiscard(BlockBackend *blk, int64_t offset, int count); int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int64_t pos, int size); diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index ecae0cff19..ed6a437f4d 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -62,6 +62,7 @@ struct HostMemoryBackend { MemoryRegion mr; }; +bool host_memory_backend_mr_inited(HostMemoryBackend *backend); MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 24281fc7f8..5cc83f2003 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -527,5 +527,6 @@ int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source); * Returns: 0 on success, or a negative errno on failure. */ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target); +struct ppc_radix_page_info *kvm_get_radix_page_info(void); int kvm_get_max_memslots(void); #endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 576c7ce640..16175f7295 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -67,7 +67,7 @@ int qemu_reset_requested_get(void); void qemu_system_killed(int signal, pid_t pid); void qemu_system_reset(bool report); void qemu_system_guest_panicked(GuestPanicInformation *info); -size_t qemu_target_page_bits(void); +size_t qemu_target_page_size(void); void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); diff --git a/io/channel-socket.c b/io/channel-socket.c index 64b36f58be..53386b7ba3 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -140,7 +140,7 @@ int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, int fd; trace_qio_channel_socket_connect_sync(ioc, addr); - fd = socket_connect(addr, errp, NULL, NULL); + fd = socket_connect(addr, NULL, NULL, errp); if (fd < 0) { trace_qio_channel_socket_connect_fail(ioc); return -1; @@ -1583,7 +1583,7 @@ static void memory_region_update_iommu_notify_flags(MemoryRegion *mr) IOMMUNotifierFlag flags = IOMMU_NOTIFIER_NONE; IOMMUNotifier *iommu_notifier; - QLIST_FOREACH(iommu_notifier, &mr->iommu_notify, node) { + IOMMU_NOTIFIER_FOREACH(iommu_notifier, mr) { flags |= iommu_notifier->notifier_flags; } @@ -1606,6 +1606,7 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, /* We need to register for at least one bitfield */ assert(n->notifier_flags != IOMMU_NOTIFIER_NONE); + assert(n->start <= n->end); QLIST_INSERT_HEAD(&mr->iommu_notify, n, node); memory_region_update_iommu_notify_flags(mr); } @@ -1625,6 +1626,12 @@ void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n, hwaddr addr, granularity; IOMMUTLBEntry iotlb; + /* If the IOMMU has its own replay callback, override */ + if (mr->iommu_ops->replay) { + mr->iommu_ops->replay(mr, n); + return; + } + granularity = memory_region_iommu_get_min_page_size(mr); for (addr = 0; addr < memory_region_size(mr); addr += granularity) { @@ -1641,6 +1648,15 @@ void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n, } } +void memory_region_iommu_replay_all(MemoryRegion *mr) +{ + IOMMUNotifier *notifier; + + IOMMU_NOTIFIER_FOREACH(notifier, mr) { + memory_region_iommu_replay(mr, notifier, false); + } +} + void memory_region_unregister_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n) { @@ -1652,24 +1668,40 @@ void memory_region_unregister_iommu_notifier(MemoryRegion *mr, memory_region_update_iommu_notify_flags(mr); } -void memory_region_notify_iommu(MemoryRegion *mr, - IOMMUTLBEntry entry) +void memory_region_notify_one(IOMMUNotifier *notifier, + IOMMUTLBEntry *entry) { - IOMMUNotifier *iommu_notifier; IOMMUNotifierFlag request_flags; - assert(memory_region_is_iommu(mr)); + /* + * Skip the notification if the notification does not overlap + * with registered range. + */ + if (notifier->start > entry->iova + entry->addr_mask + 1 || + notifier->end < entry->iova) { + return; + } - if (entry.perm & IOMMU_RW) { + if (entry->perm & IOMMU_RW) { request_flags = IOMMU_NOTIFIER_MAP; } else { request_flags = IOMMU_NOTIFIER_UNMAP; } - QLIST_FOREACH(iommu_notifier, &mr->iommu_notify, node) { - if (iommu_notifier->notifier_flags & request_flags) { - iommu_notifier->notify(iommu_notifier, &entry); - } + if (notifier->notifier_flags & request_flags) { + notifier->notify(notifier, entry); + } +} + +void memory_region_notify_iommu(MemoryRegion *mr, + IOMMUTLBEntry entry) +{ + IOMMUNotifier *iommu_notifier; + + assert(memory_region_is_iommu(mr)); + + IOMMU_NOTIFIER_FOREACH(iommu_notifier, mr) { + memory_region_notify_one(iommu_notifier, &entry); } } @@ -1716,6 +1748,23 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, memory_region_get_ram_addr(mr) + addr, size, client); } +DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, + hwaddr addr, + hwaddr size, + unsigned client) +{ + assert(mr->ram_block); + return cpu_physical_memory_snapshot_and_clear_dirty( + memory_region_get_ram_addr(mr) + addr, size, client); +} + +bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap, + hwaddr addr, hwaddr size) +{ + assert(mr->ram_block); + return cpu_physical_memory_snapshot_get_dirty(snap, + memory_region_get_ram_addr(mr) + addr, size); +} void memory_region_sync_dirty_bitmap(MemoryRegion *mr) { diff --git a/migration/block.c b/migration/block.c index 7734ff728a..060087fa32 100644 --- a/migration/block.c +++ b/migration/block.c @@ -885,6 +885,8 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) int64_t total_sectors = 0; int nr_sectors; int ret; + BlockDriverInfo bdi; + int cluster_size = BLOCK_SIZE; do { addr = qemu_get_be64(f); @@ -919,6 +921,15 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) error_report_err(local_err); return -EINVAL; } + + ret = bdrv_get_info(blk_bs(blk), &bdi); + if (ret == 0 && bdi.cluster_size > 0 && + bdi.cluster_size <= BLOCK_SIZE && + BLOCK_SIZE % bdi.cluster_size == 0) { + cluster_size = bdi.cluster_size; + } else { + cluster_size = BLOCK_SIZE; + } } if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) { @@ -932,10 +943,30 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) nr_sectors * BDRV_SECTOR_SIZE, BDRV_REQ_MAY_UNMAP); } else { + int i; + int64_t cur_addr; + uint8_t *cur_buf; + buf = g_malloc(BLOCK_SIZE); qemu_get_buffer(f, buf, BLOCK_SIZE); - ret = blk_pwrite(blk, addr * BDRV_SECTOR_SIZE, buf, - nr_sectors * BDRV_SECTOR_SIZE, 0); + for (i = 0; i < BLOCK_SIZE / cluster_size; i++) { + cur_addr = addr * BDRV_SECTOR_SIZE + i * cluster_size; + cur_buf = buf + i * cluster_size; + + if ((!block_mig_state.zero_blocks || + cluster_size < BLOCK_SIZE) && + buffer_is_zero(cur_buf, cluster_size)) { + ret = blk_pwrite_zeroes(blk, cur_addr, + cluster_size, + BDRV_REQ_MAY_UNMAP); + } else { + ret = blk_pwrite(blk, cur_addr, cur_buf, + cluster_size, 0); + } + if (ret < 0) { + break; + } + } g_free(buf); } diff --git a/migration/colo.c b/migration/colo.c index c19eb3f073..963c80256d 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -153,6 +153,7 @@ void qmp_xen_set_replication(bool enable, bool primary, bool has_failover, bool failover, Error **errp) { +#ifdef CONFIG_REPLICATION ReplicationMode mode = primary ? REPLICATION_MODE_PRIMARY : REPLICATION_MODE_SECONDARY; @@ -171,10 +172,14 @@ void qmp_xen_set_replication(bool enable, bool primary, } replication_stop_all(failover, failover ? NULL : errp); } +#else + abort(); +#endif } ReplicationStatus *qmp_query_xen_replication_status(Error **errp) { +#ifdef CONFIG_REPLICATION Error *err = NULL; ReplicationStatus *s = g_new0(ReplicationStatus, 1); @@ -189,11 +194,18 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp) error_free(err); return s; +#else + abort(); +#endif } void qmp_xen_colo_do_checkpoint(Error **errp) { +#ifdef CONFIG_REPLICATION replication_do_checkpoint_all(errp); +#else + abort(); +#endif } static void colo_send_message(QEMUFile *f, COLOMessage msg, diff --git a/migration/migration.c b/migration/migration.c index ad4036fdb1..353f2728cf 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -109,7 +109,6 @@ MigrationState *migrate_get_current(void) }; if (!once) { - qemu_mutex_init(¤t_migration.src_page_req_mutex); current_migration.parameters.tls_creds = g_strdup(""); current_migration.parameters.tls_hostname = g_strdup(""); once = true; @@ -436,9 +435,6 @@ static void process_incoming_migration_co(void *opaque) qemu_thread_join(&mis->colo_incoming_thread); } - qemu_fclose(f); - free_xbzrle_decoded_buf(); - if (ret < 0) { migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); @@ -447,6 +443,9 @@ static void process_incoming_migration_co(void *opaque) exit(EXIT_FAILURE); } + qemu_fclose(f); + free_xbzrle_decoded_buf(); + mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); qemu_bh_schedule(mis->bh); } @@ -651,16 +650,19 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->ram->transferred = ram_bytes_transferred(); info->ram->total = ram_bytes_total(); info->ram->duplicate = dup_mig_pages_transferred(); - info->ram->skipped = skipped_mig_pages_transferred(); + /* legacy value. It is not used anymore */ + info->ram->skipped = 0; info->ram->normal = norm_mig_pages_transferred(); - info->ram->normal_bytes = norm_mig_bytes_transferred(); + info->ram->normal_bytes = norm_mig_pages_transferred() * + qemu_target_page_size(); info->ram->mbps = s->mbps; - info->ram->dirty_sync_count = s->dirty_sync_count; - info->ram->postcopy_requests = s->postcopy_requests; + info->ram->dirty_sync_count = ram_dirty_sync_count(); + info->ram->postcopy_requests = ram_postcopy_requests(); + info->ram->page_size = qemu_target_page_size(); if (s->state != MIGRATION_STATUS_COMPLETED) { info->ram->remaining = ram_bytes_remaining(); - info->ram->dirty_pages_rate = s->dirty_pages_rate; + info->ram->dirty_pages_rate = ram_dirty_pages_rate(); } } @@ -955,7 +957,7 @@ static void migrate_fd_cleanup(void *opaque) qemu_bh_delete(s->cleanup_bh); s->cleanup_bh = NULL; - flush_page_queue(s); + migration_page_queue_free(); if (s->to_dst_file) { trace_migrate_fd_cleanup(); @@ -1061,21 +1063,21 @@ bool migration_has_failed(MigrationState *s) s->state == MIGRATION_STATUS_FAILED); } -bool migration_in_postcopy(MigrationState *s) +bool migration_in_postcopy(void) { + MigrationState *s = migrate_get_current(); + return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); } bool migration_in_postcopy_after_devices(MigrationState *s) { - return migration_in_postcopy(s) && s->postcopy_after_devices; + return migration_in_postcopy() && s->postcopy_after_devices; } -bool migration_is_idle(MigrationState *s) +bool migration_is_idle(void) { - if (!s) { - s = migrate_get_current(); - } + MigrationState *s = migrate_get_current(); switch (s->state) { case MIGRATION_STATUS_NONE: @@ -1116,22 +1118,15 @@ MigrationState *migrate_init(const MigrationParams *params) s->mbps = 0.0; s->downtime = 0; s->expected_downtime = 0; - s->dirty_pages_rate = 0; - s->dirty_bytes_rate = 0; s->setup_time = 0; - s->dirty_sync_count = 0; s->start_postcopy = false; s->postcopy_after_devices = false; - s->postcopy_requests = 0; s->migration_thread_running = false; - s->last_req_rb = NULL; error_free(s->error); s->error = NULL; migrate_set_state(&s->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); - QSIMPLEQ_INIT(&s->src_page_requests); - s->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); return s; } @@ -1147,7 +1142,7 @@ int migrate_add_blocker(Error *reason, Error **errp) return -EACCES; } - if (migration_is_idle(NULL)) { + if (migration_is_idle()) { migration_blockers = g_slist_prepend(migration_blockers, reason); return 0; } @@ -1485,7 +1480,7 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, return; } - if (ram_save_queue_pages(ms, rbname, start, len)) { + if (ram_save_queue_pages(rbname, start, len)) { mark_source_rp_bad(ms); } } @@ -1915,7 +1910,12 @@ static void *migration_thread(void *opaque) int64_t initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); int64_t initial_bytes = 0; - int64_t max_size = 0; + /* + * The final stage happens when the remaining data is smaller than + * this threshold; it's calculated from the requested downtime and + * measured bandwidth + */ + int64_t threshold_size = 0; int64_t start_time = initial_time; int64_t end_time; bool old_vm_running = false; @@ -1946,7 +1946,6 @@ static void *migration_thread(void *opaque) qemu_savevm_state_begin(s->to_dst_file, &s->params); s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; - current_active_state = MIGRATION_STATUS_ACTIVE; migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); @@ -1960,17 +1959,17 @@ static void *migration_thread(void *opaque) if (!qemu_file_rate_limit(s->to_dst_file)) { uint64_t pend_post, pend_nonpost; - qemu_savevm_state_pending(s->to_dst_file, max_size, &pend_nonpost, - &pend_post); + qemu_savevm_state_pending(s->to_dst_file, threshold_size, + &pend_nonpost, &pend_post); pending_size = pend_nonpost + pend_post; - trace_migrate_pending(pending_size, max_size, + trace_migrate_pending(pending_size, threshold_size, pend_post, pend_nonpost); - if (pending_size && pending_size >= max_size) { + if (pending_size && pending_size >= threshold_size) { /* Still a significant amount to transfer */ if (migrate_postcopy_ram() && s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE && - pend_nonpost <= max_size && + pend_nonpost <= threshold_size && atomic_read(&s->start_postcopy)) { if (!postcopy_start(s, &old_vm_running)) { @@ -2002,17 +2001,18 @@ static void *migration_thread(void *opaque) initial_bytes; uint64_t time_spent = current_time - initial_time; double bandwidth = (double)transferred_bytes / time_spent; - max_size = bandwidth * s->parameters.downtime_limit; + threshold_size = bandwidth * s->parameters.downtime_limit; s->mbps = (((double) transferred_bytes * 8.0) / ((double) time_spent / 1000.0)) / 1000.0 / 1000.0; trace_migrate_transferred(transferred_bytes, time_spent, - bandwidth, max_size); + bandwidth, threshold_size); /* if we haven't sent anything, we don't want to recalculate 10000 is a small enough number for our purposes */ - if (s->dirty_bytes_rate && transferred_bytes > 10000) { - s->expected_downtime = s->dirty_bytes_rate / bandwidth; + if (ram_dirty_pages_rate() && transferred_bytes > 10000) { + s->expected_downtime = ram_dirty_pages_rate() * + qemu_target_page_size() / bandwidth; } qemu_file_reset_rate_limit(s->to_dst_file); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index dc80dbb67f..85fd8d72b3 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -123,7 +123,7 @@ bool postcopy_ram_supported_by_host(void) struct uffdio_range range_struct; uint64_t feature_mask; - if ((1ul << qemu_target_page_bits()) > pagesize) { + if (qemu_target_page_size() > pagesize) { error_report("Target page size bigger than host page size"); goto out; } @@ -213,8 +213,6 @@ out: static int init_range(const char *block_name, void *host_addr, ram_addr_t offset, ram_addr_t length, void *opaque) { - MigrationIncomingState *mis = opaque; - trace_postcopy_init_range(block_name, host_addr, offset, length); /* @@ -223,7 +221,7 @@ static int init_range(const char *block_name, void *host_addr, * - we're going to get the copy from the source anyway. * (Precopy will just overwrite this data, so doesn't need the discard) */ - if (ram_discard_range(mis, block_name, 0, length)) { + if (ram_discard_range(block_name, 0, length)) { return -1; } @@ -271,7 +269,7 @@ static int cleanup_range(const char *block_name, void *host_addr, */ int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) { - if (qemu_ram_foreach_block(init_range, mis)) { + if (qemu_ram_foreach_block(init_range, NULL)) { return -1; } @@ -745,10 +743,10 @@ PostcopyDiscardState *postcopy_discard_send_init(MigrationState *ms, void postcopy_discard_send_range(MigrationState *ms, PostcopyDiscardState *pds, unsigned long start, unsigned long length) { - size_t tp_bits = qemu_target_page_bits(); + size_t tp_size = qemu_target_page_size(); /* Convert to byte offsets within the RAM block */ - pds->start_list[pds->cur_entry] = (start - pds->offset) << tp_bits; - pds->length_list[pds->cur_entry] = length << tp_bits; + pds->start_list[pds->cur_entry] = (start - pds->offset) * tp_size; + pds->length_list[pds->cur_entry] = length * tp_size; trace_postcopy_discard_send_range(pds->ramblock_name, start, length); pds->cur_entry++; pds->nsentwords++; diff --git a/migration/ram.c b/migration/ram.c index de1e0a3b18..f48664ec62 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -45,10 +45,6 @@ #include "qemu/rcu_queue.h" #include "migration/colo.h" -static int dirty_rate_high_cnt; - -static uint64_t bitmap_sync_count; - /***********************************************************/ /* ram save/restore */ @@ -96,11 +92,17 @@ static void XBZRLE_cache_unlock(void) qemu_mutex_unlock(&XBZRLE.lock); } -/* - * called from qmp_migrate_set_cache_size in main thread, possibly while - * a migration is in progress. - * A running migration maybe using the cache and might finish during this - * call, hence changes to the cache are protected by XBZRLE.lock(). +/** + * xbzrle_cache_resize: resize the xbzrle cache + * + * This function is called from qmp_migrate_set_cache_size in main + * thread, possibly while a migration is in progress. A running + * migration may be using the cache and might finish during this call, + * hence changes to the cache are protected by XBZRLE.lock(). + * + * Returns the new_size or negative in case of error. + * + * @new_size: new cache size */ int64_t xbzrle_cache_resize(int64_t new_size) { @@ -136,115 +138,171 @@ out: return ret; } -/* accounting for migration statistics */ -typedef struct AccountingInfo { - uint64_t dup_pages; - uint64_t skipped_pages; +struct RAMBitmap { + struct rcu_head rcu; + /* Main migration bitmap */ + unsigned long *bmap; + /* bitmap of pages that haven't been sent even once + * only maintained and used in postcopy at the moment + * where it's used to send the dirtymap at the start + * of the postcopy phase + */ + unsigned long *unsentmap; +}; +typedef struct RAMBitmap RAMBitmap; + +/* + * An outstanding page request, on the source, having been received + * and queued + */ +struct RAMSrcPageRequest { + RAMBlock *rb; + hwaddr offset; + hwaddr len; + + QSIMPLEQ_ENTRY(RAMSrcPageRequest) next_req; +}; + +/* State of RAM for migration */ +struct RAMState { + /* QEMUFile used for this migration */ + QEMUFile *f; + /* Last block that we have visited searching for dirty pages */ + RAMBlock *last_seen_block; + /* Last block from where we have sent data */ + RAMBlock *last_sent_block; + /* Last dirty target page we have sent */ + ram_addr_t last_page; + /* last ram version we have seen */ + uint32_t last_version; + /* We are in the first round */ + bool ram_bulk_stage; + /* How many times we have dirty too many pages */ + int dirty_rate_high_cnt; + /* How many times we have synchronized the bitmap */ + uint64_t bitmap_sync_count; + /* these variables are used for bitmap sync */ + /* last time we did a full bitmap_sync */ + int64_t time_last_bitmap_sync; + /* bytes transferred at start_time */ + uint64_t bytes_xfer_prev; + /* number of dirty pages since start_time */ + uint64_t num_dirty_pages_period; + /* xbzrle misses since the beginning of the period */ + uint64_t xbzrle_cache_miss_prev; + /* number of iterations at the beginning of period */ + uint64_t iterations_prev; + /* Accounting fields */ + /* number of zero pages. It used to be pages filled by the same char. */ + uint64_t zero_pages; + /* number of normal transferred pages */ uint64_t norm_pages; + /* Iterations since start */ uint64_t iterations; + /* xbzrle transmitted bytes. Notice that this is with + * compression, they can't be calculated from the pages */ uint64_t xbzrle_bytes; + /* xbzrle transmmited pages */ uint64_t xbzrle_pages; + /* xbzrle number of cache miss */ uint64_t xbzrle_cache_miss; + /* xbzrle miss rate */ double xbzrle_cache_miss_rate; + /* xbzrle number of overflows */ uint64_t xbzrle_overflows; -} AccountingInfo; + /* number of dirty bits in the bitmap */ + uint64_t migration_dirty_pages; + /* total number of bytes transferred */ + uint64_t bytes_transferred; + /* number of dirtied pages in the last second */ + uint64_t dirty_pages_rate; + /* Count of requests incoming from destination */ + uint64_t postcopy_requests; + /* protects modification of the bitmap */ + QemuMutex bitmap_mutex; + /* Ram Bitmap protected by RCU */ + RAMBitmap *ram_bitmap; + /* The RAMBlock used in the last src_page_requests */ + RAMBlock *last_req_rb; + /* Queue of outstanding page requests from the destination */ + QemuMutex src_page_req_mutex; + QSIMPLEQ_HEAD(src_page_requests, RAMSrcPageRequest) src_page_requests; +}; +typedef struct RAMState RAMState; -static AccountingInfo acct_info; +static RAMState ram_state; -static void acct_clear(void) +uint64_t dup_mig_pages_transferred(void) { - memset(&acct_info, 0, sizeof(acct_info)); + return ram_state.zero_pages; } -uint64_t dup_mig_bytes_transferred(void) +uint64_t norm_mig_pages_transferred(void) { - return acct_info.dup_pages * TARGET_PAGE_SIZE; + return ram_state.norm_pages; } -uint64_t dup_mig_pages_transferred(void) +uint64_t xbzrle_mig_bytes_transferred(void) { - return acct_info.dup_pages; + return ram_state.xbzrle_bytes; } -uint64_t skipped_mig_bytes_transferred(void) +uint64_t xbzrle_mig_pages_transferred(void) { - return acct_info.skipped_pages * TARGET_PAGE_SIZE; + return ram_state.xbzrle_pages; } -uint64_t skipped_mig_pages_transferred(void) +uint64_t xbzrle_mig_pages_cache_miss(void) { - return acct_info.skipped_pages; + return ram_state.xbzrle_cache_miss; } -uint64_t norm_mig_bytes_transferred(void) +double xbzrle_mig_cache_miss_rate(void) { - return acct_info.norm_pages * TARGET_PAGE_SIZE; + return ram_state.xbzrle_cache_miss_rate; } -uint64_t norm_mig_pages_transferred(void) +uint64_t xbzrle_mig_pages_overflow(void) { - return acct_info.norm_pages; + return ram_state.xbzrle_overflows; } -uint64_t xbzrle_mig_bytes_transferred(void) +uint64_t ram_bytes_transferred(void) { - return acct_info.xbzrle_bytes; + return ram_state.bytes_transferred; } -uint64_t xbzrle_mig_pages_transferred(void) +uint64_t ram_bytes_remaining(void) { - return acct_info.xbzrle_pages; + return ram_state.migration_dirty_pages * TARGET_PAGE_SIZE; } -uint64_t xbzrle_mig_pages_cache_miss(void) +uint64_t ram_dirty_sync_count(void) { - return acct_info.xbzrle_cache_miss; + return ram_state.bitmap_sync_count; } -double xbzrle_mig_cache_miss_rate(void) +uint64_t ram_dirty_pages_rate(void) { - return acct_info.xbzrle_cache_miss_rate; + return ram_state.dirty_pages_rate; } -uint64_t xbzrle_mig_pages_overflow(void) +uint64_t ram_postcopy_requests(void) { - return acct_info.xbzrle_overflows; + return ram_state.postcopy_requests; } -/* This is the last block that we have visited serching for dirty pages - */ -static RAMBlock *last_seen_block; -/* This is the last block from where we have sent data */ -static RAMBlock *last_sent_block; -static ram_addr_t last_offset; -static QemuMutex migration_bitmap_mutex; -static uint64_t migration_dirty_pages; -static uint32_t last_version; -static bool ram_bulk_stage; - /* used by the search for pages to send */ struct PageSearchStatus { /* Current block being searched */ RAMBlock *block; - /* Current offset to search from */ - ram_addr_t offset; + /* Current page to search from */ + unsigned long page; /* Set once we wrap around */ bool complete_round; }; typedef struct PageSearchStatus PageSearchStatus; -static struct BitmapRcu { - struct rcu_head rcu; - /* Main migration bitmap */ - unsigned long *bmap; - /* bitmap of pages that haven't been sent even once - * only maintained and used in postcopy at the moment - * where it's used to send the dirtymap at the start - * of the postcopy phase - */ - unsigned long *unsentmap; -} *migration_bitmap_rcu; - struct CompressParam { bool done; bool quit; @@ -278,7 +336,6 @@ static QemuCond comp_done_cond; /* The empty QEMUFileOps will be used by file in CompressParam */ static const QEMUFileOps empty_ops = { }; -static bool compression_switch; static DecompressParam *decomp_param; static QemuThread *decompress_threads; static QemuMutex decomp_done_lock; @@ -323,6 +380,7 @@ static inline void terminate_compression_threads(void) int idx, thread_count; thread_count = migrate_compress_threads(); + for (idx = 0; idx < thread_count; idx++) { qemu_mutex_lock(&comp_param[idx].mutex); comp_param[idx].quit = true; @@ -361,7 +419,6 @@ void migrate_compress_threads_create(void) if (!migrate_use_compression()) { return; } - compression_switch = true; thread_count = migrate_compress_threads(); compress_threads = g_new0(QemuThread, thread_count); comp_param = g_new0(CompressParam, thread_count); @@ -383,38 +440,45 @@ void migrate_compress_threads_create(void) } /** - * save_page_header: Write page header to wire + * save_page_header: write page header to wire * * If this is the 1st block, it also writes the block identification * - * Returns: Number of bytes written + * Returns the number of bytes written * * @f: QEMUFile where to send the data * @block: block that contains the page we want to send * @offset: offset inside the block for the page * in the lower bits, it contains flags */ -static size_t save_page_header(QEMUFile *f, RAMBlock *block, ram_addr_t offset) +static size_t save_page_header(RAMState *rs, RAMBlock *block, ram_addr_t offset) { size_t size, len; - qemu_put_be64(f, offset); + if (block == rs->last_sent_block) { + offset |= RAM_SAVE_FLAG_CONTINUE; + } + qemu_put_be64(rs->f, offset); size = 8; if (!(offset & RAM_SAVE_FLAG_CONTINUE)) { len = strlen(block->idstr); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *)block->idstr, len); + qemu_put_byte(rs->f, len); + qemu_put_buffer(rs->f, (uint8_t *)block->idstr, len); size += 1 + len; + rs->last_sent_block = block; } return size; } -/* Reduce amount of guest cpu execution to hopefully slow down memory writes. - * If guest dirty memory rate is reduced below the rate at which we can - * transfer pages to the destination then we should be able to complete - * migration. Some workloads dirty memory way too fast and will not effectively - * converge, even with auto-converge. +/** + * mig_throttle_guest_down: throotle down the guest + * + * Reduce amount of guest cpu execution to hopefully slow down memory + * writes. If guest dirty memory rate is reduced below the rate at + * which we can transfer pages to the destination then we should be + * able to complete migration. Some workloads dirty memory way too + * fast and will not effectively converge, even with auto-converge. */ static void mig_throttle_guest_down(void) { @@ -431,22 +495,28 @@ static void mig_throttle_guest_down(void) } } -/* Update the xbzrle cache to reflect a page that's been sent as all 0. +/** + * xbzrle_cache_zero_page: insert a zero page in the XBZRLE cache + * + * @rs: current RAM state + * @current_addr: address for the zero page + * + * Update the xbzrle cache to reflect a page that's been sent as all 0. * The important thing is that a stale (not-yet-0'd) page be replaced * by the new data. * As a bonus, if the page wasn't in the cache it gets added so that - * when a small write is made into the 0'd page it gets XBZRLE sent + * when a small write is made into the 0'd page it gets XBZRLE sent. */ -static void xbzrle_cache_zero_page(ram_addr_t current_addr) +static void xbzrle_cache_zero_page(RAMState *rs, ram_addr_t current_addr) { - if (ram_bulk_stage || !migrate_use_xbzrle()) { + if (rs->ram_bulk_stage || !migrate_use_xbzrle()) { return; } /* We don't care if this fails to allocate a new cache page * as long as it updated an old one */ cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE, - bitmap_sync_count); + rs->bitmap_sync_count); } #define ENCODING_FLAG_XBZRLE 0x1 @@ -458,27 +528,25 @@ static void xbzrle_cache_zero_page(ram_addr_t current_addr) * 0 means that page is identical to the one already sent * -1 means that xbzrle would be longer than normal * - * @f: QEMUFile where to send the data - * @current_data: - * @current_addr: + * @rs: current RAM state + * @current_data: pointer to the address of the page contents + * @current_addr: addr of the page * @block: block that contains the page we want to send * @offset: offset inside the block for the page * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes */ -static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, +static int save_xbzrle_page(RAMState *rs, uint8_t **current_data, ram_addr_t current_addr, RAMBlock *block, - ram_addr_t offset, bool last_stage, - uint64_t *bytes_transferred) + ram_addr_t offset, bool last_stage) { int encoded_len = 0, bytes_xbzrle; uint8_t *prev_cached_page; - if (!cache_is_cached(XBZRLE.cache, current_addr, bitmap_sync_count)) { - acct_info.xbzrle_cache_miss++; + if (!cache_is_cached(XBZRLE.cache, current_addr, rs->bitmap_sync_count)) { + rs->xbzrle_cache_miss++; if (!last_stage) { if (cache_insert(XBZRLE.cache, current_addr, *current_data, - bitmap_sync_count) == -1) { + rs->bitmap_sync_count) == -1) { return -1; } else { /* update *current_data when the page has been @@ -503,7 +571,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, return 0; } else if (encoded_len == -1) { trace_save_xbzrle_page_overflow(); - acct_info.xbzrle_overflows++; + rs->xbzrle_overflows++; /* update data in the cache */ if (!last_stage) { memcpy(prev_cached_page, *current_data, TARGET_PAGE_SIZE); @@ -518,92 +586,86 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, } /* Send XBZRLE based compressed page */ - bytes_xbzrle = save_page_header(f, block, offset | RAM_SAVE_FLAG_XBZRLE); - qemu_put_byte(f, ENCODING_FLAG_XBZRLE); - qemu_put_be16(f, encoded_len); - qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len); + bytes_xbzrle = save_page_header(rs, block, + offset | RAM_SAVE_FLAG_XBZRLE); + qemu_put_byte(rs->f, ENCODING_FLAG_XBZRLE); + qemu_put_be16(rs->f, encoded_len); + qemu_put_buffer(rs->f, XBZRLE.encoded_buf, encoded_len); bytes_xbzrle += encoded_len + 1 + 2; - acct_info.xbzrle_pages++; - acct_info.xbzrle_bytes += bytes_xbzrle; - *bytes_transferred += bytes_xbzrle; + rs->xbzrle_pages++; + rs->xbzrle_bytes += bytes_xbzrle; + rs->bytes_transferred += bytes_xbzrle; return 1; } -/* Called with rcu_read_lock() to protect migration_bitmap - * rb: The RAMBlock to search for dirty pages in - * start: Start address (typically so we can continue from previous page) - * ram_addr_abs: Pointer into which to store the address of the dirty page - * within the global ram_addr space +/** + * migration_bitmap_find_dirty: find the next dirty page from start + * + * Called with rcu_read_lock() to protect migration_bitmap * - * Returns: byte offset within memory region of the start of a dirty page + * Returns the byte offset within memory region of the start of a dirty page + * + * @rs: current RAM state + * @rb: RAMBlock where to search for dirty pages + * @start: page where we start the search */ static inline -ram_addr_t migration_bitmap_find_dirty(RAMBlock *rb, - ram_addr_t start, - ram_addr_t *ram_addr_abs) +unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, + unsigned long start) { unsigned long base = rb->offset >> TARGET_PAGE_BITS; - unsigned long nr = base + (start >> TARGET_PAGE_BITS); + unsigned long nr = base + start; uint64_t rb_size = rb->used_length; unsigned long size = base + (rb_size >> TARGET_PAGE_BITS); unsigned long *bitmap; unsigned long next; - bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; - if (ram_bulk_stage && nr > base) { + bitmap = atomic_rcu_read(&rs->ram_bitmap)->bmap; + if (rs->ram_bulk_stage && nr > base) { next = nr + 1; } else { next = find_next_bit(bitmap, size, nr); } - *ram_addr_abs = next << TARGET_PAGE_BITS; - return (next - base) << TARGET_PAGE_BITS; + return next - base; } -static inline bool migration_bitmap_clear_dirty(ram_addr_t addr) +static inline bool migration_bitmap_clear_dirty(RAMState *rs, + RAMBlock *rb, + unsigned long page) { bool ret; - int nr = addr >> TARGET_PAGE_BITS; - unsigned long *bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; + unsigned long *bitmap = atomic_rcu_read(&rs->ram_bitmap)->bmap; + unsigned long nr = (rb->offset >> TARGET_PAGE_BITS) + page; ret = test_and_clear_bit(nr, bitmap); if (ret) { - migration_dirty_pages--; + rs->migration_dirty_pages--; } return ret; } -static int64_t num_dirty_pages_period; -static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) +static void migration_bitmap_sync_range(RAMState *rs, RAMBlock *rb, + ram_addr_t start, ram_addr_t length) { unsigned long *bitmap; - bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; - migration_dirty_pages += cpu_physical_memory_sync_dirty_bitmap(bitmap, - start, length, &num_dirty_pages_period); -} - -/* Fix me: there are too many global variables used in migration process. */ -static int64_t start_time; -static int64_t bytes_xfer_prev; -static uint64_t xbzrle_cache_miss_prev; -static uint64_t iterations_prev; - -static void migration_bitmap_sync_init(void) -{ - start_time = 0; - bytes_xfer_prev = 0; - num_dirty_pages_period = 0; - xbzrle_cache_miss_prev = 0; - iterations_prev = 0; + bitmap = atomic_rcu_read(&rs->ram_bitmap)->bmap; + rs->migration_dirty_pages += + cpu_physical_memory_sync_dirty_bitmap(bitmap, rb, start, length, + &rs->num_dirty_pages_period); } -/* Returns a summary bitmap of the page sizes of all RAMBlocks; - * for VMs with just normal pages this is equivalent to the - * host page size. If it's got some huge pages then it's the OR - * of all the different page sizes. +/** + * ram_pagesize_summary: calculate all the pagesizes of a VM + * + * Returns a summary bitmap of the page sizes of all RAMBlocks + * + * For VMs with just normal pages this is equivalent to the host page + * size. If it's got some huge pages then it's the OR of all the + * different page sizes. */ uint64_t ram_pagesize_summary(void) { @@ -617,40 +679,39 @@ uint64_t ram_pagesize_summary(void) return summary; } -static void migration_bitmap_sync(void) +static void migration_bitmap_sync(RAMState *rs) { RAMBlock *block; - MigrationState *s = migrate_get_current(); int64_t end_time; - int64_t bytes_xfer_now; + uint64_t bytes_xfer_now; - bitmap_sync_count++; + rs->bitmap_sync_count++; - if (!bytes_xfer_prev) { - bytes_xfer_prev = ram_bytes_transferred(); + if (!rs->bytes_xfer_prev) { + rs->bytes_xfer_prev = ram_bytes_transferred(); } - if (!start_time) { - start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + if (!rs->time_last_bitmap_sync) { + rs->time_last_bitmap_sync = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); } trace_migration_bitmap_sync_start(); memory_global_dirty_log_sync(); - qemu_mutex_lock(&migration_bitmap_mutex); + qemu_mutex_lock(&rs->bitmap_mutex); rcu_read_lock(); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - migration_bitmap_sync_range(block->offset, block->used_length); + migration_bitmap_sync_range(rs, block, 0, block->used_length); } rcu_read_unlock(); - qemu_mutex_unlock(&migration_bitmap_mutex); + qemu_mutex_unlock(&rs->bitmap_mutex); - trace_migration_bitmap_sync_end(num_dirty_pages_period); + trace_migration_bitmap_sync_end(rs->num_dirty_pages_period); end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); /* more than 1 second = 1000 millisecons */ - if (end_time > start_time + 1000) { + if (end_time > rs->time_last_bitmap_sync + 1000) { if (migrate_auto_converge()) { /* The following detection logic can be refined later. For now: Check to see if the dirtied bytes is 50% more than the approx. @@ -659,94 +720,87 @@ static void migration_bitmap_sync(void) throttling */ bytes_xfer_now = ram_bytes_transferred(); - if (s->dirty_pages_rate && - (num_dirty_pages_period * TARGET_PAGE_SIZE > - (bytes_xfer_now - bytes_xfer_prev)/2) && - (dirty_rate_high_cnt++ >= 2)) { + if (rs->dirty_pages_rate && + (rs->num_dirty_pages_period * TARGET_PAGE_SIZE > + (bytes_xfer_now - rs->bytes_xfer_prev) / 2) && + (rs->dirty_rate_high_cnt++ >= 2)) { trace_migration_throttle(); - dirty_rate_high_cnt = 0; + rs->dirty_rate_high_cnt = 0; mig_throttle_guest_down(); } - bytes_xfer_prev = bytes_xfer_now; + rs->bytes_xfer_prev = bytes_xfer_now; } if (migrate_use_xbzrle()) { - if (iterations_prev != acct_info.iterations) { - acct_info.xbzrle_cache_miss_rate = - (double)(acct_info.xbzrle_cache_miss - - xbzrle_cache_miss_prev) / - (acct_info.iterations - iterations_prev); + if (rs->iterations_prev != rs->iterations) { + rs->xbzrle_cache_miss_rate = + (double)(rs->xbzrle_cache_miss - + rs->xbzrle_cache_miss_prev) / + (rs->iterations - rs->iterations_prev); } - iterations_prev = acct_info.iterations; - xbzrle_cache_miss_prev = acct_info.xbzrle_cache_miss; + rs->iterations_prev = rs->iterations; + rs->xbzrle_cache_miss_prev = rs->xbzrle_cache_miss; } - s->dirty_pages_rate = num_dirty_pages_period * 1000 - / (end_time - start_time); - s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE; - start_time = end_time; - num_dirty_pages_period = 0; + rs->dirty_pages_rate = rs->num_dirty_pages_period * 1000 + / (end_time - rs->time_last_bitmap_sync); + rs->time_last_bitmap_sync = end_time; + rs->num_dirty_pages_period = 0; } - s->dirty_sync_count = bitmap_sync_count; if (migrate_use_events()) { - qapi_event_send_migration_pass(bitmap_sync_count, NULL); + qapi_event_send_migration_pass(rs->bitmap_sync_count, NULL); } } /** - * save_zero_page: Send the zero page to the stream + * save_zero_page: send the zero page to the stream * - * Returns: Number of pages written. + * Returns the number of pages written. * - * @f: QEMUFile where to send the data + * @rs: current RAM state * @block: block that contains the page we want to send * @offset: offset inside the block for the page * @p: pointer to the page - * @bytes_transferred: increase it with the number of transferred bytes */ -static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset, - uint8_t *p, uint64_t *bytes_transferred) +static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, + uint8_t *p) { int pages = -1; if (is_zero_range(p, TARGET_PAGE_SIZE)) { - acct_info.dup_pages++; - *bytes_transferred += save_page_header(f, block, - offset | RAM_SAVE_FLAG_COMPRESS); - qemu_put_byte(f, 0); - *bytes_transferred += 1; + rs->zero_pages++; + rs->bytes_transferred += + save_page_header(rs, block, offset | RAM_SAVE_FLAG_COMPRESS); + qemu_put_byte(rs->f, 0); + rs->bytes_transferred += 1; pages = 1; } return pages; } -static void ram_release_pages(MigrationState *ms, const char *block_name, - uint64_t offset, int pages) +static void ram_release_pages(const char *rbname, uint64_t offset, int pages) { - if (!migrate_release_ram() || !migration_in_postcopy(ms)) { + if (!migrate_release_ram() || !migration_in_postcopy()) { return; } - ram_discard_range(NULL, block_name, offset, pages << TARGET_PAGE_BITS); + ram_discard_range(rbname, offset, pages << TARGET_PAGE_BITS); } /** - * ram_save_page: Send the given page to the stream + * ram_save_page: send the given page to the stream * - * Returns: Number of pages written. + * Returns the number of pages written. * < 0 - error * >=0 - Number of pages written - this might legally be 0 * if xbzrle noticed the page was the same. * - * @ms: The current migration state. - * @f: QEMUFile where to send the data + * @rs: current RAM state * @block: block that contains the page we want to send * @offset: offset inside the block for the page * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes */ -static int ram_save_page(MigrationState *ms, QEMUFile *f, PageSearchStatus *pss, - bool last_stage, uint64_t *bytes_transferred) +static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) { int pages = -1; uint64_t bytes_xmit; @@ -755,16 +809,16 @@ static int ram_save_page(MigrationState *ms, QEMUFile *f, PageSearchStatus *pss, int ret; bool send_async = true; RAMBlock *block = pss->block; - ram_addr_t offset = pss->offset; + ram_addr_t offset = pss->page << TARGET_PAGE_BITS; p = block->host + offset; /* In doubt sent page as normal */ bytes_xmit = 0; - ret = ram_control_save_page(f, block->offset, + ret = ram_control_save_page(rs->f, block->offset, offset, TARGET_PAGE_SIZE, &bytes_xmit); if (bytes_xmit) { - *bytes_transferred += bytes_xmit; + rs->bytes_transferred += bytes_xmit; pages = 1; } @@ -772,29 +826,26 @@ static int ram_save_page(MigrationState *ms, QEMUFile *f, PageSearchStatus *pss, current_addr = block->offset + offset; - if (block == last_sent_block) { - offset |= RAM_SAVE_FLAG_CONTINUE; - } if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { if (ret != RAM_SAVE_CONTROL_DELAYED) { if (bytes_xmit > 0) { - acct_info.norm_pages++; + rs->norm_pages++; } else if (bytes_xmit == 0) { - acct_info.dup_pages++; + rs->zero_pages++; } } } else { - pages = save_zero_page(f, block, offset, p, bytes_transferred); + pages = save_zero_page(rs, block, offset, p); if (pages > 0) { /* Must let xbzrle know, otherwise a previous (now 0'd) cached * page would be stale */ - xbzrle_cache_zero_page(current_addr); - ram_release_pages(ms, block->idstr, pss->offset, pages); - } else if (!ram_bulk_stage && - !migration_in_postcopy(ms) && migrate_use_xbzrle()) { - pages = save_xbzrle_page(f, &p, current_addr, block, - offset, last_stage, bytes_transferred); + xbzrle_cache_zero_page(rs, current_addr); + ram_release_pages(block->idstr, offset, pages); + } else if (!rs->ram_bulk_stage && + !migration_in_postcopy() && migrate_use_xbzrle()) { + pages = save_xbzrle_page(rs, &p, current_addr, block, + offset, last_stage); if (!last_stage) { /* Can't send this cached data async, since the cache page * might get updated before it gets to the wire @@ -806,18 +857,18 @@ static int ram_save_page(MigrationState *ms, QEMUFile *f, PageSearchStatus *pss, /* XBZRLE overflow or normal page */ if (pages == -1) { - *bytes_transferred += save_page_header(f, block, - offset | RAM_SAVE_FLAG_PAGE); + rs->bytes_transferred += save_page_header(rs, block, + offset | RAM_SAVE_FLAG_PAGE); if (send_async) { - qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE, + qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, migrate_release_ram() & - migration_in_postcopy(ms)); + migration_in_postcopy()); } else { - qemu_put_buffer(f, p, TARGET_PAGE_SIZE); + qemu_put_buffer(rs->f, p, TARGET_PAGE_SIZE); } - *bytes_transferred += TARGET_PAGE_SIZE; + rs->bytes_transferred += TARGET_PAGE_SIZE; pages = 1; - acct_info.norm_pages++; + rs->norm_pages++; } XBZRLE_cache_unlock(); @@ -828,10 +879,11 @@ static int ram_save_page(MigrationState *ms, QEMUFile *f, PageSearchStatus *pss, static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset) { + RAMState *rs = &ram_state; int bytes_sent, blen; uint8_t *p = block->host + (offset & TARGET_PAGE_MASK); - bytes_sent = save_page_header(f, block, offset | + bytes_sent = save_page_header(rs, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); blen = qemu_put_compression_data(f, p, TARGET_PAGE_SIZE, migrate_compress_level()); @@ -841,16 +893,13 @@ static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, error_report("compressed data failed!"); } else { bytes_sent += blen; - ram_release_pages(migrate_get_current(), block->idstr, - offset & TARGET_PAGE_MASK, 1); + ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1); } return bytes_sent; } -static uint64_t bytes_transferred; - -static void flush_compressed_data(QEMUFile *f) +static void flush_compressed_data(RAMState *rs) { int idx, len, thread_count; @@ -870,8 +919,8 @@ static void flush_compressed_data(QEMUFile *f) for (idx = 0; idx < thread_count; idx++) { qemu_mutex_lock(&comp_param[idx].mutex); if (!comp_param[idx].quit) { - len = qemu_put_qemu_file(f, comp_param[idx].file); - bytes_transferred += len; + len = qemu_put_qemu_file(rs->f, comp_param[idx].file); + rs->bytes_transferred += len; } qemu_mutex_unlock(&comp_param[idx].mutex); } @@ -884,9 +933,8 @@ static inline void set_compress_params(CompressParam *param, RAMBlock *block, param->offset = offset; } -static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block, - ram_addr_t offset, - uint64_t *bytes_transferred) +static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block, + ram_addr_t offset) { int idx, thread_count, bytes_xmit = -1, pages = -1; @@ -896,14 +944,14 @@ static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block, for (idx = 0; idx < thread_count; idx++) { if (comp_param[idx].done) { comp_param[idx].done = false; - bytes_xmit = qemu_put_qemu_file(f, comp_param[idx].file); + bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file); qemu_mutex_lock(&comp_param[idx].mutex); set_compress_params(&comp_param[idx], block, offset); qemu_cond_signal(&comp_param[idx].cond); qemu_mutex_unlock(&comp_param[idx].mutex); pages = 1; - acct_info.norm_pages++; - *bytes_transferred += bytes_xmit; + rs->norm_pages++; + rs->bytes_transferred += bytes_xmit; break; } } @@ -921,40 +969,37 @@ static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block, /** * ram_save_compressed_page: compress the given page and send it to the stream * - * Returns: Number of pages written. + * Returns the number of pages written. * - * @ms: The current migration state. - * @f: QEMUFile where to send the data + * @rs: current RAM state * @block: block that contains the page we want to send * @offset: offset inside the block for the page * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes */ -static int ram_save_compressed_page(MigrationState *ms, QEMUFile *f, - PageSearchStatus *pss, bool last_stage, - uint64_t *bytes_transferred) +static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, + bool last_stage) { int pages = -1; uint64_t bytes_xmit = 0; uint8_t *p; int ret, blen; RAMBlock *block = pss->block; - ram_addr_t offset = pss->offset; + ram_addr_t offset = pss->page << TARGET_PAGE_BITS; p = block->host + offset; - ret = ram_control_save_page(f, block->offset, + ret = ram_control_save_page(rs->f, block->offset, offset, TARGET_PAGE_SIZE, &bytes_xmit); if (bytes_xmit) { - *bytes_transferred += bytes_xmit; + rs->bytes_transferred += bytes_xmit; pages = 1; } if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { if (ret != RAM_SAVE_CONTROL_DELAYED) { if (bytes_xmit > 0) { - acct_info.norm_pages++; + rs->norm_pages++; } else if (bytes_xmit == 0) { - acct_info.dup_pages++; + rs->zero_pages++; } } } else { @@ -964,35 +1009,33 @@ static int ram_save_compressed_page(MigrationState *ms, QEMUFile *f, * out, keeping this order is important, because the 'cont' flag * is used to avoid resending the block name. */ - if (block != last_sent_block) { - flush_compressed_data(f); - pages = save_zero_page(f, block, offset, p, bytes_transferred); + if (block != rs->last_sent_block) { + flush_compressed_data(rs); + pages = save_zero_page(rs, block, offset, p); if (pages == -1) { /* Make sure the first page is sent out before other pages */ - bytes_xmit = save_page_header(f, block, offset | + bytes_xmit = save_page_header(rs, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); - blen = qemu_put_compression_data(f, p, TARGET_PAGE_SIZE, + blen = qemu_put_compression_data(rs->f, p, TARGET_PAGE_SIZE, migrate_compress_level()); if (blen > 0) { - *bytes_transferred += bytes_xmit + blen; - acct_info.norm_pages++; + rs->bytes_transferred += bytes_xmit + blen; + rs->norm_pages++; pages = 1; } else { - qemu_file_set_error(f, blen); + qemu_file_set_error(rs->f, blen); error_report("compressed data failed!"); } } if (pages > 0) { - ram_release_pages(ms, block->idstr, pss->offset, pages); + ram_release_pages(block->idstr, offset, pages); } } else { - offset |= RAM_SAVE_FLAG_CONTINUE; - pages = save_zero_page(f, block, offset, p, bytes_transferred); + pages = save_zero_page(rs, block, offset, p); if (pages == -1) { - pages = compress_page_with_multi_thread(f, block, offset, - bytes_transferred); + pages = compress_page_with_multi_thread(rs, block, offset); } else { - ram_release_pages(ms, block->idstr, pss->offset, pages); + ram_release_pages(block->idstr, offset, pages); } } } @@ -1000,25 +1043,21 @@ static int ram_save_compressed_page(MigrationState *ms, QEMUFile *f, return pages; } -/* - * Find the next dirty page and update any state associated with - * the search process. +/** + * find_dirty_block: find the next dirty page and update any state + * associated with the search process. * - * Returns: True if a page is found + * Returns if a page is found * - * @f: Current migration stream. - * @pss: Data about the state of the current dirty page scan. - * @*again: Set to false if the search has scanned the whole of RAM - * *ram_addr_abs: Pointer into which to store the address of the dirty page - * within the global ram_addr space + * @rs: current RAM state + * @pss: data about the state of the current dirty page scan + * @again: set to false if the search has scanned the whole of RAM */ -static bool find_dirty_block(QEMUFile *f, PageSearchStatus *pss, - bool *again, ram_addr_t *ram_addr_abs) +static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) { - pss->offset = migration_bitmap_find_dirty(pss->block, pss->offset, - ram_addr_abs); - if (pss->complete_round && pss->block == last_seen_block && - pss->offset >= last_offset) { + pss->page = migration_bitmap_find_dirty(rs, pss->block, pss->page); + if (pss->complete_round && pss->block == rs->last_seen_block && + pss->page >= rs->last_page) { /* * We've been once around the RAM and haven't found anything. * Give up. @@ -1026,22 +1065,21 @@ static bool find_dirty_block(QEMUFile *f, PageSearchStatus *pss, *again = false; return false; } - if (pss->offset >= pss->block->used_length) { + if ((pss->page << TARGET_PAGE_BITS) >= pss->block->used_length) { /* Didn't find anything in this RAM Block */ - pss->offset = 0; + pss->page = 0; pss->block = QLIST_NEXT_RCU(pss->block, next); if (!pss->block) { /* Hit the end of the list */ pss->block = QLIST_FIRST_RCU(&ram_list.blocks); /* Flag that we've looped */ pss->complete_round = true; - ram_bulk_stage = false; + rs->ram_bulk_stage = false; if (migrate_use_xbzrle()) { /* If xbzrle is on, stop using the data compression at this * point. In theory, xbzrle can do better than compression. */ - flush_compressed_data(f); - compression_switch = false; + flush_compressed_data(rs); } } /* Didn't find anything this time, but try again on the new block */ @@ -1055,61 +1093,59 @@ static bool find_dirty_block(QEMUFile *f, PageSearchStatus *pss, } } -/* +/** + * unqueue_page: gets a page of the queue + * * Helper for 'get_queued_page' - gets a page off the queue - * ms: MigrationState in - * *offset: Used to return the offset within the RAMBlock - * ram_addr_abs: global offset in the dirty/sent bitmaps * - * Returns: block (or NULL if none available) + * Returns the block of the page (or NULL if none available) + * + * @rs: current RAM state + * @offset: used to return the offset within the RAMBlock */ -static RAMBlock *unqueue_page(MigrationState *ms, ram_addr_t *offset, - ram_addr_t *ram_addr_abs) +static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) { RAMBlock *block = NULL; - qemu_mutex_lock(&ms->src_page_req_mutex); - if (!QSIMPLEQ_EMPTY(&ms->src_page_requests)) { - struct MigrationSrcPageRequest *entry = - QSIMPLEQ_FIRST(&ms->src_page_requests); + qemu_mutex_lock(&rs->src_page_req_mutex); + if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) { + struct RAMSrcPageRequest *entry = + QSIMPLEQ_FIRST(&rs->src_page_requests); block = entry->rb; *offset = entry->offset; - *ram_addr_abs = (entry->offset + entry->rb->offset) & - TARGET_PAGE_MASK; if (entry->len > TARGET_PAGE_SIZE) { entry->len -= TARGET_PAGE_SIZE; entry->offset += TARGET_PAGE_SIZE; } else { memory_region_unref(block->mr); - QSIMPLEQ_REMOVE_HEAD(&ms->src_page_requests, next_req); + QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); g_free(entry); } } - qemu_mutex_unlock(&ms->src_page_req_mutex); + qemu_mutex_unlock(&rs->src_page_req_mutex); return block; } -/* - * Unqueue a page from the queue fed by postcopy page requests; skips pages - * that are already sent (!dirty) +/** + * get_queued_page: unqueue a page from the postocpy requests + * + * Skips pages that are already sent (!dirty) * - * ms: MigrationState in - * pss: PageSearchStatus structure updated with found block/offset - * ram_addr_abs: global offset in the dirty/sent bitmaps + * Returns if a queued page is found * - * Returns: true if a queued page is found + * @rs: current RAM state + * @pss: data about the state of the current dirty page scan */ -static bool get_queued_page(MigrationState *ms, PageSearchStatus *pss, - ram_addr_t *ram_addr_abs) +static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) { RAMBlock *block; ram_addr_t offset; bool dirty; do { - block = unqueue_page(ms, &offset, ram_addr_abs); + block = unqueue_page(rs, &offset); /* * We're sending this page, and since it's postcopy nothing else * will dirty it, and we must make sure it doesn't get sent again @@ -1118,18 +1154,18 @@ static bool get_queued_page(MigrationState *ms, PageSearchStatus *pss, */ if (block) { unsigned long *bitmap; - bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; - dirty = test_bit(*ram_addr_abs >> TARGET_PAGE_BITS, bitmap); + unsigned long page; + + bitmap = atomic_rcu_read(&rs->ram_bitmap)->bmap; + page = (block->offset + offset) >> TARGET_PAGE_BITS; + dirty = test_bit(page, bitmap); if (!dirty) { - trace_get_queued_page_not_dirty( - block->idstr, (uint64_t)offset, - (uint64_t)*ram_addr_abs, - test_bit(*ram_addr_abs >> TARGET_PAGE_BITS, - atomic_rcu_read(&migration_bitmap_rcu)->unsentmap)); + trace_get_queued_page_not_dirty(block->idstr, (uint64_t)offset, + page, + test_bit(page, + atomic_rcu_read(&rs->ram_bitmap)->unsentmap)); } else { - trace_get_queued_page(block->idstr, - (uint64_t)offset, - (uint64_t)*ram_addr_abs); + trace_get_queued_page(block->idstr, (uint64_t)offset, page); } } @@ -1142,7 +1178,7 @@ static bool get_queued_page(MigrationState *ms, PageSearchStatus *pss, * in (migration_bitmap_find_and_reset_dirty) that every page is * dirty, that's no longer true. */ - ram_bulk_stage = false; + rs->ram_bulk_stage = false; /* * We want the background search to continue from the queued page @@ -1150,52 +1186,58 @@ static bool get_queued_page(MigrationState *ms, PageSearchStatus *pss, * it just requested. */ pss->block = block; - pss->offset = offset; + pss->page = offset >> TARGET_PAGE_BITS; } return !!block; } /** - * flush_page_queue: Flush any remaining pages in the ram request queue - * it should be empty at the end anyway, but in error cases there may be - * some left. + * migration_page_queue_free: drop any remaining pages in the ram + * request queue + * + * It should be empty at the end anyway, but in error cases there may + * be some left. in case that there is any page left, we drop it. * - * ms: MigrationState */ -void flush_page_queue(MigrationState *ms) +void migration_page_queue_free(void) { - struct MigrationSrcPageRequest *mspr, *next_mspr; + struct RAMSrcPageRequest *mspr, *next_mspr; + RAMState *rs = &ram_state; /* This queue generally should be empty - but in the case of a failed * migration might have some droppings in. */ rcu_read_lock(); - QSIMPLEQ_FOREACH_SAFE(mspr, &ms->src_page_requests, next_req, next_mspr) { + QSIMPLEQ_FOREACH_SAFE(mspr, &rs->src_page_requests, next_req, next_mspr) { memory_region_unref(mspr->rb->mr); - QSIMPLEQ_REMOVE_HEAD(&ms->src_page_requests, next_req); + QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); g_free(mspr); } rcu_read_unlock(); } /** - * Queue the pages for transmission, e.g. a request from postcopy destination - * ms: MigrationStatus in which the queue is held - * rbname: The RAMBlock the request is for - may be NULL (to mean reuse last) - * start: Offset from the start of the RAMBlock - * len: Length (in bytes) to send - * Return: 0 on success + * ram_save_queue_pages: queue the page for transmission + * + * A request from postcopy destination for example. + * + * Returns zero on success or negative on error + * + * @rbname: Name of the RAMBLock of the request. NULL means the + * same that last one. + * @start: starting address from the start of the RAMBlock + * @len: length (in bytes) to send */ -int ram_save_queue_pages(MigrationState *ms, const char *rbname, - ram_addr_t start, ram_addr_t len) +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) { RAMBlock *ramblock; + RAMState *rs = &ram_state; - ms->postcopy_requests++; + rs->postcopy_requests++; rcu_read_lock(); if (!rbname) { /* Reuse last RAMBlock */ - ramblock = ms->last_req_rb; + ramblock = rs->last_req_rb; if (!ramblock) { /* @@ -1213,7 +1255,7 @@ int ram_save_queue_pages(MigrationState *ms, const char *rbname, error_report("ram_save_queue_pages no block '%s'", rbname); goto err; } - ms->last_req_rb = ramblock; + rs->last_req_rb = ramblock; } trace_ram_save_queue_pages(ramblock->idstr, start, len); if (start+len > ramblock->used_length) { @@ -1223,16 +1265,16 @@ int ram_save_queue_pages(MigrationState *ms, const char *rbname, goto err; } - struct MigrationSrcPageRequest *new_entry = - g_malloc0(sizeof(struct MigrationSrcPageRequest)); + struct RAMSrcPageRequest *new_entry = + g_malloc0(sizeof(struct RAMSrcPageRequest)); new_entry->rb = ramblock; new_entry->offset = start; new_entry->len = len; memory_region_ref(ramblock->mr); - qemu_mutex_lock(&ms->src_page_req_mutex); - QSIMPLEQ_INSERT_TAIL(&ms->src_page_requests, new_entry, next_req); - qemu_mutex_unlock(&ms->src_page_req_mutex); + qemu_mutex_lock(&rs->src_page_req_mutex); + QSIMPLEQ_INSERT_TAIL(&rs->src_page_requests, new_entry, next_req); + qemu_mutex_unlock(&rs->src_page_req_mutex); rcu_read_unlock(); return 0; @@ -1243,51 +1285,43 @@ err: } /** - * ram_save_target_page: Save one target page + * ram_save_target_page: save one target page * + * Returns the number of pages written * - * @f: QEMUFile where to send the data - * @block: pointer to block that contains the page we want to send - * @offset: offset inside the block for the page; + * @rs: current RAM state + * @ms: current migration state + * @pss: data about the page we want to send * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes - * @dirty_ram_abs: Address of the start of the dirty page in ram_addr_t space - * - * Returns: Number of pages written. */ -static int ram_save_target_page(MigrationState *ms, QEMUFile *f, - PageSearchStatus *pss, - bool last_stage, - uint64_t *bytes_transferred, - ram_addr_t dirty_ram_abs) +static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, + bool last_stage) { int res = 0; /* Check the pages is dirty and if it is send it */ - if (migration_bitmap_clear_dirty(dirty_ram_abs)) { + if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { unsigned long *unsentmap; - if (compression_switch && migrate_use_compression()) { - res = ram_save_compressed_page(ms, f, pss, - last_stage, - bytes_transferred); + /* + * If xbzrle is on, stop using the data compression after first + * round of migration even if compression is enabled. In theory, + * xbzrle can do better than compression. + */ + unsigned long page = + (pss->block->offset >> TARGET_PAGE_BITS) + pss->page; + if (migrate_use_compression() + && (rs->ram_bulk_stage || !migrate_use_xbzrle())) { + res = ram_save_compressed_page(rs, pss, last_stage); } else { - res = ram_save_page(ms, f, pss, last_stage, - bytes_transferred); + res = ram_save_page(rs, pss, last_stage); } if (res < 0) { return res; } - unsentmap = atomic_rcu_read(&migration_bitmap_rcu)->unsentmap; + unsentmap = atomic_rcu_read(&rs->ram_bitmap)->unsentmap; if (unsentmap) { - clear_bit(dirty_ram_abs >> TARGET_PAGE_BITS, unsentmap); - } - /* Only update last_sent_block if a block was actually sent; xbzrle - * might have decided the page was identical so didn't bother writing - * to the stream. - */ - if (res > 0) { - last_sent_block = pss->block; + clear_bit(page, unsentmap); } } @@ -1295,83 +1329,70 @@ static int ram_save_target_page(MigrationState *ms, QEMUFile *f, } /** - * ram_save_host_page: Starting at *offset send pages up to the end - * of the current host page. It's valid for the initial - * offset to point into the middle of a host page - * in which case the remainder of the hostpage is sent. - * Only dirty target pages are sent. - * Note that the host page size may be a huge page for this - * block. + * ram_save_host_page: save a whole host page * - * Returns: Number of pages written. + * Starting at *offset send pages up to the end of the current host + * page. It's valid for the initial offset to point into the middle of + * a host page in which case the remainder of the hostpage is sent. + * Only dirty target pages are sent. Note that the host page size may + * be a huge page for this block. * - * @f: QEMUFile where to send the data - * @block: pointer to block that contains the page we want to send - * @offset: offset inside the block for the page; updated to last target page - * sent + * Returns the number of pages written or negative on error + * + * @rs: current RAM state + * @ms: current migration state + * @pss: data about the page we want to send * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes - * @dirty_ram_abs: Address of the start of the dirty page in ram_addr_t space */ -static int ram_save_host_page(MigrationState *ms, QEMUFile *f, - PageSearchStatus *pss, - bool last_stage, - uint64_t *bytes_transferred, - ram_addr_t dirty_ram_abs) +static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, + bool last_stage) { int tmppages, pages = 0; - size_t pagesize = qemu_ram_pagesize(pss->block); + size_t pagesize_bits = + qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; do { - tmppages = ram_save_target_page(ms, f, pss, last_stage, - bytes_transferred, dirty_ram_abs); + tmppages = ram_save_target_page(rs, pss, last_stage); if (tmppages < 0) { return tmppages; } pages += tmppages; - pss->offset += TARGET_PAGE_SIZE; - dirty_ram_abs += TARGET_PAGE_SIZE; - } while (pss->offset & (pagesize - 1)); + pss->page++; + } while (pss->page & (pagesize_bits - 1)); /* The offset we leave with is the last one we looked at */ - pss->offset -= TARGET_PAGE_SIZE; + pss->page--; return pages; } /** - * ram_find_and_save_block: Finds a dirty page and sends it to f + * ram_find_and_save_block: finds a dirty page and sends it to f * * Called within an RCU critical section. * - * Returns: The number of pages written - * 0 means no dirty pages + * Returns the number of pages written where zero means no dirty pages * - * @f: QEMUFile where to send the data + * @rs: current RAM state * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes * * On systems where host-page-size > target-page-size it will send all the * pages in a host page that are dirty. */ -static int ram_find_and_save_block(QEMUFile *f, bool last_stage, - uint64_t *bytes_transferred) +static int ram_find_and_save_block(RAMState *rs, bool last_stage) { PageSearchStatus pss; - MigrationState *ms = migrate_get_current(); int pages = 0; bool again, found; - ram_addr_t dirty_ram_abs; /* Address of the start of the dirty page in - ram_addr_t space */ /* No dirty page as there is zero RAM */ if (!ram_bytes_total()) { return pages; } - pss.block = last_seen_block; - pss.offset = last_offset; + pss.block = rs->last_seen_block; + pss.page = rs->last_page; pss.complete_round = false; if (!pss.block) { @@ -1380,22 +1401,20 @@ static int ram_find_and_save_block(QEMUFile *f, bool last_stage, do { again = true; - found = get_queued_page(ms, &pss, &dirty_ram_abs); + found = get_queued_page(rs, &pss); if (!found) { /* priority queue empty, so just search for something dirty */ - found = find_dirty_block(f, &pss, &again, &dirty_ram_abs); + found = find_dirty_block(rs, &pss, &again); } if (found) { - pages = ram_save_host_page(ms, f, &pss, - last_stage, bytes_transferred, - dirty_ram_abs); + pages = ram_save_host_page(rs, &pss, last_stage); } } while (!pages && again); - last_seen_block = pss.block; - last_offset = pss.offset; + rs->last_seen_block = pss.block; + rs->last_page = pss.page; return pages; } @@ -1403,30 +1422,17 @@ static int ram_find_and_save_block(QEMUFile *f, bool last_stage, void acct_update_position(QEMUFile *f, size_t size, bool zero) { uint64_t pages = size / TARGET_PAGE_SIZE; + RAMState *rs = &ram_state; + if (zero) { - acct_info.dup_pages += pages; + rs->zero_pages += pages; } else { - acct_info.norm_pages += pages; - bytes_transferred += size; + rs->norm_pages += pages; + rs->bytes_transferred += size; qemu_update_position(f, size); } } -static ram_addr_t ram_save_remaining(void) -{ - return migration_dirty_pages; -} - -uint64_t ram_bytes_remaining(void) -{ - return ram_save_remaining() * TARGET_PAGE_SIZE; -} - -uint64_t ram_bytes_transferred(void) -{ - return bytes_transferred; -} - uint64_t ram_bytes_total(void) { RAMBlock *block; @@ -1445,7 +1451,7 @@ void free_xbzrle_decoded_buf(void) xbzrle_decoded_buf = NULL; } -static void migration_bitmap_free(struct BitmapRcu *bmap) +static void migration_bitmap_free(RAMBitmap *bmap) { g_free(bmap->bmap); g_free(bmap->unsentmap); @@ -1454,11 +1460,13 @@ static void migration_bitmap_free(struct BitmapRcu *bmap) static void ram_migration_cleanup(void *opaque) { + RAMState *rs = opaque; + /* caller have hold iothread lock or is in a bh, so there is * no writing race against this migration_bitmap */ - struct BitmapRcu *bitmap = migration_bitmap_rcu; - atomic_rcu_set(&migration_bitmap_rcu, NULL); + RAMBitmap *bitmap = rs->ram_bitmap; + atomic_rcu_set(&rs->ram_bitmap, NULL); if (bitmap) { memory_global_dirty_log_stop(); call_rcu(bitmap, migration_bitmap_free, rcu); @@ -1477,49 +1485,17 @@ static void ram_migration_cleanup(void *opaque) XBZRLE_cache_unlock(); } -static void reset_ram_globals(void) +static void ram_state_reset(RAMState *rs) { - last_seen_block = NULL; - last_sent_block = NULL; - last_offset = 0; - last_version = ram_list.version; - ram_bulk_stage = true; + rs->last_seen_block = NULL; + rs->last_sent_block = NULL; + rs->last_page = 0; + rs->last_version = ram_list.version; + rs->ram_bulk_stage = true; } #define MAX_WAIT 50 /* ms, half buffered_file limit */ -void migration_bitmap_extend(ram_addr_t old, ram_addr_t new) -{ - /* called in qemu main thread, so there is - * no writing race against this migration_bitmap - */ - if (migration_bitmap_rcu) { - struct BitmapRcu *old_bitmap = migration_bitmap_rcu, *bitmap; - bitmap = g_new(struct BitmapRcu, 1); - bitmap->bmap = bitmap_new(new); - - /* prevent migration_bitmap content from being set bit - * by migration_bitmap_sync_range() at the same time. - * it is safe to migration if migration_bitmap is cleared bit - * at the same time. - */ - qemu_mutex_lock(&migration_bitmap_mutex); - bitmap_copy(bitmap->bmap, old_bitmap->bmap, old); - bitmap_set(bitmap->bmap, old, new - old); - - /* We don't have a way to safely extend the sentmap - * with RCU; so mark it as missing, entry to postcopy - * will fail. - */ - bitmap->unsentmap = NULL; - - atomic_rcu_set(&migration_bitmap_rcu, bitmap); - qemu_mutex_unlock(&migration_bitmap_mutex); - migration_dirty_pages += new - old; - call_rcu(old_bitmap, migration_bitmap_free, rcu); - } -} - /* * 'expected' is the value you expect the bitmap mostly to be full * of; it won't bother printing lines that are all this value. @@ -1527,14 +1503,14 @@ void migration_bitmap_extend(ram_addr_t old, ram_addr_t new) */ void ram_debug_dump_bitmap(unsigned long *todump, bool expected) { - int64_t ram_pages = last_ram_offset() >> TARGET_PAGE_BITS; - + unsigned long ram_pages = last_ram_page(); + RAMState *rs = &ram_state; int64_t cur; int64_t linelen = 128; char linebuf[129]; if (!todump) { - todump = atomic_rcu_read(&migration_bitmap_rcu)->bmap; + todump = atomic_rcu_read(&rs->ram_bitmap)->bmap; } for (cur = 0; cur < ram_pages; cur += linelen) { @@ -1563,8 +1539,9 @@ void ram_debug_dump_bitmap(unsigned long *todump, bool expected) void ram_postcopy_migrated_memory_release(MigrationState *ms) { + RAMState *rs = &ram_state; struct RAMBlock *block; - unsigned long *bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; + unsigned long *bitmap = atomic_rcu_read(&rs->ram_bitmap)->bmap; QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { unsigned long first = block->offset >> TARGET_PAGE_BITS; @@ -1573,30 +1550,38 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms) while (run_start < range) { unsigned long run_end = find_next_bit(bitmap, range, run_start + 1); - ram_discard_range(NULL, block->idstr, run_start << TARGET_PAGE_BITS, + ram_discard_range(block->idstr, run_start << TARGET_PAGE_BITS, (run_end - run_start) << TARGET_PAGE_BITS); run_start = find_next_zero_bit(bitmap, range, run_end + 1); } } } -/* +/** + * postcopy_send_discard_bm_ram: discard a RAMBlock + * + * Returns zero on success + * * Callback from postcopy_each_ram_send_discard for each RAMBlock * Note: At this point the 'unsentmap' is the processed bitmap combined * with the dirtymap; so a '1' means it's either dirty or unsent. - * start,length: Indexes into the bitmap for the first bit - * representing the named block and length in target-pages + * + * @ms: current migration state + * @pds: state for postcopy + * @start: RAMBlock starting page + * @length: RAMBlock size */ static int postcopy_send_discard_bm_ram(MigrationState *ms, PostcopyDiscardState *pds, unsigned long start, unsigned long length) { + RAMState *rs = &ram_state; unsigned long end = start + length; /* one after the end */ unsigned long current; unsigned long *unsentmap; - unsentmap = atomic_rcu_read(&migration_bitmap_rcu)->unsentmap; + unsentmap = atomic_rcu_read(&rs->ram_bitmap)->unsentmap; for (current = start; current < end; ) { unsigned long one = find_next_bit(unsentmap, end, current); @@ -1621,13 +1606,18 @@ static int postcopy_send_discard_bm_ram(MigrationState *ms, return 0; } -/* +/** + * postcopy_each_ram_send_discard: discard all RAMBlocks + * + * Returns 0 for success or negative for error + * * Utility for the outgoing postcopy code. * Calls postcopy_send_discard_bm_ram for each RAMBlock * passing it bitmap indexes and name. - * Returns: 0 on success * (qemu_ram_foreach_block ends up passing unscaled lengths * which would mean postcopy code would have to deal with target page) + * + * @ms: current migration state */ static int postcopy_each_ram_send_discard(MigrationState *ms) { @@ -1656,22 +1646,27 @@ static int postcopy_each_ram_send_discard(MigrationState *ms) return 0; } -/* - * Helper for postcopy_chunk_hostpages; it's called twice to cleanup - * the two bitmaps, that are similar, but one is inverted. +/** + * postcopy_chunk_hostpages_pass: canocalize bitmap in hostpages + * + * Helper for postcopy_chunk_hostpages; it's called twice to + * canonicalize the two bitmaps, that are similar, but one is + * inverted. * - * We search for runs of target-pages that don't start or end on a - * host page boundary; - * unsent_pass=true: Cleans up partially unsent host pages by searching - * the unsentmap - * unsent_pass=false: Cleans up partially dirty host pages by searching - * the main migration bitmap + * Postcopy requires that all target pages in a hostpage are dirty or + * clean, not a mix. This function canonicalizes the bitmaps. * + * @ms: current migration state + * @unsent_pass: if true we need to canonicalize partially unsent host pages + * otherwise we need to canonicalize partially dirty host pages + * @block: block that contains the page we want to canonicalize + * @pds: state for postcopy */ static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass, RAMBlock *block, PostcopyDiscardState *pds) { + RAMState *rs = &ram_state; unsigned long *bitmap; unsigned long *unsentmap; unsigned int host_ratio = block->page_size / TARGET_PAGE_SIZE; @@ -1685,8 +1680,8 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass, return; } - bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; - unsentmap = atomic_rcu_read(&migration_bitmap_rcu)->unsentmap; + bitmap = atomic_rcu_read(&rs->ram_bitmap)->bmap; + unsentmap = atomic_rcu_read(&rs->ram_bitmap)->unsentmap; if (unsent_pass) { /* Find a sent page */ @@ -1769,7 +1764,7 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass, * Remark them as dirty, updating the count for any pages * that weren't previously dirty. */ - migration_dirty_pages += !test_and_set_bit(page, bitmap); + rs->migration_dirty_pages += !test_and_set_bit(page, bitmap); } } @@ -1784,23 +1779,28 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass, } } -/* +/** + * postcopy_chuck_hostpages: discrad any partially sent host page + * * Utility for the outgoing postcopy code. * * Discard any partially sent host-page size chunks, mark any partially * dirty host-page size chunks as all dirty. In this case the host-page * is the host-page for the particular RAMBlock, i.e. it might be a huge page * - * Returns: 0 on success + * Returns zero on success + * + * @ms: current migration state */ static int postcopy_chunk_hostpages(MigrationState *ms) { + RAMState *rs = &ram_state; struct RAMBlock *block; /* Easiest way to make sure we don't resume in the middle of a host-page */ - last_seen_block = NULL; - last_sent_block = NULL; - last_offset = 0; + rs->last_seen_block = NULL; + rs->last_sent_block = NULL; + rs->last_page = 0; QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { unsigned long first = block->offset >> TARGET_PAGE_BITS; @@ -1822,7 +1822,11 @@ static int postcopy_chunk_hostpages(MigrationState *ms) return 0; } -/* +/** + * ram_postcopy_send_discard_bitmap: transmit the discard bitmap + * + * Returns zero on success + * * Transmit the set of pages to be discarded after precopy to the target * these are pages that: * a) Have been previously transmitted but are now dirty again @@ -1830,18 +1834,21 @@ static int postcopy_chunk_hostpages(MigrationState *ms) * any pages on the destination that have been mapped by background * tasks get discarded (transparent huge pages is the specific concern) * Hopefully this is pretty sparse + * + * @ms: current migration state */ int ram_postcopy_send_discard_bitmap(MigrationState *ms) { + RAMState *rs = &ram_state; int ret; unsigned long *bitmap, *unsentmap; rcu_read_lock(); /* This should be our last sync, the src is now paused */ - migration_bitmap_sync(); + migration_bitmap_sync(rs); - unsentmap = atomic_rcu_read(&migration_bitmap_rcu)->unsentmap; + unsentmap = atomic_rcu_read(&rs->ram_bitmap)->unsentmap; if (!unsentmap) { /* We don't have a safe way to resize the sentmap, so * if the bitmap was resized it will be NULL at this @@ -1862,9 +1869,8 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) /* * Update the unsentmap to be unsentmap = unsentmap | dirty */ - bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap; - bitmap_or(unsentmap, unsentmap, bitmap, - last_ram_offset() >> TARGET_PAGE_BITS); + bitmap = atomic_rcu_read(&rs->ram_bitmap)->bmap; + bitmap_or(unsentmap, unsentmap, bitmap, last_ram_page()); trace_ram_postcopy_send_discard_bitmap(); @@ -1878,28 +1884,27 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) return ret; } -/* - * At the start of the postcopy phase of migration, any now-dirty - * precopied pages are discarded. +/** + * ram_discard_range: discard dirtied pages at the beginning of postcopy * - * start, length describe a byte address range within the RAMBlock + * Returns zero on success * - * Returns 0 on success. + * @rbname: name of the RAMBlock of the request. NULL means the + * same that last one. + * @start: RAMBlock starting page + * @length: RAMBlock size */ -int ram_discard_range(MigrationIncomingState *mis, - const char *block_name, - uint64_t start, size_t length) +int ram_discard_range(const char *rbname, uint64_t start, size_t length) { int ret = -1; - trace_ram_discard_range(block_name, start, length); + trace_ram_discard_range(rbname, start, length); rcu_read_lock(); - RAMBlock *rb = qemu_ram_block_by_name(block_name); + RAMBlock *rb = qemu_ram_block_by_name(rbname); if (!rb) { - error_report("ram_discard_range: Failed to find block '%s'", - block_name); + error_report("ram_discard_range: Failed to find block '%s'", rbname); goto err; } @@ -1911,14 +1916,14 @@ err: return ret; } -static int ram_save_init_globals(void) +static int ram_state_init(RAMState *rs) { - int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */ + unsigned long ram_bitmap_pages; - dirty_rate_high_cnt = 0; - bitmap_sync_count = 0; - migration_bitmap_sync_init(); - qemu_mutex_init(&migration_bitmap_mutex); + memset(rs, 0, sizeof(*rs)); + qemu_mutex_init(&rs->bitmap_mutex); + qemu_mutex_init(&rs->src_page_req_mutex); + QSIMPLEQ_INIT(&rs->src_page_requests); if (migrate_use_xbzrle()) { XBZRLE_cache_lock(); @@ -1947,8 +1952,6 @@ static int ram_save_init_globals(void) XBZRLE.encoded_buf = NULL; return -1; } - - acct_clear(); } /* For memory_global_dirty_log_start below. */ @@ -1956,19 +1959,18 @@ static int ram_save_init_globals(void) qemu_mutex_lock_ramlist(); rcu_read_lock(); - bytes_transferred = 0; - reset_ram_globals(); + ram_state_reset(rs); - migration_bitmap_rcu = g_new0(struct BitmapRcu, 1); + rs->ram_bitmap = g_new0(RAMBitmap, 1); /* Skip setting bitmap if there is no RAM */ if (ram_bytes_total()) { - ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS; - migration_bitmap_rcu->bmap = bitmap_new(ram_bitmap_pages); - bitmap_set(migration_bitmap_rcu->bmap, 0, ram_bitmap_pages); + ram_bitmap_pages = last_ram_page(); + rs->ram_bitmap->bmap = bitmap_new(ram_bitmap_pages); + bitmap_set(rs->ram_bitmap->bmap, 0, ram_bitmap_pages); if (migrate_postcopy_ram()) { - migration_bitmap_rcu->unsentmap = bitmap_new(ram_bitmap_pages); - bitmap_set(migration_bitmap_rcu->unsentmap, 0, ram_bitmap_pages); + rs->ram_bitmap->unsentmap = bitmap_new(ram_bitmap_pages); + bitmap_set(rs->ram_bitmap->unsentmap, 0, ram_bitmap_pages); } } @@ -1976,10 +1978,10 @@ static int ram_save_init_globals(void) * Count the total number of pages used by ram blocks not including any * gaps due to alignment or unplugs. */ - migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS; + rs->migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS; memory_global_dirty_log_start(); - migration_bitmap_sync(); + migration_bitmap_sync(rs); qemu_mutex_unlock_ramlist(); qemu_mutex_unlock_iothread(); rcu_read_unlock(); @@ -1987,22 +1989,33 @@ static int ram_save_init_globals(void) return 0; } -/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has +/* + * Each of ram_save_setup, ram_save_iterate and ram_save_complete has * long-running RCU critical section. When rcu-reclaims in the code * start to become numerous it will be necessary to reduce the * granularity of these critical sections. */ +/** + * ram_save_setup: Setup RAM for migration + * + * Returns zero to indicate success and negative for error + * + * @f: QEMUFile where to send the data + * @opaque: RAMState pointer + */ static int ram_save_setup(QEMUFile *f, void *opaque) { + RAMState *rs = opaque; RAMBlock *block; /* migration has already setup the bitmap, reuse it. */ if (!migration_in_colo_state()) { - if (ram_save_init_globals() < 0) { + if (ram_state_init(rs) < 0) { return -1; } } + rs->f = f; rcu_read_lock(); @@ -2027,16 +2040,25 @@ static int ram_save_setup(QEMUFile *f, void *opaque) return 0; } +/** + * ram_save_iterate: iterative stage for migration + * + * Returns zero to indicate success and negative for error + * + * @f: QEMUFile where to send the data + * @opaque: RAMState pointer + */ static int ram_save_iterate(QEMUFile *f, void *opaque) { + RAMState *rs = opaque; int ret; int i; int64_t t0; int done = 0; rcu_read_lock(); - if (ram_list.version != last_version) { - reset_ram_globals(); + if (ram_list.version != rs->last_version) { + ram_state_reset(rs); } /* Read version before ram_list.blocks */ @@ -2049,13 +2071,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) while ((ret = qemu_file_rate_limit(f)) == 0) { int pages; - pages = ram_find_and_save_block(f, false, &bytes_transferred); + pages = ram_find_and_save_block(rs, false); /* no more pages to sent */ if (pages == 0) { done = 1; break; } - acct_info.iterations++; + rs->iterations++; /* we want to check in the 1st loop, just in case it was the 1st time and we had to sync the dirty bitmap. @@ -2071,7 +2093,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) } i++; } - flush_compressed_data(f); + flush_compressed_data(rs); rcu_read_unlock(); /* @@ -2081,7 +2103,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) ram_control_after_iterate(f, RAM_CONTROL_ROUND); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - bytes_transferred += 8; + rs->bytes_transferred += 8; ret = qemu_file_get_error(f); if (ret < 0) { @@ -2091,13 +2113,24 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) return done; } -/* Called with iothread lock */ +/** + * ram_save_complete: function called to send the remaining amount of ram + * + * Returns zero to indicate success + * + * Called with iothread lock + * + * @f: QEMUFile where to send the data + * @opaque: RAMState pointer + */ static int ram_save_complete(QEMUFile *f, void *opaque) { + RAMState *rs = opaque; + rcu_read_lock(); - if (!migration_in_postcopy(migrate_get_current())) { - migration_bitmap_sync(); + if (!migration_in_postcopy()) { + migration_bitmap_sync(rs); } ram_control_before_iterate(f, RAM_CONTROL_FINISH); @@ -2108,15 +2141,14 @@ static int ram_save_complete(QEMUFile *f, void *opaque) while (true) { int pages; - pages = ram_find_and_save_block(f, !migration_in_colo_state(), - &bytes_transferred); + pages = ram_find_and_save_block(rs, !migration_in_colo_state()); /* no more blocks to sent */ if (pages == 0) { break; } } - flush_compressed_data(f); + flush_compressed_data(rs); ram_control_after_iterate(f, RAM_CONTROL_FINISH); rcu_read_unlock(); @@ -2130,18 +2162,19 @@ static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, uint64_t *non_postcopiable_pending, uint64_t *postcopiable_pending) { + RAMState *rs = opaque; uint64_t remaining_size; - remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; + remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; - if (!migration_in_postcopy(migrate_get_current()) && + if (!migration_in_postcopy() && remaining_size < max_size) { qemu_mutex_lock_iothread(); rcu_read_lock(); - migration_bitmap_sync(); + migration_bitmap_sync(rs); rcu_read_unlock(); qemu_mutex_unlock_iothread(); - remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; + remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; } /* We can do postcopy, and all the data is postcopiable */ @@ -2185,17 +2218,17 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) return 0; } -/* Must be called from within a rcu critical section. +/** + * ram_block_from_stream: read a RAMBlock id from the migration stream + * + * Must be called from within a rcu critical section. + * * Returns a pointer from within the RCU-protected ram_list. - */ -/* - * Read a RAMBlock ID from the stream f. * - * f: Stream to read from - * flags: Page flags (mostly to see if it's a continuation of previous block) + * @f: QEMUFile where to read the data from + * @flags: Page flags (mostly to see if it's a continuation of previous block) */ -static inline RAMBlock *ram_block_from_stream(QEMUFile *f, - int flags) +static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags) { static RAMBlock *block = NULL; char id[256]; @@ -2232,9 +2265,15 @@ static inline void *host_from_ram_block_offset(RAMBlock *block, return block->host + offset; } -/* +/** + * ram_handle_compressed: handle the zero page case + * * If a page (or a whole RDMA chunk) has been * determined to be zero, then zap it. + * + * @host: host address for the zero page + * @ch: what the page is filled from. We only support zero + * @size: size of the zero page */ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) { @@ -2373,20 +2412,33 @@ static void decompress_data_with_multi_threads(QEMUFile *f, qemu_mutex_unlock(&decomp_done_lock); } -/* - * Allocate data structures etc needed by incoming migration with postcopy-ram - * postcopy-ram's similarly names postcopy_ram_incoming_init does the work +/** + * ram_postcopy_incoming_init: allocate postcopy data structures + * + * Returns 0 for success and negative if there was one error + * + * @mis: current migration incoming state + * + * Allocate data structures etc needed by incoming migration with + * postcopy-ram. postcopy-ram's similarly names + * postcopy_ram_incoming_init does the work. */ int ram_postcopy_incoming_init(MigrationIncomingState *mis) { - size_t ram_pages = last_ram_offset() >> TARGET_PAGE_BITS; + unsigned long ram_pages = last_ram_page(); return postcopy_ram_incoming_init(mis, ram_pages); } -/* +/** + * ram_load_postcopy: load a page in postcopy case + * + * Returns 0 for success or -errno in case of error + * * Called in postcopy mode by ram_load(). * rcu_read_lock is taken prior to this being called. + * + * @f: QEMUFile where to send the data */ static int ram_load_postcopy(QEMUFile *f) { @@ -2673,5 +2725,5 @@ static SaveVMHandlers savevm_ram_handlers = { void ram_mig_init(void) { qemu_mutex_init(&XBZRLE.lock); - register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL); + register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, &ram_state); } diff --git a/migration/rdma.c b/migration/rdma.c index 674ccab12e..fe0a4b5a83 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -809,7 +809,7 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) * * Patches are being reviewed on linux-rdma. */ -static int qemu_rdma_broken_ipv6_kernel(Error **errp, struct ibv_context *verbs) +static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) { struct ibv_port_attr port_attr; @@ -950,7 +950,7 @@ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) RDMA_RESOLVE_TIMEOUT_MS); if (!ret) { if (e->ai_family == AF_INET6) { - ret = qemu_rdma_broken_ipv6_kernel(errp, rdma->cm_id->verbs); + ret = qemu_rdma_broken_ipv6_kernel(rdma->cm_id->verbs, errp); if (ret) { continue; } @@ -2277,7 +2277,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) } -static int qemu_rdma_source_init(RDMAContext *rdma, Error **errp, bool pin_all) +static int qemu_rdma_source_init(RDMAContext *rdma, bool pin_all, Error **errp) { int ret, idx; Error *local_err = NULL, **temp = &local_err; @@ -2469,7 +2469,7 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) continue; } if (e->ai_family == AF_INET6) { - ret = qemu_rdma_broken_ipv6_kernel(errp, listen_id->verbs); + ret = qemu_rdma_broken_ipv6_kernel(listen_id->verbs, errp); if (ret) { continue; } @@ -3676,8 +3676,8 @@ void rdma_start_outgoing_migration(void *opaque, goto err; } - ret = qemu_rdma_source_init(rdma, errp, - s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL]); + ret = qemu_rdma_source_init(rdma, + s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL], errp); if (ret) { goto err; diff --git a/migration/savevm.c b/migration/savevm.c index 3b19a4a274..a00c1ab0af 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -871,7 +871,7 @@ void qemu_savevm_send_postcopy_advise(QEMUFile *f) { uint64_t tmp[2]; tmp[0] = cpu_to_be64(ram_pagesize_summary()); - tmp[1] = cpu_to_be64(1ul << qemu_target_page_bits()); + tmp[1] = cpu_to_be64(qemu_target_page_size()); trace_qemu_savevm_send_postcopy_advise(); qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_ADVISE, 16, (uint8_t *)tmp); @@ -1062,7 +1062,7 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) static bool should_send_vmdesc(void) { MachineState *machine = MACHINE(qdev_get_machine()); - bool in_postcopy = migration_in_postcopy(migrate_get_current()); + bool in_postcopy = migration_in_postcopy(); return !machine->suppress_vmdesc && !in_postcopy; } @@ -1111,7 +1111,7 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) int vmdesc_len; SaveStateEntry *se; int ret; - bool in_postcopy = migration_in_postcopy(migrate_get_current()); + bool in_postcopy = migration_in_postcopy(); trace_savevm_state_complete_precopy(); @@ -1197,7 +1197,7 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) * the result is split into the amount for units that can and * for units that can't do postcopy. */ -void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size, +void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size, uint64_t *res_non_postcopiable, uint64_t *res_postcopiable) { @@ -1216,7 +1216,7 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size, continue; } } - se->ops->save_live_pending(f, se->opaque, max_size, + se->ops->save_live_pending(f, se->opaque, threshold_size, res_non_postcopiable, res_postcopiable); } } @@ -1390,13 +1390,13 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis) } remote_tps = qemu_get_be64(mis->from_src_file); - if (remote_tps != (1ul << qemu_target_page_bits())) { + if (remote_tps != qemu_target_page_size()) { /* * Again, some differences could be dealt with, but for now keep it * simple. */ - error_report("Postcopy needs matching target page sizes (s=%d d=%d)", - (int)remote_tps, 1 << qemu_target_page_bits()); + error_report("Postcopy needs matching target page sizes (s=%d d=%zd)", + (int)remote_tps, qemu_target_page_size()); return -1; } @@ -1479,8 +1479,7 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, block_length = qemu_get_be64(mis->from_src_file); len -= 16; - int ret = ram_discard_range(mis, ramid, start_addr, - block_length); + int ret = ram_discard_range(ramid, start_addr, block_length); if (ret) { return ret; } @@ -1623,6 +1622,14 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) error_report_err(local_err); } + /* If we get an error here, just don't restart the VM yet. */ + blk_resume_after_migration(&local_err); + if (local_err) { + error_free(local_err); + local_err = NULL; + autostart = false; + } + trace_loadvm_postcopy_handle_run_cpu_sync(); cpu_synchronize_all_post_init(); diff --git a/migration/trace-events b/migration/trace-events index 7372ce2a51..b8f01a218c 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -63,8 +63,8 @@ put_qtailq_end(const char *name, const char *reason) "%s %s" qemu_file_fclose(void) "" # migration/ram.c -get_queued_page(const char *block_name, uint64_t tmp_offset, uint64_t ram_addr) "%s/%" PRIx64 " ram_addr=%" PRIx64 -get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, uint64_t ram_addr, int sent) "%s/%" PRIx64 " ram_addr=%" PRIx64 " (sent=%d)" +get_queued_page(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/%" PRIx64 " page_abs=%lx" +get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned long page_abs, int sent) "%s/%" PRIx64 " page_abs=%lx (sent=%d)" migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_throttle(void) "" @@ -974,6 +974,11 @@ static void qmp_unregister_commands_hack(void) #ifndef CONFIG_SPICE qmp_unregister_command(&qmp_commands, "query-spice"); #endif +#ifndef CONFIG_REPLICATION + qmp_unregister_command(&qmp_commands, "xen-set-replication"); + qmp_unregister_command(&qmp_commands, "query-xen-replication-status"); + qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint"); +#endif #ifndef TARGET_I386 qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection"); #endif @@ -1086,6 +1091,11 @@ static void hmp_info_registers(Monitor *mon, const QDict *qdict) static void hmp_info_jit(Monitor *mon, const QDict *qdict) { + if (!tcg_enabled()) { + error_report("JIT information is only available with accel=tcg"); + return; + } + dump_exec_info((FILE *)mon, monitor_fprintf); dump_drift_info((FILE *)mon, monitor_fprintf); } @@ -1421,6 +1431,107 @@ static void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict) memory_dump(mon, count, format, size, addr, 1); } +static void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, Error **errp) +{ + MemoryRegionSection mrs = memory_region_find(get_system_memory(), + addr, 1); + + if (!mrs.mr) { + error_setg(errp, "No memory is mapped at address 0x%" HWADDR_PRIx, addr); + return NULL; + } + + if (!memory_region_is_ram(mrs.mr) && !memory_region_is_romd(mrs.mr)) { + error_setg(errp, "Memory at address 0x%" HWADDR_PRIx "is not RAM", addr); + memory_region_unref(mrs.mr); + return NULL; + } + + *p_mr = mrs.mr; + return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region); +} + +static void hmp_gpa2hva(Monitor *mon, const QDict *qdict) +{ + hwaddr addr = qdict_get_int(qdict, "addr"); + Error *local_err = NULL; + MemoryRegion *mr = NULL; + void *ptr; + + ptr = gpa2hva(&mr, addr, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } + + monitor_printf(mon, "Host virtual address for 0x%" HWADDR_PRIx + " (%s) is %p\n", + addr, mr->name, ptr); + + memory_region_unref(mr); +} + +#ifdef CONFIG_LINUX +static uint64_t vtop(void *ptr, Error **errp) +{ + uint64_t pinfo; + uint64_t ret = -1; + uintptr_t addr = (uintptr_t) ptr; + uintptr_t pagesize = getpagesize(); + off_t offset = addr / pagesize * sizeof(pinfo); + int fd; + + fd = open("/proc/self/pagemap", O_RDONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "Cannot open /proc/self/pagemap"); + return -1; + } + + /* Force copy-on-write if necessary. */ + atomic_add((uint8_t *)ptr, 0); + + if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) { + error_setg_errno(errp, errno, "Cannot read pagemap"); + goto out; + } + if ((pinfo & (1ull << 63)) == 0) { + error_setg(errp, "Page not present"); + goto out; + } + ret = ((pinfo & 0x007fffffffffffffull) * pagesize) | (addr & (pagesize - 1)); + +out: + close(fd); + return ret; +} + +static void hmp_gpa2hpa(Monitor *mon, const QDict *qdict) +{ + hwaddr addr = qdict_get_int(qdict, "addr"); + Error *local_err = NULL; + MemoryRegion *mr = NULL; + void *ptr; + uint64_t physaddr; + + ptr = gpa2hva(&mr, addr, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } + + physaddr = vtop(ptr, &local_err); + if (local_err) { + error_report_err(local_err); + } else { + monitor_printf(mon, "Host physical address for 0x%" HWADDR_PRIx + " (%s) is 0x%" PRIx64 "\n", + addr, mr->name, (uint64_t) physaddr); + } + + memory_region_unref(mr); +} +#endif + static void do_print(Monitor *mon, const QDict *qdict) { int format = qdict_get_int(qdict, "format"); diff --git a/net/colo-compare.c b/net/colo-compare.c index 54e6d40525..4ab80b1cbb 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -233,7 +233,32 @@ static int colo_packet_compare_tcp(Packet *spkt, Packet *ppkt) spkt->ip->ip_sum = ppkt->ip->ip_sum; } - if (ptcp->th_sum == stcp->th_sum) { + /* + * Check tcp header length for tcp option field. + * th_off > 5 means this tcp packet have options field. + * The tcp options maybe always different. + * for example: + * From RFC 7323. + * TCP Timestamps option (TSopt): + * Kind: 8 + * + * Length: 10 bytes + * + * +-------+-------+---------------------+---------------------+ + * |Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)| + * +-------+-------+---------------------+---------------------+ + * 1 1 4 4 + * + * In this case the primary guest's timestamp always different with + * the secondary guest's timestamp. COLO just focus on payload, + * so we just need skip this field. + */ + if (ptcp->th_off > 5) { + ptrdiff_t tcp_offset; + tcp_offset = ppkt->transport_header - (uint8_t *)ppkt->data + + (ptcp->th_off * 4); + res = colo_packet_compare_common(ppkt, spkt, tcp_offset); + } else if (ptcp->th_sum == stcp->th_sum) { res = colo_packet_compare_common(ppkt, spkt, ETH_HLEN); } else { res = -1; @@ -372,10 +397,9 @@ static int colo_old_packet_check_one(Packet *pkt, int64_t *check_time) } } -static void colo_old_packet_check_one_conn(void *opaque, - void *user_data) +static int colo_old_packet_check_one_conn(Connection *conn, + void *user_data) { - Connection *conn = opaque; GList *result = NULL; int64_t check_time = REGULAR_PACKET_CHECK_MS; @@ -386,7 +410,10 @@ static void colo_old_packet_check_one_conn(void *opaque, if (result) { /* do checkpoint will flush old packet */ /* TODO: colo_notify_checkpoint();*/ + return 0; } + + return 1; } /* @@ -398,7 +425,12 @@ static void colo_old_packet_check(void *opaque) { CompareState *s = opaque; - g_queue_foreach(&s->conn_list, colo_old_packet_check_one_conn, NULL); + /* + * If we find one old packet, stop finding job and notify + * COLO frame do checkpoint. + */ + g_queue_find_custom(&s->conn_list, NULL, + (GCompareFunc)colo_old_packet_check_one_conn); } /* diff --git a/net/slirp.c b/net/slirp.c index f97ec23345..c705a60b62 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -80,7 +80,7 @@ typedef struct SlirpState { Slirp *slirp; Notifier exit_notifier; #ifndef _WIN32 - char smb_dir[128]; + gchar *smb_dir; #endif } SlirpState; @@ -487,7 +487,7 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, goto fail_syntax; } host_port = strtol(buf, &end, 0); - if (*end != '\0' || host_port < 1 || host_port > 65535) { + if (*end != '\0' || host_port < 0 || host_port > 65535) { goto fail_syntax; } @@ -558,11 +558,10 @@ int net_slirp_redir(const char *redir_str) /* automatic user mode samba server configuration */ static void slirp_smb_cleanup(SlirpState *s) { - char cmd[128]; int ret; - if (s->smb_dir[0] != '\0') { - snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir); + if (s->smb_dir) { + gchar *cmd = g_strdup_printf("rm -rf %s", s->smb_dir); ret = system(cmd); if (ret == -1 || !WIFEXITED(ret)) { error_report("'%s' failed.", cmd); @@ -570,15 +569,17 @@ static void slirp_smb_cleanup(SlirpState *s) error_report("'%s' failed. Error code: %d", cmd, WEXITSTATUS(ret)); } - s->smb_dir[0] = '\0'; + g_free(cmd); + g_free(s->smb_dir); + s->smb_dir = NULL; } } static int slirp_smb(SlirpState* s, const char *exported_dir, struct in_addr vserver_addr) { - char smb_conf[128]; - char smb_cmdline[128]; + char *smb_conf; + char *smb_cmdline; struct passwd *passwd; FILE *f; @@ -600,19 +601,19 @@ static int slirp_smb(SlirpState* s, const char *exported_dir, return -1; } - snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.XXXXXX"); - if (!mkdtemp(s->smb_dir)) { - error_report("could not create samba server dir '%s'", s->smb_dir); - s->smb_dir[0] = 0; + s->smb_dir = g_dir_make_tmp("qemu-smb.XXXXXX", NULL); + if (!s->smb_dir) { + error_report("could not create samba server dir"); return -1; } - snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf"); + smb_conf = g_strdup_printf("%s/%s", s->smb_dir, "smb.conf"); f = fopen(smb_conf, "w"); if (!f) { slirp_smb_cleanup(s); error_report("could not create samba server configuration file '%s'", smb_conf); + g_free(smb_conf); return -1; } fprintf(f, @@ -651,15 +652,18 @@ static int slirp_smb(SlirpState* s, const char *exported_dir, ); fclose(f); - snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -l %s -s %s", + smb_cmdline = g_strdup_printf("%s -l %s -s %s", CONFIG_SMBD_COMMAND, s->smb_dir, smb_conf); + g_free(smb_conf); if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0 || slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 445) < 0) { slirp_smb_cleanup(s); + g_free(smb_cmdline); error_report("conflicting/invalid smbserver address"); return -1; } + g_free(smb_cmdline); return 0; } diff --git a/net/socket.c b/net/socket.c index fe3547b018..b8c931e762 100644 --- a/net/socket.c +++ b/net/socket.c @@ -578,7 +578,7 @@ static int net_socket_connect_init(NetClientState *peer, goto err; } - fd = socket_connect(c->saddr, &local_error, net_socket_connected, c); + fd = socket_connect(c->saddr, net_socket_connected, c, &local_error); if (fd < 0) { goto err; } diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc Binary files differindex cfe0aee7e5..ed9a51e738 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 d6244b9ebd..1cf43dbe1d 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 74f67f2724..281d20d604 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex 2a4adfa654..0b01d49495 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 0339c24789..79a46b6735 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o virtio-scsi.o +OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS)) QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index b21c877b53..523fa78c5f 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -183,15 +183,21 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) static void run_eckd_boot_script(block_number_t mbr_block_nr) { int i; + unsigned int loadparm = get_loadparm_index(); block_number_t block_nr; uint64_t address; - ScsiMbr *scsi_mbr = (void *)sec; + ScsiMbr *bte = (void *)sec; /* Eckd bootmap table entry */ BootMapScript *bms = (void *)sec; + debug_print_int("loadparm", loadparm); + IPL_assert(loadparm < 31, "loadparm value greater than" + " maximum number of boot entries allowed"); + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(mbr_block_nr, sec, "Cannot read MBR"); - block_nr = eckd_block_num((void *)&(scsi_mbr->blockptr)); + block_nr = eckd_block_num((void *)&(bte->blockptr[loadparm])); + IPL_assert(block_nr != -1, "No Boot Map"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(block_nr, sec, "Cannot read Boot Map Script"); @@ -444,7 +450,8 @@ static void ipl_scsi(void) uint8_t *ns, *ns_end; int program_table_entries = 0; const int pte_len = sizeof(ScsiBlockPtr); - ScsiBlockPtr *prog_table_entry; + ScsiBlockPtr *prog_table_entry = NULL; + unsigned int loadparm = get_loadparm_index(); /* Grab the MBR */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -458,15 +465,16 @@ static void ipl_scsi(void) debug_print_int("MBR Version", mbr->version_id); IPL_check(mbr->version_id == 1, "Unknown MBR layout version, assuming version 1"); - debug_print_int("program table", mbr->blockptr.blockno); - IPL_assert(mbr->blockptr.blockno, "No Program Table"); + debug_print_int("program table", mbr->blockptr[0].blockno); + IPL_assert(mbr->blockptr[0].blockno, "No Program Table"); /* Parse the program table */ - read_block(mbr->blockptr.blockno, sec, + read_block(mbr->blockptr[0].blockno, sec, "Error reading Program Table"); IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); + debug_print_int("loadparm index", loadparm); ns_end = sec + virtio_get_block_size(); for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns += pte_len) { prog_table_entry = (ScsiBlockPtr *)ns; @@ -475,16 +483,15 @@ static void ipl_scsi(void) } program_table_entries++; + if (program_table_entries == loadparm + 1) { + break; /* selected entry found */ + } } debug_print_int("program table entries", program_table_entries); IPL_assert(program_table_entries != 0, "Empty Program Table"); - /* Run the default entry */ - - prog_table_entry = (ScsiBlockPtr *)(sec + pte_len); - zipl_run(prog_table_entry); /* no return */ } @@ -648,6 +655,7 @@ static IsoBcSection *find_iso_bc_entry(void) IsoBcEntry *e = (IsoBcEntry *)sec; uint32_t offset = find_iso_bc(); int i; + unsigned int loadparm = get_loadparm_index(); if (!offset) { return NULL; @@ -668,7 +676,11 @@ static IsoBcSection *find_iso_bc_entry(void) for (i = 1; i < ISO_BC_ENTRY_PER_SECTOR; i++) { if (e[i].id == ISO_BC_BOOTABLE_SECTION) { if (is_iso_bc_entry_compatible(&e[i].body.sect)) { - return &e[i].body.sect; + if (loadparm <= 1) { + /* found, default, or unspecified */ + return &e[i].body.sect; + } + loadparm--; } } } diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index bea168714b..7f367820f3 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -70,7 +70,7 @@ typedef struct ScsiMbr { uint8_t magic[4]; uint32_t version_id; uint8_t reserved[8]; - ScsiBlockPtr blockptr; + ScsiBlockPtr blockptr[]; } __attribute__ ((packed)) ScsiMbr; #define ZIPL_MAGIC "zIPL" @@ -264,28 +264,6 @@ typedef enum { /* utility code below */ -static const unsigned char ebc2asc[256] = - /* 0123456789abcdef0123456789abcdef */ - "................................" /* 1F */ - "................................" /* 3F */ - " ...........<(+|&.........!$*);." /* 5F first.chr.here.is.real.space */ - "-/.........,%_>?.........`:#@'=\""/* 7F */ - ".abcdefghi.......jklmnopqr......" /* 9F */ - "..stuvwxyz......................" /* BF */ - ".ABCDEFGHI.......JKLMNOPQR......" /* DF */ - "..STUVWXYZ......0123456789......";/* FF */ - -static inline void ebcdic_to_ascii(const char *src, - char *dst, - unsigned int size) -{ - unsigned int i; - for (i = 0; i < size; i++) { - unsigned c = src[i]; - dst[i] = ebc2asc[c]; - } -} - static inline void print_volser(const void *volser) { char ascii[8]; diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 0946766d86..1cacc1b46f 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -14,6 +14,18 @@ char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); static SubChannelId blk_schid = { .one = 1 }; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); +static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +const unsigned char ebc2asc[256] = + /* 0123456789abcdef0123456789abcdef */ + "................................" /* 1F */ + "................................" /* 3F */ + " ...........<(+|&.........!$*);." /* 5F first.chr.here.is.real.space */ + "-/.........,%_>?.........`:#@'=\""/* 7F */ + ".abcdefghi.......jklmnopqr......" /* 9F */ + "..stuvwxyz......................" /* BF */ + ".ABCDEFGHI.......JKLMNOPQR......" /* DF */ + "..STUVWXYZ......0123456789......";/* FF */ /* * Priniciples of Operations (SA22-7832-09) chapter 17 requires that @@ -29,7 +41,6 @@ void write_subsystem_identification(void) *zeroes = 0; } - void panic(const char *string) { sclp_print(string); @@ -37,6 +48,26 @@ void panic(const char *string) while (1) { } } +unsigned int get_loadparm_index(void) +{ + const char *lp = loadparm; + int i; + unsigned int idx = 0; + + for (i = 0; i < 8; i++) { + char c = lp[i]; + + if (c < '0' || c > '9') { + break; + } + + idx *= 10; + idx += c - '0'; + } + + return idx; +} + static bool find_dev(Schib *schib, int dev_no) { int i, r; @@ -73,6 +104,7 @@ static void virtio_setup(void) int ssid; bool found = false; uint16_t dev_no; + char ldp[] = "LOADPARM=[________]\n"; VDev *vdev = virtio_get_device(); /* @@ -82,6 +114,10 @@ static void virtio_setup(void) */ enable_mss_facility(); + sclp_get_loadparm_ascii(loadparm); + memcpy(ldp + 10, loadparm, 8); + sclp_print(ldp); + if (store_iplb(&iplb)) { switch (iplb.pbt) { case S390_IPL_TYPE_CCW: diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index ded67bcbc6..07d8cbcb20 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -62,10 +62,12 @@ void consume_sclp_int(void); void panic(const char *string); void write_subsystem_identification(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); +unsigned int get_loadparm_index(void); -/* sclp-ascii.c */ +/* sclp.c */ void sclp_print(const char *string); void sclp_setup(void); +void sclp_get_loadparm_ascii(char *loadparm); /* virtio.c */ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, @@ -189,4 +191,17 @@ static inline void IPL_check(bool term, const char *message) } } +extern const unsigned char ebc2asc[256]; +static inline void ebcdic_to_ascii(const char *src, + char *dst, + unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i++) { + unsigned c = src[i]; + dst[i] = ebc2asc[c]; + } +} + #endif /* S390_CCW_H */ diff --git a/pc-bios/s390-ccw/sclp-ascii.c b/pc-bios/s390-ccw/sclp.c index dc1c3e4f4d..a1639baed7 100644 --- a/pc-bios/s390-ccw/sclp-ascii.c +++ b/pc-bios/s390-ccw/sclp.c @@ -80,3 +80,15 @@ void sclp_print(const char *str) sclp_service_call(SCLP_CMD_WRITE_EVENT_DATA, sccb); } + +void sclp_get_loadparm_ascii(char *loadparm) +{ + + ReadInfo *sccb = (void *)_sccb; + + memset((char *)_sccb, 0, sizeof(ReadInfo)); + sccb->h.length = sizeof(ReadInfo); + if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) { + ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8); + } +} diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h index 3cbfb78930..0dd987ff5d 100644 --- a/pc-bios/s390-ccw/sclp.h +++ b/pc-bios/s390-ccw/sclp.h @@ -55,6 +55,8 @@ typedef struct ReadInfo { SCCBHeader h; uint16_t rnmax; uint8_t rnsize; + uint8_t reserved[13]; + uint8_t loadparm[8]; } __attribute__((packed)) ReadInfo; typedef struct SCCB { diff --git a/qapi-schema.json b/qapi-schema.json index 250e4dc49b..01b087fa16 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -598,6 +598,9 @@ # @postcopy-requests: The number of page requests received from the destination # (since 2.7) # +# @page-size: The number of bytes per page for the various page-based +# statistics (since 2.10) +# # Since: 0.14.0 ## { 'struct': 'MigrationStats', @@ -605,7 +608,7 @@ 'duplicate': 'int', 'skipped': 'int', 'normal': 'int', 'normal-bytes': 'int', 'dirty-pages-rate' : 'int', 'mbps' : 'number', 'dirty-sync-count' : 'int', - 'postcopy-requests' : 'int' } } + 'postcopy-requests' : 'int', 'page-size' : 'int' } } ## # @XBZRLECacheStats: diff --git a/qapi/block-core.json b/qapi/block-core.json index 033457ce86..87fb747ab6 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2108,6 +2108,8 @@ # # Drivers that are supported in block device operations. # +# @vxhs: Since 2.10 +# # Since: 2.9 ## { 'enum': 'BlockdevDriver', @@ -2116,7 +2118,7 @@ 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', - 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } + 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } ## # @BlockdevOptionsFile: @@ -2866,6 +2868,22 @@ 'data': { '*offset': 'int', '*size': 'int' } } ## +# @BlockdevOptionsVxHS: +# +# Driver specific block device options for VxHS +# +# @vdisk-id: UUID of VxHS volume +# @server: vxhs server IP, port +# @tls-creds: TLS credentials ID +# +# Since: 2.10 +## +{ 'struct': 'BlockdevOptionsVxHS', + 'data': { 'vdisk-id': 'str', + 'server': 'InetSocketAddressBase', + '*tls-creds': 'str' } } + +## # @BlockdevOptions: # # Options for creating a block device. Many options are available for all @@ -2927,7 +2945,8 @@ 'vhdx': 'BlockdevOptionsGenericFormat', 'vmdk': 'BlockdevOptionsGenericCOWFormat', 'vpc': 'BlockdevOptionsGenericFormat', - 'vvfat': 'BlockdevOptionsVVFAT' + 'vvfat': 'BlockdevOptionsVVFAT', + 'vxhs': 'BlockdevOptionsVxHS' } } ## diff --git a/qdev-monitor.c b/qdev-monitor.c index 5f2fcdfc45..e61d596ef7 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -29,6 +29,7 @@ #include "qemu/error-report.h" #include "qemu/help_option.h" #include "sysemu/block-backend.h" +#include "migration/migration.h" /* * Aliases were a bad idea from the start. Let's keep them @@ -603,6 +604,11 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) return NULL; } + if (!migration_is_idle()) { + error_setg(errp, "device_add not allowed while migrating"); + return NULL; + } + /* create device */ dev = DEVICE(object_new(driver)); @@ -836,6 +842,45 @@ static DeviceState *find_device_state(const char *id, Error **errp) return DEVICE(obj); } +void qdev_unplug(DeviceState *dev, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + HotplugHandler *hotplug_ctrl; + HotplugHandlerClass *hdc; + + if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { + error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); + return; + } + + if (!dc->hotpluggable) { + error_setg(errp, QERR_DEVICE_NO_HOTPLUG, + object_get_typename(OBJECT(dev))); + return; + } + + if (!migration_is_idle()) { + error_setg(errp, "device_del not allowed while migrating"); + return; + } + + qdev_hot_removed = true; + + hotplug_ctrl = qdev_get_hotplug_handler(dev); + /* hotpluggable device MUST have HotplugHandler, if it doesn't + * then something is very wrong with it */ + g_assert(hotplug_ctrl); + + /* If device supports async unplug just request it to be done, + * otherwise just remove it synchronously */ + hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl); + if (hdc->unplug_request) { + hotplug_handler_unplug_request(hotplug_ctrl, dev, errp); + } else { + hotplug_handler_unplug(hotplug_ctrl, dev, errp); + } +} + void qmp_device_del(const char *id, Error **errp) { DeviceState *dev = find_device_state(id, errp); diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 8ac78222af..bf4ce59019 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -22,9 +22,9 @@ STEXI ETEXI DEF("create", img_create, - "create [-q] [--object objectdef] [-f fmt] [-o options] filename [size]") + "create [-q] [--object objectdef] [-f fmt] [-b backing_file] [-F backing_fmt] [-o options] filename [size]") STEXI -@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] +@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-o @var{options}] @var{filename} [@var{size}] ETEXI DEF("commit", img_commit, @@ -40,9 +40,9 @@ STEXI ETEXI DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") STEXI -@item convert [--object @var{objectdef}] [--image-opts] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("dd", img_dd, diff --git a/qemu-img.c b/qemu-img.c index b220cf71d7..c7196362df 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -516,7 +516,7 @@ static int img_create(int argc, char **argv) } bdrv_img_create(filename, fmt, base_filename, base_fmt, - options, img_size, 0, &local_err, quiet); + options, img_size, 0, quiet, &local_err); if (local_err) { error_reportf_err(local_err, "%s: ", filename); goto fail; @@ -984,7 +984,7 @@ static int img_commit(int argc, char **argv) aio_context_acquire(aio_context); commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, - &cbi, &local_err, false); + &cbi, false, &local_err); aio_context_release(aio_context); if (local_err) { goto done; @@ -1522,7 +1522,7 @@ typedef struct ImgConvertState { int min_sparse; size_t cluster_sectors; size_t buf_sectors; - int num_coroutines; + long num_coroutines; int running_coroutines; Coroutine *co[MAX_COROUTINES]; int64_t wait_sector_num[MAX_COROUTINES]; @@ -1554,9 +1554,15 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) if (s->sector_next_status <= sector_num) { BlockDriverState *file; - ret = bdrv_get_block_status(blk_bs(s->src[src_cur]), - sector_num - src_cur_offset, - n, &n, &file); + if (s->target_has_backing) { + ret = bdrv_get_block_status(blk_bs(s->src[src_cur]), + sector_num - src_cur_offset, + n, &n, &file); + } else { + ret = bdrv_get_block_status_above(blk_bs(s->src[src_cur]), NULL, + sector_num - src_cur_offset, + n, &n, &file); + } if (ret < 0) { return ret; } @@ -1565,26 +1571,8 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) s->status = BLK_ZERO; } else if (ret & BDRV_BLOCK_DATA) { s->status = BLK_DATA; - } else if (!s->target_has_backing) { - /* Without a target backing file we must copy over the contents of - * the backing file as well. */ - /* Check block status of the backing file chain to avoid - * needlessly reading zeroes and limiting the iteration to the - * buffer size */ - ret = bdrv_get_block_status_above(blk_bs(s->src[src_cur]), NULL, - sector_num - src_cur_offset, - n, &n, &file); - if (ret < 0) { - return ret; - } - - if (ret & BDRV_BLOCK_ZERO) { - s->status = BLK_ZERO; - } else { - s->status = BLK_DATA; - } } else { - s->status = BLK_BACKING_FILE; + s->status = s->target_has_backing ? BLK_BACKING_FILE : BLK_DATA; } s->sector_next_status = sector_num + n; @@ -1661,6 +1649,8 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, while (nb_sectors > 0) { int n = nb_sectors; + BdrvRequestFlags flags = s->compressed ? BDRV_REQ_WRITE_COMPRESSED : 0; + switch (status) { case BLK_BACKING_FILE: /* If we have a backing file, leave clusters unallocated that are @@ -1670,43 +1660,24 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, break; case BLK_DATA: - /* We must always write compressed clusters as a whole, so don't - * try to find zeroed parts in the buffer. We can only save the - * write if the buffer is completely zeroed and we're allowed to - * keep the target sparse. */ - if (s->compressed) { - if (s->has_zero_init && s->min_sparse && - buffer_is_zero(buf, n * BDRV_SECTOR_SIZE)) - { - assert(!s->target_has_backing); - break; - } - - iov.iov_base = buf; - iov.iov_len = n << BDRV_SECTOR_BITS; - qemu_iovec_init_external(&qiov, &iov, 1); - - ret = blk_co_pwritev(s->target, sector_num << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, &qiov, - BDRV_REQ_WRITE_COMPRESSED); - if (ret < 0) { - return ret; - } - break; - } - - /* If there is real non-zero data or we're told to keep the target - * fully allocated (-S 0), we must write it. Otherwise we can treat - * it as zero sectors. */ + /* If we're told to keep the target fully allocated (-S 0) or there + * is real non-zero data, we must write it. Otherwise we can treat + * it as zero sectors. + * Compressed clusters need to be written as a whole, so in that + * case we can only save the write if the buffer is completely + * zeroed. */ if (!s->min_sparse || - is_allocated_sectors_min(buf, n, &n, s->min_sparse)) + (!s->compressed && + is_allocated_sectors_min(buf, n, &n, s->min_sparse)) || + (s->compressed && + !buffer_is_zero(buf, n * BDRV_SECTOR_SIZE))) { iov.iov_base = buf; iov.iov_len = n << BDRV_SECTOR_BITS; qemu_iovec_init_external(&qiov, &iov, 1); ret = blk_co_pwritev(s->target, sector_num << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, &qiov, 0); + n << BDRV_SECTOR_BITS, &qiov, flags); if (ret < 0) { return ret; } @@ -1716,6 +1687,7 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, case BLK_ZERO: if (s->has_zero_init) { + assert(!s->target_has_backing); break; } ret = blk_co_pwrite_zeroes(s->target, @@ -1916,39 +1888,29 @@ static int convert_do_copy(ImgConvertState *s) static int img_convert(int argc, char **argv) { - int c, bs_n, bs_i, compress, cluster_sectors, skip_create; - int64_t ret = 0; - int progress = 0, flags, src_flags; - bool writethrough, src_writethrough; - const char *fmt, *out_fmt, *cache, *src_cache, *out_baseimg, *out_filename; + int c, bs_i, flags, src_flags = 0; + const char *fmt = NULL, *out_fmt = "raw", *cache = "unsafe", + *src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL, + *out_filename, *out_baseimg_param, *snapshot_name = NULL; BlockDriver *drv, *proto_drv; - BlockBackend **blk = NULL, *out_blk = NULL; - BlockDriverState **bs = NULL, *out_bs = NULL; - int64_t total_sectors; - int64_t *bs_sectors = NULL; - size_t bufsectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE; BlockDriverInfo bdi; - QemuOpts *opts = NULL; + BlockDriverState *out_bs; + QemuOpts *opts = NULL, *sn_opts = NULL; QemuOptsList *create_opts = NULL; - const char *out_baseimg_param; char *options = NULL; - const char *snapshot_name = NULL; - int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */ - bool quiet = false; Error *local_err = NULL; - QemuOpts *sn_opts = NULL; - ImgConvertState state; - bool image_opts = false; - bool wr_in_order = true; - long num_coroutines = 8; + bool writethrough, src_writethrough, quiet = false, image_opts = false, + skip_create = false, progress = false; + int64_t ret = -EINVAL; + + ImgConvertState s = (ImgConvertState) { + /* Need at least 4k of zeros for sparse detection */ + .min_sparse = 8, + .buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE, + .wr_in_order = true, + .num_coroutines = 8, + }; - fmt = NULL; - out_fmt = "raw"; - cache = "unsafe"; - src_cache = BDRV_DEFAULT_CACHE; - out_baseimg = NULL; - compress = 0; - skip_create = 0; for(;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -1981,22 +1943,19 @@ static int img_convert(int argc, char **argv) out_baseimg = optarg; break; case 'c': - compress = 1; + s.compressed = true; break; case 'e': error_report("option -e is deprecated, please use \'-o " "encryption\' instead!"); - ret = -1; goto fail_getopt; case '6': error_report("option -6 is deprecated, please use \'-o " "compat6\' instead!"); - ret = -1; goto fail_getopt; case 'o': if (!is_valid_option_list(optarg)) { error_report("Invalid option list: %s", optarg); - ret = -1; goto fail_getopt; } if (!options) { @@ -2017,7 +1976,6 @@ static int img_convert(int argc, char **argv) if (!sn_opts) { error_report("Failed in parsing snapshot param '%s'", optarg); - ret = -1; goto fail_getopt; } } else { @@ -2031,15 +1989,14 @@ static int img_convert(int argc, char **argv) sval = cvtnum(optarg); if (sval < 0) { error_report("Invalid minimum zero buffer size for sparse output specified"); - ret = -1; goto fail_getopt; } - min_sparse = sval / BDRV_SECTOR_SIZE; + s.min_sparse = sval / BDRV_SECTOR_SIZE; break; } case 'p': - progress = 1; + progress = true; break; case 't': cache = optarg; @@ -2051,27 +2008,28 @@ static int img_convert(int argc, char **argv) quiet = true; break; case 'n': - skip_create = 1; + skip_create = true; break; case 'm': - if (qemu_strtol(optarg, NULL, 0, &num_coroutines) || - num_coroutines < 1 || num_coroutines > MAX_COROUTINES) { + if (qemu_strtol(optarg, NULL, 0, &s.num_coroutines) || + s.num_coroutines < 1 || s.num_coroutines > MAX_COROUTINES) { error_report("Invalid number of coroutines. Allowed number of" " coroutines is between 1 and %d", MAX_COROUTINES); - ret = -1; goto fail_getopt; } break; case 'W': - wr_in_order = false; + s.wr_in_order = false; break; - case OPTION_OBJECT: - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { + case OPTION_OBJECT: { + QemuOpts *object_opts; + object_opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!object_opts) { goto fail_getopt; } break; + } case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -2084,83 +2042,73 @@ static int img_convert(int argc, char **argv) goto fail_getopt; } - if (!wr_in_order && compress) { + if (!s.wr_in_order && s.compressed) { error_report("Out of order write and compress are mutually exclusive"); - ret = -1; goto fail_getopt; } - /* Initialize before goto out */ - if (quiet) { - progress = 0; - } - qemu_progress_init(progress, 1.0); - - bs_n = argc - optind - 1; - out_filename = bs_n >= 1 ? argv[argc - 1] : NULL; + s.src_num = argc - optind - 1; + out_filename = s.src_num >= 1 ? argv[argc - 1] : NULL; if (options && has_help_option(options)) { ret = print_block_option_help(out_filename, out_fmt); - goto out; + goto fail_getopt; } - if (bs_n < 1) { - error_exit("Must specify image file name"); + if (s.src_num < 1) { + error_report("Must specify image file name"); + goto fail_getopt; } - if (bs_n > 1 && out_baseimg) { - error_report("-B makes no sense when concatenating multiple input " - "images"); - ret = -1; - goto out; - } - - src_flags = 0; + /* ret is still -EINVAL until here */ ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", src_cache); - goto out; + goto fail_getopt; } + /* Initialize before goto out */ + if (quiet) { + progress = false; + } + qemu_progress_init(progress, 1.0); qemu_progress_print(0, 100); - blk = g_new0(BlockBackend *, bs_n); - bs = g_new0(BlockDriverState *, bs_n); - bs_sectors = g_new(int64_t, bs_n); + s.src = g_new0(BlockBackend *, s.src_num); + s.src_sectors = g_new(int64_t, s.src_num); - total_sectors = 0; - for (bs_i = 0; bs_i < bs_n; bs_i++) { - blk[bs_i] = img_open(image_opts, argv[optind + bs_i], - fmt, src_flags, src_writethrough, quiet); - if (!blk[bs_i]) { + for (bs_i = 0; bs_i < s.src_num; bs_i++) { + s.src[bs_i] = img_open(image_opts, argv[optind + bs_i], + fmt, src_flags, src_writethrough, quiet); + if (!s.src[bs_i]) { ret = -1; goto out; } - bs[bs_i] = blk_bs(blk[bs_i]); - bs_sectors[bs_i] = blk_nb_sectors(blk[bs_i]); - if (bs_sectors[bs_i] < 0) { + s.src_sectors[bs_i] = blk_nb_sectors(s.src[bs_i]); + if (s.src_sectors[bs_i] < 0) { error_report("Could not get size of %s: %s", - argv[optind + bs_i], strerror(-bs_sectors[bs_i])); + argv[optind + bs_i], strerror(-s.src_sectors[bs_i])); ret = -1; goto out; } - total_sectors += bs_sectors[bs_i]; + s.total_sectors += s.src_sectors[bs_i]; } if (sn_opts) { - bdrv_snapshot_load_tmp(bs[0], + bdrv_snapshot_load_tmp(blk_bs(s.src[0]), qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME), &local_err); } else if (snapshot_name != NULL) { - if (bs_n > 1) { + if (s.src_num > 1) { error_report("No support for concatenating multiple snapshot"); ret = -1; goto out; } - bdrv_snapshot_load_tmp_by_id_or_name(bs[0], snapshot_name, &local_err); + bdrv_snapshot_load_tmp_by_id_or_name(blk_bs(s.src[0]), snapshot_name, + &local_err); } if (local_err) { error_reportf_err(local_err, "Failed to load snapshot: "); @@ -2211,7 +2159,7 @@ static int img_convert(int argc, char **argv) } } - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512, + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s.total_sectors * 512, &error_abort); ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL); if (ret < 0) { @@ -2224,9 +2172,17 @@ static int img_convert(int argc, char **argv) if (out_baseimg_param) { out_baseimg = out_baseimg_param; } + s.target_has_backing = (bool) out_baseimg; + + if (s.src_num > 1 && out_baseimg) { + error_report("Having a backing file for the target makes no sense when " + "concatenating multiple input images"); + ret = -1; + goto out; + } /* Check if compression is supported */ - if (compress) { + if (s.compressed) { bool encryption = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, false); const char *preallocation = @@ -2265,7 +2221,7 @@ static int img_convert(int argc, char **argv) } } - flags = min_sparse ? (BDRV_O_RDWR | BDRV_O_UNMAP) : BDRV_O_RDWR; + flags = s.min_sparse ? (BDRV_O_RDWR | BDRV_O_UNMAP) : BDRV_O_RDWR; ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); @@ -2277,64 +2233,48 @@ static int img_convert(int argc, char **argv) * the bdrv_create() call which takes different params. * Not critical right now, so fix can wait... */ - out_blk = img_open_file(out_filename, out_fmt, flags, writethrough, quiet); - if (!out_blk) { + s.target = img_open_file(out_filename, out_fmt, flags, writethrough, quiet); + if (!s.target) { ret = -1; goto out; } - out_bs = blk_bs(out_blk); + out_bs = blk_bs(s.target); /* increase bufsectors from the default 4096 (2M) if opt_transfer * or discard_alignment of the out_bs is greater. Limit to 32768 (16MB) * as maximum. */ - bufsectors = MIN(32768, - MAX(bufsectors, - MAX(out_bs->bl.opt_transfer >> BDRV_SECTOR_BITS, - out_bs->bl.pdiscard_alignment >> - BDRV_SECTOR_BITS))); + s.buf_sectors = MIN(32768, + MAX(s.buf_sectors, + MAX(out_bs->bl.opt_transfer >> BDRV_SECTOR_BITS, + out_bs->bl.pdiscard_alignment >> + BDRV_SECTOR_BITS))); if (skip_create) { - int64_t output_sectors = blk_nb_sectors(out_blk); + int64_t output_sectors = blk_nb_sectors(s.target); if (output_sectors < 0) { error_report("unable to get output image length: %s", strerror(-output_sectors)); ret = -1; goto out; - } else if (output_sectors < total_sectors) { + } else if (output_sectors < s.total_sectors) { error_report("output file is smaller than input file"); ret = -1; goto out; } } - cluster_sectors = 0; ret = bdrv_get_info(out_bs, &bdi); if (ret < 0) { - if (compress) { + if (s.compressed) { error_report("could not get block driver info"); goto out; } } else { - compress = compress || bdi.needs_compressed_writes; - cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; - } - - state = (ImgConvertState) { - .src = blk, - .src_sectors = bs_sectors, - .src_num = bs_n, - .total_sectors = total_sectors, - .target = out_blk, - .compressed = compress, - .target_has_backing = (bool) out_baseimg, - .min_sparse = min_sparse, - .cluster_sectors = cluster_sectors, - .buf_sectors = bufsectors, - .wr_in_order = wr_in_order, - .num_coroutines = num_coroutines, - }; - ret = convert_do_copy(&state); + s.compressed = s.compressed || bdi.needs_compressed_writes; + s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; + } + ret = convert_do_copy(&s); out: if (!ret) { qemu_progress_print(100, 0); @@ -2343,22 +2283,18 @@ out: qemu_opts_del(opts); qemu_opts_free(create_opts); qemu_opts_del(sn_opts); - blk_unref(out_blk); - g_free(bs); - if (blk) { - for (bs_i = 0; bs_i < bs_n; bs_i++) { - blk_unref(blk[bs_i]); + blk_unref(s.target); + if (s.src) { + for (bs_i = 0; bs_i < s.src_num; bs_i++) { + blk_unref(s.src[bs_i]); } - g_free(blk); + g_free(s.src); } - g_free(bs_sectors); + g_free(s.src_sectors); fail_getopt: g_free(options); - if (ret) { - return 1; - } - return 0; + return !!ret; } @@ -3500,20 +3436,11 @@ static int img_resize(int argc, char **argv) goto out; } - ret = blk_truncate(blk, total_size); - switch (ret) { - case 0: + ret = blk_truncate(blk, total_size, &err); + if (!ret) { qprintf(quiet, "Image resized.\n"); - break; - case -ENOTSUP: - error_report("This image does not support resize"); - break; - case -EACCES: - error_report("Image is read-only"); - break; - default: - error_report("Error resizing image: %s", strerror(-ret)); - break; + } else { + error_report_err(err); } out: blk_unref(blk); diff --git a/qemu-img.texi b/qemu-img.texi index c81db3e81c..50a2364e80 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -84,7 +84,8 @@ with or without a command shows help and lists the supported formats @item -p display progress bar (compare, convert and rebase commands only). If the @var{-p} option is not used for a command that supports it, the -progress is reported when the process receives a @code{SIGUSR1} signal. +progress is reported when the process receives a @code{SIGUSR1} or +@code{SIGINFO} signal. @item -q Quiet mode - do not print any output (except errors). There's no progress bar in case both @var{-q} and @var{-p} options are used. @@ -224,7 +225,7 @@ If @code{-r} is specified, exit codes representing the image state refer to the state after (the attempt at) repairing it. That is, a successful @code{-r all} will yield the exit code 0, independently of the image state before. -@item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] +@item create [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-o @var{options}] @var{filename} [@var{size}] Create the new disk image @var{filename} of size @var{size} and format @var{fmt}. Depending on the file format, you can add one or more @var{options} @@ -302,7 +303,7 @@ Error on reading data @end table -@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-m @var{num_coroutines}] [-W] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-m @var{num_coroutines}] [-W] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated) to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 312fc6d157..21af9e65b2 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1567,6 +1567,7 @@ static const cmdinfo_t flush_cmd = { static int truncate_f(BlockBackend *blk, int argc, char **argv) { + Error *local_err = NULL; int64_t offset; int ret; @@ -1576,9 +1577,9 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv) return 0; } - ret = blk_truncate(blk, offset); + ret = blk_truncate(blk, offset, &local_err); if (ret < 0) { - printf("truncate: %s\n", strerror(-ret)); + error_report_err(local_err); return 0; } diff --git a/qemu-options.hx b/qemu-options.hx index 99af8edf5f..f68829f3b0 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -425,7 +425,7 @@ possible drivers and properties, use @code{-device help} and @code{-device @var{driver},help}. Some drivers are: -@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}] +@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}][,sdrfile=@var{file}][,furareasize=@var{val}][,furdatafile=@var{file}] Add an IPMI BMC. This is a simulation of a hardware management interface processor that normally sits on a system. It provides @@ -437,6 +437,19 @@ This address is the BMC's address on the I2C network of management controllers. If you don't know what this means, it is safe to ignore it. +@table @option +@item bmc=@var{id} +The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. +@item slave_addr=@var{val} +Define slave address to use for the BMC. The default is 0x20. +@item sdrfile=@var{file} +file containing raw Sensor Data Records (SDR) data. The default is none. +@item fruareasize=@var{val} +size of a Field Replaceable Unit (FRU) area. The default is 1024. +@item frudatafile=@var{file} +file containing raw Field Replaceable Unit (FRU) inventory data. The default is none. +@end table + @item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}] Add a connection to an external IPMI BMC simulator. Instead of @@ -635,6 +648,30 @@ file sectors into the image file. conversion of plain zero writes by the OS to driver specific optimized zero write commands. You may even choose "unmap" if @var{discard} is set to "unmap" to allow a zero write to be converted to an UNMAP operation. +@item bps=@var{b},bps_rd=@var{r},bps_wr=@var{w} +Specify bandwidth throttling limits in bytes per second, either for all request +types or for reads or writes only. Small values can lead to timeouts or hangs +inside the guest. A safe minimum for disks is 2 MB/s. +@item bps_max=@var{bm},bps_rd_max=@var{rm},bps_wr_max=@var{wm} +Specify bursts in bytes per second, either for all request types or for reads +or writes only. Bursts allow the guest I/O to spike above the limit +temporarily. +@item iops=@var{i},iops_rd=@var{r},iops_wr=@var{w} +Specify request rate limits in requests per second, either for all request +types or for reads or writes only. +@item iops_max=@var{bm},iops_rd_max=@var{rm},iops_wr_max=@var{wm} +Specify bursts in requests per second, either for all request types or for reads +or writes only. Bursts allow the guest I/O to spike above the limit +temporarily. +@item iops_size=@var{is} +Let every @var{is} bytes of a request count as a new request for iops +throttling purposes. Use this option to prevent guests from circumventing iops +limits by sending fewer but larger requests. +@item group=@var{g} +Join a throttling quota group with given name @var{g}. All drives that are +members of the same group are accounted for together. Use this option to +prevent guests from circumventing throttling limits by using many small disks +instead of a single larger disk. @end table By default, the @option{cache=writeback} mode is used. It will report data @@ -766,8 +803,8 @@ STEXI Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <= @var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS translation mode (@var{t}=none, lba or auto). Usually QEMU can guess -all those parameters. This option is useful for old MS-DOS disk -images. +all those parameters. This option is deprecated, please use +@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead. ETEXI DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, @@ -3354,6 +3391,11 @@ DEF("xen-attach", 0, QEMU_OPTION_xen_attach, "-xen-attach attach to existing xen domain\n" " xend will use this when starting QEMU\n", QEMU_ARCH_ALL) +DEF("xen-domid-restrict", 0, QEMU_OPTION_xen_domid_restrict, + "-xen-domid-restrict restrict set of available xen operations\n" + " to specified domain id. (Does not affect\n" + " xenpv machine type).\n", + QEMU_ARCH_ALL) STEXI @item -xen-domid @var{id} @findex -xen-domid @@ -3366,6 +3408,8 @@ Warning: should not be used when xend is in use (XEN only). @findex -xen-attach Attach to existing xen domain. xend will use this when starting QEMU (XEN only). +@findex -xen-domid-restrict +Restrict set of available xen operations to specified domain id (XEN only). ETEXI DEF("no-reboot", 0, QEMU_OPTION_no_reboot, \ diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 915df9ed90..ba06be4c86 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -15,6 +15,7 @@ #include <sys/ioctl.h> #include <sys/wait.h> #include <dirent.h> +#include <utmpx.h> #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -2517,3 +2518,62 @@ void ga_command_state_init(GAState *s, GACommandState *cs) ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); #endif } + +#define QGA_MICRO_SECOND_TO_SECOND 1000000 + +static double ga_get_login_time(struct utmpx *user_info) +{ + double seconds = (double)user_info->ut_tv.tv_sec; + double useconds = (double)user_info->ut_tv.tv_usec; + useconds /= QGA_MICRO_SECOND_TO_SECOND; + return seconds + useconds; +} + +GuestUserList *qmp_guest_get_users(Error **err) +{ + GHashTable *cache = NULL; + GuestUserList *head = NULL, *cur_item = NULL; + struct utmpx *user_info = NULL; + gpointer value = NULL; + GuestUser *user = NULL; + GuestUserList *item = NULL; + double login_time = 0; + + cache = g_hash_table_new(g_str_hash, g_str_equal); + setutxent(); + + for (;;) { + user_info = getutxent(); + if (user_info == NULL) { + break; + } else if (user_info->ut_type != USER_PROCESS) { + continue; + } else if (g_hash_table_contains(cache, user_info->ut_user)) { + value = g_hash_table_lookup(cache, user_info->ut_user); + user = (GuestUser *)value; + login_time = ga_get_login_time(user_info); + /* We're ensuring the earliest login time to be sent */ + if (login_time < user->login_time) { + user->login_time = login_time; + } + continue; + } + + item = g_new0(GuestUserList, 1); + item->value = g_new0(GuestUser, 1); + item->value->user = g_strdup(user_info->ut_user); + item->value->login_time = ga_get_login_time(user_info); + + g_hash_table_insert(cache, item->value->user, item->value); + + if (!cur_item) { + head = cur_item = item; + } else { + cur_item->next = item; + cur_item = item; + } + } + endutxent(); + g_hash_table_destroy(cache); + return head; +} diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 19d72b2411..439d229225 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -11,6 +11,9 @@ * See the COPYING file in the top-level directory. */ +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0600 +#endif #include "qemu/osdep.h" #include <wtypes.h> #include <powrprof.h> @@ -25,6 +28,7 @@ #include <initguid.h> #endif #include <lm.h> +#include <wtsapi32.h> #include "qga/guest-agent-core.h" #include "qga/vss-win32.h" @@ -768,7 +772,7 @@ int64_t qmp_guest_fsfreeze_freeze(Error **errp) /* cannot risk guest agent blocking itself on a write in this state */ ga_set_frozen(ga_state); - qga_vss_fsfreeze(&i, &local_err, true); + qga_vss_fsfreeze(&i, true, &local_err); if (local_err) { error_propagate(errp, local_err); goto error; @@ -807,7 +811,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) return 0; } - qga_vss_fsfreeze(&i, errp, false); + qga_vss_fsfreeze(&i, false, errp); ga_unset_frozen(ga_state); return i; @@ -1344,7 +1348,7 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) vcpu = g_malloc0(sizeof *vcpu); vcpu->logical_id = current++; vcpu->online = true; - vcpu->has_can_offline = false; + vcpu->has_can_offline = true; entry = g_malloc0(sizeof *entry); entry->value = vcpu; @@ -1536,3 +1540,102 @@ void ga_command_state_init(GAState *s, GACommandState *cs) ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); } } + +/* MINGW is missing two fields: IncomingFrames & OutgoingFrames */ +typedef struct _GA_WTSINFOA { + WTS_CONNECTSTATE_CLASS State; + DWORD SessionId; + DWORD IncomingBytes; + DWORD OutgoingBytes; + DWORD IncomingFrames; + DWORD OutgoingFrames; + DWORD IncomingCompressedBytes; + DWORD OutgoingCompressedBy; + CHAR WinStationName[WINSTATIONNAME_LENGTH]; + CHAR Domain[DOMAIN_LENGTH]; + CHAR UserName[USERNAME_LENGTH + 1]; + LARGE_INTEGER ConnectTime; + LARGE_INTEGER DisconnectTime; + LARGE_INTEGER LastInputTime; + LARGE_INTEGER LogonTime; + LARGE_INTEGER CurrentTime; + +} GA_WTSINFOA; + +GuestUserList *qmp_guest_get_users(Error **err) +{ +#if (_WIN32_WINNT >= 0x0600) +#define QGA_NANOSECONDS 10000000 + + GHashTable *cache = NULL; + GuestUserList *head = NULL, *cur_item = NULL; + + DWORD buffer_size = 0, count = 0, i = 0; + GA_WTSINFOA *info = NULL; + WTS_SESSION_INFOA *entries = NULL; + GuestUserList *item = NULL; + GuestUser *user = NULL; + gpointer value = NULL; + INT64 login = 0; + double login_time = 0; + + cache = g_hash_table_new(g_str_hash, g_str_equal); + + if (WTSEnumerateSessionsA(NULL, 0, 1, &entries, &count)) { + for (i = 0; i < count; ++i) { + buffer_size = 0; + info = NULL; + if (WTSQuerySessionInformationA( + NULL, + entries[i].SessionId, + WTSSessionInfo, + (LPSTR *)&info, + &buffer_size + )) { + + if (strlen(info->UserName) == 0) { + WTSFreeMemory(info); + continue; + } + + login = info->LogonTime.QuadPart; + login -= W32_FT_OFFSET; + login_time = ((double)login) / QGA_NANOSECONDS; + + if (g_hash_table_contains(cache, info->UserName)) { + value = g_hash_table_lookup(cache, info->UserName); + user = (GuestUser *)value; + if (user->login_time > login_time) { + user->login_time = login_time; + } + } else { + item = g_new0(GuestUserList, 1); + item->value = g_new0(GuestUser, 1); + + item->value->user = g_strdup(info->UserName); + item->value->domain = g_strdup(info->Domain); + item->value->has_domain = true; + + item->value->login_time = login_time; + + g_hash_table_add(cache, item->value->user); + + if (!cur_item) { + head = cur_item = item; + } else { + cur_item->next = item; + cur_item = item; + } + } + } + WTSFreeMemory(info); + } + WTSFreeMemory(entries); + } + g_hash_table_destroy(cache); + return head; +#else + error_setg(err, QERR_UNSUPPORTED); + return NULL; +#endif +} diff --git a/qga/commands.c b/qga/commands.c index 4d92946820..3333ed50b2 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -499,3 +499,52 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp) error_setg(errp, "invalid whence code %"PRId64, whence->u.value); return -1; } + +GuestHostName *qmp_guest_get_host_name(Error **err) +{ + GuestHostName *result = NULL; + gchar const *hostname = g_get_host_name(); + if (hostname != NULL) { + result = g_new0(GuestHostName, 1); + result->host_name = g_strdup(hostname); + } + return result; +} + +GuestTimezone *qmp_guest_get_timezone(Error **errp) +{ +#if GLIB_CHECK_VERSION(2, 28, 0) + GuestTimezone *info = NULL; + GTimeZone *tz = NULL; + gint64 now = 0; + gint32 intv = 0; + gchar const *name = NULL; + + info = g_new0(GuestTimezone, 1); + tz = g_time_zone_new_local(); + if (tz == NULL) { + error_setg(errp, QERR_QGA_COMMAND_FAILED, + "Couldn't retrieve local timezone"); + goto error; + } + + now = g_get_real_time() / G_USEC_PER_SEC; + intv = g_time_zone_find_interval(tz, G_TIME_TYPE_UNIVERSAL, now); + info->offset = g_time_zone_get_offset(tz, intv); + name = g_time_zone_get_abbreviation(tz, intv); + if (name != NULL) { + info->has_zone = true; + info->zone = g_strdup(name); + } + g_time_zone_unref(tz); + + return info; + +error: + g_free(info); + return NULL; +#else + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +#endif +} diff --git a/qga/main.c b/qga/main.c index 07c295376f..ad6f68f187 100644 --- a/qga/main.c +++ b/qga/main.c @@ -131,9 +131,32 @@ static void quit_handler(int sig) * unless all log/pid files are on unfreezable filesystems. there's * also a very likely chance killing the agent before unfreezing * the filesystems is a mistake (or will be viewed as one later). + * On Windows the freeze interval is limited to 10 seconds, so + * we should quit, but first we should wait for the timeout, thaw + * the filesystem and quit. */ if (ga_is_frozen(ga_state)) { +#ifdef _WIN32 + int i = 0; + Error *err = NULL; + HANDLE hEventTimeout; + + g_debug("Thawing filesystems before exiting"); + + hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); + if (hEventTimeout) { + WaitForSingleObject(hEventTimeout, 0); + CloseHandle(hEventTimeout); + } + qga_vss_fsfreeze(&i, false, &err); + if (err) { + g_debug("Error unfreezing filesystems prior to exiting: %s", + error_get_pretty(err)); + error_free(err); + } +#else return; +#endif } g_debug("received signal num %d, quitting", sig); diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index a02dbf2d18..03743ab905 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -426,7 +426,13 @@ ## # @guest-fsfreeze-freeze: # -# Sync and freeze all freezable, local guest filesystems +# Sync and freeze all freezable, local guest filesystems. If this +# command succeeded, you may call @guest-fsfreeze-thaw later to +# unfreeze. +# +# Note: On Windows, the command is implemented with the help of a +# Volume Shadow-copy Service DLL helper. The frozen state is limited +# for up to 10 seconds by VSS. # # Returns: Number of file systems currently frozen. On error, all filesystems # will be thawed. @@ -439,10 +445,12 @@ ## # @guest-fsfreeze-freeze-list: # -# Sync and freeze specified guest filesystems +# Sync and freeze specified guest filesystems. +# See also @guest-fsfreeze-freeze. # # @mountpoints: an array of mountpoints of filesystems to be frozen. # If omitted, every mounted filesystem is frozen. +# Invalid mount points are ignored. # # Returns: Number of file systems currently frozen. On error, all filesystems # will be thawed. @@ -1042,3 +1050,79 @@ 'data': { 'path': 'str', '*arg': ['str'], '*env': ['str'], '*input-data': 'str', '*capture-output': 'bool' }, 'returns': 'GuestExec' } + + +## +# @GuestHostName: +# @host-name: Fully qualified domain name of the guest OS +# +# Since: 2.10 +## +{ 'struct': 'GuestHostName', + 'data': { 'host-name': 'str' } } + +## +# @guest-get-host-name: +# +# Return a name for the machine. +# +# The returned name is not necessarily a fully-qualified domain name, or even +# present in DNS or some other name service at all. It need not even be unique +# on your local network or site, but usually it is. +# +# Returns: the host name of the machine on success +# +# Since: 2.10 +## +{ 'command': 'guest-get-host-name', + 'returns': 'GuestHostName' } + + +## +# @GuestUser: +# @user: Username +# @domain: Logon domain (windows only) +# @login-time: Time of login of this user on the computer. If multiple +# instances of the user are logged in, the earliest login time is +# reported. The value is in fractional seconds since epoch time. +# +# Since: 2.10 +## +{ 'struct': 'GuestUser', + 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' } } + +## +# @guest-get-users: +# Retrieves a list of currently active users on the VM. +# +# Returns: A unique list of users. +# +# Since: 2.10 +## +{ 'command': 'guest-get-users', + 'returns': ['GuestUser'] } + +## +# @GuestTimezone: +# +# @zone: Timezone name. These values may differ depending on guest/OS and +# should only be used for informational purposes. +# @offset: Offset to UTC in seconds, negative numbers for time zones west of +# GMT, positive numbers for east +# +# Since: 2.10 +## +{ 'struct': 'GuestTimezone', + 'data': { '*zone': 'str', 'offset': 'int' } } + +## +# @guest-get-timezone: +# +# Retrieves the timezone information from the guest. +# +# Returns: A GuestTimezone dictionary. +# +# Since: 2.10 +## +{ 'command': 'guest-get-timezone', + 'returns': 'GuestTimezone' } diff --git a/qga/vss-win32.c b/qga/vss-win32.c index 9a0e46356a..a80933c98b 100644 --- a/qga/vss-win32.c +++ b/qga/vss-win32.c @@ -145,7 +145,7 @@ void ga_uninstall_vss_provider(void) } /* Call VSS requester and freeze/thaw filesystems and applications */ -void qga_vss_fsfreeze(int *nr_volume, Error **errp, bool freeze) +void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp) { const char *func_name = freeze ? "requester_freeze" : "requester_thaw"; QGAVSSRequesterFunc func; diff --git a/qga/vss-win32.h b/qga/vss-win32.h index 4d1d15081e..4f8e39aa5c 100644 --- a/qga/vss-win32.h +++ b/qga/vss-win32.h @@ -13,6 +13,7 @@ #ifndef VSS_WIN32_H #define VSS_WIN32_H +#include "qga/vss-win32/vss-handles.h" bool vss_init(bool init_requester); void vss_deinit(bool deinit_requester); @@ -21,6 +22,6 @@ bool vss_initialized(void); int ga_install_vss_provider(void); void ga_uninstall_vss_provider(void); -void qga_vss_fsfreeze(int *nr_volume, Error **errp, bool freeze); +void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp); #endif diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index f4160a3a86..f41fcdfdda 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -14,7 +14,7 @@ #include "vss-common.h" #include <inc/win2003/vscoordint.h> -#include <comadmin.h> +#include "install.h" #include <wbemidl.h> #include <comdef.h> #include <comutil.h> @@ -276,7 +276,7 @@ STDAPI COMRegister(void) chk(pCatalog->CreateServiceForApplication( _bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME), - _bstr_t(L"SERVICE_AUTO_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"), + _bstr_t(L"SERVICE_DEMAND_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"), _bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE)); chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME), _bstr_t(dllPath), _bstr_t(tlbPath), @@ -461,3 +461,27 @@ namespace _com_util return bstr; } } + +/* Stop QGA VSS provider service from COM+ Application Admin Catalog */ + +STDAPI StopService(void) +{ + HRESULT hr; + COMInitializer initializer; + COMPointer<IUnknown> pUnknown; + COMPointer<ICOMAdminCatalog2> pCatalog; + + int count = 0; + + chk(QGAProviderFind(QGAProviderCount, (void *)&count)); + if (count) { + chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, + IID_IUnknown, (void **)pUnknown.replace())); + chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, + (void **)pCatalog.replace())); + chk(pCatalog->ShutdownApplication(_bstr_t(QGA_PROVIDER_LNAME))); + } + +out: + return hr; +} diff --git a/qga/vss-win32/install.h b/qga/vss-win32/install.h new file mode 100644 index 0000000000..35364afdea --- /dev/null +++ b/qga/vss-win32/install.h @@ -0,0 +1,20 @@ +/* + * 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 INSTALL_H +#define INSTALL_H + +#include <comadmin.h> + +STDAPI StopService(void); + +#endif diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index ef9466909a..72d8b0e19d 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -377,7 +377,6 @@ STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId) 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); diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 0cd2f0ee7f..301762d8b1 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "vss-common.h" #include "requester.h" +#include "install.h" #include <inc/win2003/vswriter.h> #include <inc/win2003/vsbackup.h> @@ -501,4 +502,5 @@ void requester_thaw(int *num_vols, ErrorSet *errset) requester_cleanup(); CoUninitialize(); + StopService(); } diff --git a/qga/vss-win32/vss-common.h b/qga/vss-win32/vss-common.h index c81a8564b2..61c170b52e 100644 --- a/qga/vss-win32/vss-common.h +++ b/qga/vss-win32/vss-common.h @@ -51,21 +51,12 @@ * http://www.microsoft.com/en-us/download/details.aspx?id=23490 */ #include <inc/win2003/vss.h> +#include "vss-handles.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, diff --git a/qga/vss-win32/vss-handles.h b/qga/vss-win32/vss-handles.h new file mode 100644 index 0000000000..ff399dd73a --- /dev/null +++ b/qga/vss-win32/vss-handles.h @@ -0,0 +1,14 @@ +#ifndef VSS_HANDLES +#define VSS_HANDLES + +/* 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" + +#endif @@ -548,11 +548,6 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename, return NULL; } - if (DEVICE_CLASS(klass)->cannot_destroy_with_object_finalize_yet) { - error_setg(errp, "Can't list properties of device '%s'", typename); - return NULL; - } - obj = object_new(typename); object_property_iter_init(&iter, obj); diff --git a/roms/openbios b/roms/openbios -Subproject f233c3f72cfa79c1123a7ccef08d2f7e228da6d +Subproject 04898e8ce4c2f7bd94c7eeff9d26f2ff23aae8d diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index 4c990047b6..d60b3a08f7 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -116,7 +116,28 @@ class Analyzer(object): is invoked. If a method matching a trace event name exists, it is invoked to process - that trace record. Otherwise the catchall() method is invoked.""" + that trace record. Otherwise the catchall() method is invoked. + + Example: + The following method handles the runstate_set(int new_state) trace event:: + + def runstate_set(self, new_state): + ... + + The method can also take a timestamp argument before the trace event + arguments:: + + def runstate_set(self, timestamp, new_state): + ... + + Timestamps have the uint64_t type and are in nanoseconds. + + The pid can be included in addition to the timestamp and is useful when + dealing with traces from multiple processes:: + + def runstate_set(self, timestamp, pid, new_state): + ... + """ def begin(self): """Called at the start of the trace.""" diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs index 1baa1f1c7c..28049b03cd 100644 --- a/slirp/Makefile.objs +++ b/slirp/Makefile.objs @@ -2,4 +2,4 @@ common-obj-y = cksum.o if.o ip_icmp.o ip6_icmp.o ip6_input.o ip6_output.o \ ip_input.o ip_output.o dnssearch.o dhcpv6.o common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o common-obj-y += tcp_subr.o tcp_timer.o udp.o udp6.o bootp.o tftp.o arp_table.o \ - ndp_table.o + ndp_table.o ncsi.o diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c index 5ffc7a683d..0b667a429a 100644 --- a/slirp/ip_icmp.c +++ b/slirp/ip_icmp.c @@ -152,8 +152,9 @@ icmp_input(struct mbuf *m, int hlen) switch (icp->icmp_type) { case ICMP_ECHO: ip->ip_len += hlen; /* since ip_input subtracts this */ - if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { - icmp_reflect(m); + if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || + ip->ip_dst.s_addr == slirp->vnameserver_addr.s_addr) { + icmp_reflect(m); } else if (slirp->restricted) { goto freeit; } else { diff --git a/slirp/ncsi-pkt.h b/slirp/ncsi-pkt.h new file mode 100644 index 0000000000..ea07d1cd0f --- /dev/null +++ b/slirp/ncsi-pkt.h @@ -0,0 +1,419 @@ +/* + * Copyright Gavin Shan, IBM Corporation 2016. + * + * 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. + */ + +#ifndef NCSI_PKT_H +#define NCSI_PKT_H + +/* from linux/net/ncsi/ncsi-pkt.h */ +#define __be32 uint32_t +#define __be16 uint16_t + +struct ncsi_pkt_hdr { + unsigned char mc_id; /* Management controller ID */ + unsigned char revision; /* NCSI version - 0x01 */ + unsigned char reserved; /* Reserved */ + unsigned char id; /* Packet sequence number */ + unsigned char type; /* Packet type */ + unsigned char channel; /* Network controller ID */ + __be16 length; /* Payload length */ + __be32 reserved1[2]; /* Reserved */ +}; + +struct ncsi_cmd_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ +}; + +struct ncsi_rsp_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ + __be16 code; /* Response code */ + __be16 reason; /* Response reason */ +}; + +struct ncsi_aen_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ + unsigned char reserved2[3]; /* Reserved */ + unsigned char type; /* AEN packet type */ +}; + +/* NCSI common command packet */ +struct ncsi_cmd_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 checksum; /* Checksum */ + unsigned char pad[26]; +}; + +struct ncsi_rsp_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Select Package */ +struct ncsi_cmd_sp_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char hw_arbitration; /* HW arbitration */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Disable Channel */ +struct ncsi_cmd_dc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char ald; /* Allow link down */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Reset Channel */ +struct ncsi_cmd_rc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 reserved; /* Reserved */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* AEN Enable */ +struct ncsi_cmd_ae_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mc_id; /* MC ID */ + __be32 mode; /* AEN working mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* Set Link */ +struct ncsi_cmd_sl_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Link working mode */ + __be32 oem_mode; /* OEM link mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* Set VLAN Filter */ +struct ncsi_cmd_svf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be16 reserved; /* Reserved */ + __be16 vlan; /* VLAN ID */ + __be16 reserved1; /* Reserved */ + unsigned char index; /* VLAN table index */ + unsigned char enable; /* Enable or disable */ + __be32 checksum; /* Checksum */ + unsigned char pad[14]; +}; + +/* Enable VLAN */ +struct ncsi_cmd_ev_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mode; /* VLAN filter mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Set MAC Address */ +struct ncsi_cmd_sma_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char mac[6]; /* MAC address */ + unsigned char index; /* MAC table index */ + unsigned char at_e; /* Addr type and operation */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* Enable Broadcast Filter */ +struct ncsi_cmd_ebf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Filter mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Enable Global Multicast Filter */ +struct ncsi_cmd_egmf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Global MC mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Set NCSI Flow Control */ +struct ncsi_cmd_snfc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mode; /* Flow control mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Get Link Status */ +struct ncsi_rsp_gls_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 status; /* Link status */ + __be32 other; /* Other indications */ + __be32 oem_status; /* OEM link status */ + __be32 checksum; + unsigned char pad[10]; +}; + +/* Get Version ID */ +struct ncsi_rsp_gvi_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 ncsi_version; /* NCSI version */ + unsigned char reserved[3]; /* Reserved */ + unsigned char alpha2; /* NCSI version */ + unsigned char fw_name[12]; /* f/w name string */ + __be32 fw_version; /* f/w version */ + __be16 pci_ids[4]; /* PCI IDs */ + __be32 mf_id; /* Manufacture ID */ + __be32 checksum; +}; + +/* Get Capabilities */ +struct ncsi_rsp_gc_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 cap; /* Capabilities */ + __be32 bc_cap; /* Broadcast cap */ + __be32 mc_cap; /* Multicast cap */ + __be32 buf_cap; /* Buffering cap */ + __be32 aen_cap; /* AEN cap */ + unsigned char vlan_cnt; /* VLAN filter count */ + unsigned char mixed_cnt; /* Mix filter count */ + unsigned char mc_cnt; /* MC filter count */ + unsigned char uc_cnt; /* UC filter count */ + unsigned char reserved[2]; /* Reserved */ + unsigned char vlan_mode; /* VLAN mode */ + unsigned char channel_cnt; /* Channel count */ + __be32 checksum; /* Checksum */ +}; + +/* Get Parameters */ +struct ncsi_rsp_gp_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + unsigned char mac_cnt; /* Number of MAC addr */ + unsigned char reserved[2]; /* Reserved */ + unsigned char mac_enable; /* MAC addr enable flags */ + unsigned char vlan_cnt; /* VLAN tag count */ + unsigned char reserved1; /* Reserved */ + __be16 vlan_enable; /* VLAN tag enable flags */ + __be32 link_mode; /* Link setting */ + __be32 bc_mode; /* BC filter mode */ + __be32 valid_modes; /* Valid mode parameters */ + unsigned char vlan_mode; /* VLAN mode */ + unsigned char fc_mode; /* Flow control mode */ + unsigned char reserved2[2]; /* Reserved */ + __be32 aen_mode; /* AEN mode */ + unsigned char mac[6]; /* Supported MAC addr */ + __be16 vlan; /* Supported VLAN tags */ + __be32 checksum; /* Checksum */ +}; + +/* Get Controller Packet Statistics */ +struct ncsi_rsp_gcps_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 cnt_hi; /* Counter cleared */ + __be32 cnt_lo; /* Counter cleared */ + __be32 rx_bytes; /* Rx bytes */ + __be32 tx_bytes; /* Tx bytes */ + __be32 rx_uc_pkts; /* Rx UC packets */ + __be32 rx_mc_pkts; /* Rx MC packets */ + __be32 rx_bc_pkts; /* Rx BC packets */ + __be32 tx_uc_pkts; /* Tx UC packets */ + __be32 tx_mc_pkts; /* Tx MC packets */ + __be32 tx_bc_pkts; /* Tx BC packets */ + __be32 fcs_err; /* FCS errors */ + __be32 align_err; /* Alignment errors */ + __be32 false_carrier; /* False carrier detection */ + __be32 runt_pkts; /* Rx runt packets */ + __be32 jabber_pkts; /* Rx jabber packets */ + __be32 rx_pause_xon; /* Rx pause XON frames */ + __be32 rx_pause_xoff; /* Rx XOFF frames */ + __be32 tx_pause_xon; /* Tx XON frames */ + __be32 tx_pause_xoff; /* Tx XOFF frames */ + __be32 tx_s_collision; /* Single collision frames */ + __be32 tx_m_collision; /* Multiple collision frames */ + __be32 l_collision; /* Late collision frames */ + __be32 e_collision; /* Excessive collision frames */ + __be32 rx_ctl_frames; /* Rx control frames */ + __be32 rx_64_frames; /* Rx 64-bytes frames */ + __be32 rx_127_frames; /* Rx 65-127 bytes frames */ + __be32 rx_255_frames; /* Rx 128-255 bytes frames */ + __be32 rx_511_frames; /* Rx 256-511 bytes frames */ + __be32 rx_1023_frames; /* Rx 512-1023 bytes frames */ + __be32 rx_1522_frames; /* Rx 1024-1522 bytes frames */ + __be32 rx_9022_frames; /* Rx 1523-9022 bytes frames */ + __be32 tx_64_frames; /* Tx 64-bytes frames */ + __be32 tx_127_frames; /* Tx 65-127 bytes frames */ + __be32 tx_255_frames; /* Tx 128-255 bytes frames */ + __be32 tx_511_frames; /* Tx 256-511 bytes frames */ + __be32 tx_1023_frames; /* Tx 512-1023 bytes frames */ + __be32 tx_1522_frames; /* Tx 1024-1522 bytes frames */ + __be32 tx_9022_frames; /* Tx 1523-9022 bytes frames */ + __be32 rx_valid_bytes; /* Rx valid bytes */ + __be32 rx_runt_pkts; /* Rx error runt packets */ + __be32 rx_jabber_pkts; /* Rx error jabber packets */ + __be32 checksum; /* Checksum */ +}; + +/* Get NCSI Statistics */ +struct ncsi_rsp_gns_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 rx_cmds; /* Rx NCSI commands */ + __be32 dropped_cmds; /* Dropped commands */ + __be32 cmd_type_errs; /* Command type errors */ + __be32 cmd_csum_errs; /* Command checksum errors */ + __be32 rx_pkts; /* Rx NCSI packets */ + __be32 tx_pkts; /* Tx NCSI packets */ + __be32 tx_aen_pkts; /* Tx AEN packets */ + __be32 checksum; /* Checksum */ +}; + +/* Get NCSI Pass-through Statistics */ +struct ncsi_rsp_gnpts_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 tx_pkts; /* Tx packets */ + __be32 tx_dropped; /* Tx dropped packets */ + __be32 tx_channel_err; /* Tx channel errors */ + __be32 tx_us_err; /* Tx undersize errors */ + __be32 rx_pkts; /* Rx packets */ + __be32 rx_dropped; /* Rx dropped packets */ + __be32 rx_channel_err; /* Rx channel errors */ + __be32 rx_us_err; /* Rx undersize errors */ + __be32 rx_os_err; /* Rx oversize errors */ + __be32 checksum; /* Checksum */ +}; + +/* Get package status */ +struct ncsi_rsp_gps_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 status; /* Hardware arbitration status */ + __be32 checksum; +}; + +/* Get package UUID */ +struct ncsi_rsp_gpuuid_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + unsigned char uuid[16]; /* UUID */ + __be32 checksum; +}; + +/* AEN: Link State Change */ +struct ncsi_aen_lsc_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 status; /* Link status */ + __be32 oem_status; /* OEM link status */ + __be32 checksum; /* Checksum */ + unsigned char pad[14]; +}; + +/* AEN: Configuration Required */ +struct ncsi_aen_cr_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* AEN: Host Network Controller Driver Status Change */ +struct ncsi_aen_hncdsc_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 status; /* Status */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* NCSI packet revision */ +#define NCSI_PKT_REVISION 0x01 + +/* NCSI packet commands */ +#define NCSI_PKT_CMD_CIS 0x00 /* Clear Initial State */ +#define NCSI_PKT_CMD_SP 0x01 /* Select Package */ +#define NCSI_PKT_CMD_DP 0x02 /* Deselect Package */ +#define NCSI_PKT_CMD_EC 0x03 /* Enable Channel */ +#define NCSI_PKT_CMD_DC 0x04 /* Disable Channel */ +#define NCSI_PKT_CMD_RC 0x05 /* Reset Channel */ +#define NCSI_PKT_CMD_ECNT 0x06 /* Enable Channel Network Tx */ +#define NCSI_PKT_CMD_DCNT 0x07 /* Disable Channel Network Tx */ +#define NCSI_PKT_CMD_AE 0x08 /* AEN Enable */ +#define NCSI_PKT_CMD_SL 0x09 /* Set Link */ +#define NCSI_PKT_CMD_GLS 0x0a /* Get Link */ +#define NCSI_PKT_CMD_SVF 0x0b /* Set VLAN Filter */ +#define NCSI_PKT_CMD_EV 0x0c /* Enable VLAN */ +#define NCSI_PKT_CMD_DV 0x0d /* Disable VLAN */ +#define NCSI_PKT_CMD_SMA 0x0e /* Set MAC address */ +#define NCSI_PKT_CMD_EBF 0x10 /* Enable Broadcast Filter */ +#define NCSI_PKT_CMD_DBF 0x11 /* Disable Broadcast Filter */ +#define NCSI_PKT_CMD_EGMF 0x12 /* Enable Global Multicast Filter */ +#define NCSI_PKT_CMD_DGMF 0x13 /* Disable Global Multicast Filter */ +#define NCSI_PKT_CMD_SNFC 0x14 /* Set NCSI Flow Control */ +#define NCSI_PKT_CMD_GVI 0x15 /* Get Version ID */ +#define NCSI_PKT_CMD_GC 0x16 /* Get Capabilities */ +#define NCSI_PKT_CMD_GP 0x17 /* Get Parameters */ +#define NCSI_PKT_CMD_GCPS 0x18 /* Get Controller Packet Statistics */ +#define NCSI_PKT_CMD_GNS 0x19 /* Get NCSI Statistics */ +#define NCSI_PKT_CMD_GNPTS 0x1a /* Get NCSI Pass-throu Statistics */ +#define NCSI_PKT_CMD_GPS 0x1b /* Get package status */ +#define NCSI_PKT_CMD_OEM 0x50 /* OEM */ +#define NCSI_PKT_CMD_PLDM 0x51 /* PLDM request over NCSI over RBT */ +#define NCSI_PKT_CMD_GPUUID 0x52 /* Get package UUID */ + +/* NCSI packet responses */ +#define NCSI_PKT_RSP_CIS (NCSI_PKT_CMD_CIS + 0x80) +#define NCSI_PKT_RSP_SP (NCSI_PKT_CMD_SP + 0x80) +#define NCSI_PKT_RSP_DP (NCSI_PKT_CMD_DP + 0x80) +#define NCSI_PKT_RSP_EC (NCSI_PKT_CMD_EC + 0x80) +#define NCSI_PKT_RSP_DC (NCSI_PKT_CMD_DC + 0x80) +#define NCSI_PKT_RSP_RC (NCSI_PKT_CMD_RC + 0x80) +#define NCSI_PKT_RSP_ECNT (NCSI_PKT_CMD_ECNT + 0x80) +#define NCSI_PKT_RSP_DCNT (NCSI_PKT_CMD_DCNT + 0x80) +#define NCSI_PKT_RSP_AE (NCSI_PKT_CMD_AE + 0x80) +#define NCSI_PKT_RSP_SL (NCSI_PKT_CMD_SL + 0x80) +#define NCSI_PKT_RSP_GLS (NCSI_PKT_CMD_GLS + 0x80) +#define NCSI_PKT_RSP_SVF (NCSI_PKT_CMD_SVF + 0x80) +#define NCSI_PKT_RSP_EV (NCSI_PKT_CMD_EV + 0x80) +#define NCSI_PKT_RSP_DV (NCSI_PKT_CMD_DV + 0x80) +#define NCSI_PKT_RSP_SMA (NCSI_PKT_CMD_SMA + 0x80) +#define NCSI_PKT_RSP_EBF (NCSI_PKT_CMD_EBF + 0x80) +#define NCSI_PKT_RSP_DBF (NCSI_PKT_CMD_DBF + 0x80) +#define NCSI_PKT_RSP_EGMF (NCSI_PKT_CMD_EGMF + 0x80) +#define NCSI_PKT_RSP_DGMF (NCSI_PKT_CMD_DGMF + 0x80) +#define NCSI_PKT_RSP_SNFC (NCSI_PKT_CMD_SNFC + 0x80) +#define NCSI_PKT_RSP_GVI (NCSI_PKT_CMD_GVI + 0x80) +#define NCSI_PKT_RSP_GC (NCSI_PKT_CMD_GC + 0x80) +#define NCSI_PKT_RSP_GP (NCSI_PKT_CMD_GP + 0x80) +#define NCSI_PKT_RSP_GCPS (NCSI_PKT_CMD_GCPS + 0x80) +#define NCSI_PKT_RSP_GNS (NCSI_PKT_CMD_GNS + 0x80) +#define NCSI_PKT_RSP_GNPTS (NCSI_PKT_CMD_GNPTS + 0x80) +#define NCSI_PKT_RSP_GPS (NCSI_PKT_CMD_GPS + 0x80) +#define NCSI_PKT_RSP_OEM (NCSI_PKT_CMD_OEM + 0x80) +#define NCSI_PKT_RSP_PLDM (NCSI_PKT_CMD_PLDM + 0x80) +#define NCSI_PKT_RSP_GPUUID (NCSI_PKT_CMD_GPUUID + 0x80) + +/* NCSI response code/reason */ +#define NCSI_PKT_RSP_C_COMPLETED 0x0000 /* Command Completed */ +#define NCSI_PKT_RSP_C_FAILED 0x0001 /* Command Failed */ +#define NCSI_PKT_RSP_C_UNAVAILABLE 0x0002 /* Command Unavailable */ +#define NCSI_PKT_RSP_C_UNSUPPORTED 0x0003 /* Command Unsupported */ +#define NCSI_PKT_RSP_R_NO_ERROR 0x0000 /* No Error */ +#define NCSI_PKT_RSP_R_INTERFACE 0x0001 /* Interface not ready */ +#define NCSI_PKT_RSP_R_PARAM 0x0002 /* Invalid Parameter */ +#define NCSI_PKT_RSP_R_CHANNEL 0x0003 /* Channel not Ready */ +#define NCSI_PKT_RSP_R_PACKAGE 0x0004 /* Package not Ready */ +#define NCSI_PKT_RSP_R_LENGTH 0x0005 /* Invalid payload length */ +#define NCSI_PKT_RSP_R_UNKNOWN 0x7fff /* Command type unsupported */ + +/* NCSI AEN packet type */ +#define NCSI_PKT_AEN 0xFF /* AEN Packet */ +#define NCSI_PKT_AEN_LSC 0x00 /* Link status change */ +#define NCSI_PKT_AEN_CR 0x01 /* Configuration required */ +#define NCSI_PKT_AEN_HNCDSC 0x02 /* HNC driver status change */ + +#endif /* NCSI_PKT_H */ diff --git a/slirp/ncsi.c b/slirp/ncsi.c new file mode 100644 index 0000000000..d12ba3e494 --- /dev/null +++ b/slirp/ncsi.c @@ -0,0 +1,130 @@ +/* + * NC-SI (Network Controller Sideband Interface) "echo" model + * + * Copyright (C) 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "slirp.h" + +#include "ncsi-pkt.h" + +/* Get Capabilities */ +static int ncsi_rsp_handler_gc(struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gc_pkt *rsp = (struct ncsi_rsp_gc_pkt *) rnh; + + rsp->cap = htonl(~0); + rsp->bc_cap = htonl(~0); + rsp->mc_cap = htonl(~0); + rsp->buf_cap = htonl(~0); + rsp->aen_cap = htonl(~0); + rsp->vlan_mode = 0xff; + rsp->uc_cnt = 2; + return 0; +} + +/* Get Link status */ +static int ncsi_rsp_handler_gls(struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gls_pkt *rsp = (struct ncsi_rsp_gls_pkt *) rnh; + + rsp->status = htonl(0x1); + return 0; +} + +static const struct ncsi_rsp_handler { + unsigned char type; + int payload; + int (*handler)(struct ncsi_rsp_pkt_hdr *rnh); +} ncsi_rsp_handlers[] = { + { NCSI_PKT_RSP_CIS, 4, NULL }, + { NCSI_PKT_RSP_SP, 4, NULL }, + { NCSI_PKT_RSP_DP, 4, NULL }, + { NCSI_PKT_RSP_EC, 4, NULL }, + { NCSI_PKT_RSP_DC, 4, NULL }, + { NCSI_PKT_RSP_RC, 4, NULL }, + { NCSI_PKT_RSP_ECNT, 4, NULL }, + { NCSI_PKT_RSP_DCNT, 4, NULL }, + { NCSI_PKT_RSP_AE, 4, NULL }, + { NCSI_PKT_RSP_SL, 4, NULL }, + { NCSI_PKT_RSP_GLS, 16, ncsi_rsp_handler_gls }, + { NCSI_PKT_RSP_SVF, 4, NULL }, + { NCSI_PKT_RSP_EV, 4, NULL }, + { NCSI_PKT_RSP_DV, 4, NULL }, + { NCSI_PKT_RSP_SMA, 4, NULL }, + { NCSI_PKT_RSP_EBF, 4, NULL }, + { NCSI_PKT_RSP_DBF, 4, NULL }, + { NCSI_PKT_RSP_EGMF, 4, NULL }, + { NCSI_PKT_RSP_DGMF, 4, NULL }, + { NCSI_PKT_RSP_SNFC, 4, NULL }, + { NCSI_PKT_RSP_GVI, 36, NULL }, + { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc }, + { NCSI_PKT_RSP_GP, -1, NULL }, + { NCSI_PKT_RSP_GCPS, 172, NULL }, + { NCSI_PKT_RSP_GNS, 172, NULL }, + { NCSI_PKT_RSP_GNPTS, 172, NULL }, + { NCSI_PKT_RSP_GPS, 8, NULL }, + { NCSI_PKT_RSP_OEM, 0, NULL }, + { NCSI_PKT_RSP_PLDM, 0, NULL }, + { NCSI_PKT_RSP_GPUUID, 20, NULL } +}; + +/* + * packet format : ncsi header + payload + checksum + */ +#define NCSI_MAX_PAYLOAD 172 +#define NCSI_MAX_LEN (sizeof(struct ncsi_pkt_hdr) + NCSI_MAX_PAYLOAD + 4) + +void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) +{ + struct ncsi_pkt_hdr *nh = (struct ncsi_pkt_hdr *)(pkt + ETH_HLEN); + uint8_t ncsi_reply[ETH_HLEN + NCSI_MAX_LEN]; + struct ethhdr *reh = (struct ethhdr *)ncsi_reply; + struct ncsi_rsp_pkt_hdr *rnh = (struct ncsi_rsp_pkt_hdr *) + (ncsi_reply + ETH_HLEN); + const struct ncsi_rsp_handler *handler = NULL; + int i; + + memset(ncsi_reply, 0, sizeof(ncsi_reply)); + + memset(reh->h_dest, 0xff, ETH_ALEN); + memset(reh->h_source, 0xff, ETH_ALEN); + reh->h_proto = htons(ETH_P_NCSI); + + for (i = 0; i < ARRAY_SIZE(ncsi_rsp_handlers); i++) { + if (ncsi_rsp_handlers[i].type == nh->type + 0x80) { + handler = &ncsi_rsp_handlers[i]; + break; + } + } + + rnh->common.mc_id = nh->mc_id; + rnh->common.revision = NCSI_PKT_REVISION; + rnh->common.id = nh->id; + rnh->common.type = nh->type + 0x80; + rnh->common.channel = nh->channel; + + if (handler) { + rnh->common.length = htons(handler->payload); + rnh->code = htons(NCSI_PKT_RSP_C_COMPLETED); + rnh->reason = htons(NCSI_PKT_RSP_R_NO_ERROR); + + if (handler->handler) { + /* TODO: handle errors */ + handler->handler(rnh); + } + } else { + rnh->common.length = 0; + rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE); + rnh->reason = htons(NCSI_PKT_RSP_R_UNKNOWN); + } + + /* TODO: add a checksum at the end of the frame but the specs + * allows it to be zero */ + + slirp_output(slirp->opaque, ncsi_reply, ETH_HLEN + sizeof(*nh) + + (handler ? handler->payload : 0) + 4); +} diff --git a/slirp/sbuf.h b/slirp/sbuf.h index efcec39a6b..a722ecb629 100644 --- a/slirp/sbuf.h +++ b/slirp/sbuf.h @@ -12,8 +12,8 @@ #define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc) struct sbuf { - u_int sb_cc; /* actual chars in buffer */ - u_int sb_datalen; /* Length of data */ + uint32_t sb_cc; /* actual chars in buffer */ + uint32_t sb_datalen; /* Length of data */ char *sb_wptr; /* write pointer. points to where the next * bytes should be written in the sbuf */ char *sb_rptr; /* read pointer. points to where the next diff --git a/slirp/slirp.c b/slirp/slirp.c index 5a94b06f5e..2f2ec2c1b3 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -870,6 +870,10 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) } break; + case ETH_P_NCSI: + ncsi_input(slirp, pkt, pkt_len); + break; + default: break; } @@ -1129,259 +1133,317 @@ void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, tcp_output(sototcpcb(so)); } -static void slirp_tcp_save(QEMUFile *f, struct tcpcb *tp) +static int slirp_tcp_post_load(void *opaque, int version) { - int i; - - qemu_put_sbe16(f, tp->t_state); - for (i = 0; i < TCPT_NTIMERS; i++) - qemu_put_sbe16(f, tp->t_timer[i]); - qemu_put_sbe16(f, tp->t_rxtshift); - qemu_put_sbe16(f, tp->t_rxtcur); - qemu_put_sbe16(f, tp->t_dupacks); - qemu_put_be16(f, tp->t_maxseg); - qemu_put_sbyte(f, tp->t_force); - qemu_put_be16(f, tp->t_flags); - qemu_put_be32(f, tp->snd_una); - qemu_put_be32(f, tp->snd_nxt); - qemu_put_be32(f, tp->snd_up); - qemu_put_be32(f, tp->snd_wl1); - qemu_put_be32(f, tp->snd_wl2); - qemu_put_be32(f, tp->iss); - qemu_put_be32(f, tp->snd_wnd); - qemu_put_be32(f, tp->rcv_wnd); - qemu_put_be32(f, tp->rcv_nxt); - qemu_put_be32(f, tp->rcv_up); - qemu_put_be32(f, tp->irs); - qemu_put_be32(f, tp->rcv_adv); - qemu_put_be32(f, tp->snd_max); - qemu_put_be32(f, tp->snd_cwnd); - qemu_put_be32(f, tp->snd_ssthresh); - qemu_put_sbe16(f, tp->t_idle); - qemu_put_sbe16(f, tp->t_rtt); - qemu_put_be32(f, tp->t_rtseq); - qemu_put_sbe16(f, tp->t_srtt); - qemu_put_sbe16(f, tp->t_rttvar); - qemu_put_be16(f, tp->t_rttmin); - qemu_put_be32(f, tp->max_sndwnd); - qemu_put_byte(f, tp->t_oobflags); - qemu_put_byte(f, tp->t_iobc); - qemu_put_sbe16(f, tp->t_softerror); - qemu_put_byte(f, tp->snd_scale); - qemu_put_byte(f, tp->rcv_scale); - qemu_put_byte(f, tp->request_r_scale); - qemu_put_byte(f, tp->requested_s_scale); - qemu_put_be32(f, tp->ts_recent); - qemu_put_be32(f, tp->ts_recent_age); - qemu_put_be32(f, tp->last_ack_sent); + tcp_template((struct tcpcb *)opaque); + + return 0; } -static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf) +static const VMStateDescription vmstate_slirp_tcp = { + .name = "slirp-tcp", + .version_id = 0, + .post_load = slirp_tcp_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT16(t_state, struct tcpcb), + VMSTATE_INT16_ARRAY(t_timer, struct tcpcb, TCPT_NTIMERS), + VMSTATE_INT16(t_rxtshift, struct tcpcb), + VMSTATE_INT16(t_rxtcur, struct tcpcb), + VMSTATE_INT16(t_dupacks, struct tcpcb), + VMSTATE_UINT16(t_maxseg, struct tcpcb), + VMSTATE_UINT8(t_force, struct tcpcb), + VMSTATE_UINT16(t_flags, struct tcpcb), + VMSTATE_UINT32(snd_una, struct tcpcb), + VMSTATE_UINT32(snd_nxt, struct tcpcb), + VMSTATE_UINT32(snd_up, struct tcpcb), + VMSTATE_UINT32(snd_wl1, struct tcpcb), + VMSTATE_UINT32(snd_wl2, struct tcpcb), + VMSTATE_UINT32(iss, struct tcpcb), + VMSTATE_UINT32(snd_wnd, struct tcpcb), + VMSTATE_UINT32(rcv_wnd, struct tcpcb), + VMSTATE_UINT32(rcv_nxt, struct tcpcb), + VMSTATE_UINT32(rcv_up, struct tcpcb), + VMSTATE_UINT32(irs, struct tcpcb), + VMSTATE_UINT32(rcv_adv, struct tcpcb), + VMSTATE_UINT32(snd_max, struct tcpcb), + VMSTATE_UINT32(snd_cwnd, struct tcpcb), + VMSTATE_UINT32(snd_ssthresh, struct tcpcb), + VMSTATE_INT16(t_idle, struct tcpcb), + VMSTATE_INT16(t_rtt, struct tcpcb), + VMSTATE_UINT32(t_rtseq, struct tcpcb), + VMSTATE_INT16(t_srtt, struct tcpcb), + VMSTATE_INT16(t_rttvar, struct tcpcb), + VMSTATE_UINT16(t_rttmin, struct tcpcb), + VMSTATE_UINT32(max_sndwnd, struct tcpcb), + VMSTATE_UINT8(t_oobflags, struct tcpcb), + VMSTATE_UINT8(t_iobc, struct tcpcb), + VMSTATE_INT16(t_softerror, struct tcpcb), + VMSTATE_UINT8(snd_scale, struct tcpcb), + VMSTATE_UINT8(rcv_scale, struct tcpcb), + VMSTATE_UINT8(request_r_scale, struct tcpcb), + VMSTATE_UINT8(requested_s_scale, struct tcpcb), + VMSTATE_UINT32(ts_recent, struct tcpcb), + VMSTATE_UINT32(ts_recent_age, struct tcpcb), + VMSTATE_UINT32(last_ack_sent, struct tcpcb), + VMSTATE_END_OF_LIST() + } +}; + +/* The sbuf has a pair of pointers that are migrated as offsets; + * we calculate the offsets and restore the pointers using + * pre_save/post_load on a tmp structure. + */ +struct sbuf_tmp { + struct sbuf *parent; + uint32_t roff, woff; +}; + +static void sbuf_tmp_pre_save(void *opaque) { - uint32_t off; - - qemu_put_be32(f, sbuf->sb_cc); - qemu_put_be32(f, sbuf->sb_datalen); - off = (uint32_t)(sbuf->sb_wptr - sbuf->sb_data); - qemu_put_sbe32(f, off); - off = (uint32_t)(sbuf->sb_rptr - sbuf->sb_data); - qemu_put_sbe32(f, off); - qemu_put_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen); + struct sbuf_tmp *tmp = opaque; + tmp->woff = tmp->parent->sb_wptr - tmp->parent->sb_data; + tmp->roff = tmp->parent->sb_rptr - tmp->parent->sb_data; } -static void slirp_socket_save(QEMUFile *f, struct socket *so) +static int sbuf_tmp_post_load(void *opaque, int version) { - qemu_put_be32(f, so->so_urgc); - qemu_put_be16(f, so->so_ffamily); - switch (so->so_ffamily) { - case AF_INET: - qemu_put_be32(f, so->so_faddr.s_addr); - qemu_put_be16(f, so->so_fport); - break; - default: - error_report("so_ffamily unknown, unable to save so_faddr and" - " so_fport"); + struct sbuf_tmp *tmp = opaque; + uint32_t requested_len = tmp->parent->sb_datalen; + + /* Allocate the buffer space used by the field after the tmp */ + sbreserve(tmp->parent, tmp->parent->sb_datalen); + + if (tmp->parent->sb_datalen != requested_len) { + return -ENOMEM; } - qemu_put_be16(f, so->so_lfamily); - switch (so->so_lfamily) { - case AF_INET: - qemu_put_be32(f, so->so_laddr.s_addr); - qemu_put_be16(f, so->so_lport); - break; - default: - error_report("so_ffamily unknown, unable to save so_laddr and" - " so_lport"); + if (tmp->woff >= requested_len || + tmp->roff >= requested_len) { + error_report("invalid sbuf offsets r/w=%u/%u len=%u", + tmp->roff, tmp->woff, requested_len); + return -EINVAL; } - qemu_put_byte(f, so->so_iptos); - qemu_put_byte(f, so->so_emu); - qemu_put_byte(f, so->so_type); - qemu_put_be32(f, so->so_state); - slirp_sbuf_save(f, &so->so_rcv); - slirp_sbuf_save(f, &so->so_snd); - slirp_tcp_save(f, so->so_tcpcb); -} -static void slirp_bootp_save(QEMUFile *f, Slirp *slirp) -{ - int i; + tmp->parent->sb_wptr = tmp->parent->sb_data + tmp->woff; + tmp->parent->sb_rptr = tmp->parent->sb_data + tmp->roff; - for (i = 0; i < NB_BOOTP_CLIENTS; i++) { - qemu_put_be16(f, slirp->bootp_clients[i].allocated); - qemu_put_buffer(f, slirp->bootp_clients[i].macaddr, 6); - } + return 0; } -static void slirp_state_save(QEMUFile *f, void *opaque) -{ - Slirp *slirp = opaque; - struct ex_list *ex_ptr; - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) - if (ex_ptr->ex_pty == 3) { - struct socket *so; - so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr, - ntohs(ex_ptr->ex_fport)); - if (!so) - continue; - - qemu_put_byte(f, 42); - slirp_socket_save(f, so); - } - qemu_put_byte(f, 0); +static const VMStateDescription vmstate_slirp_sbuf_tmp = { + .name = "slirp-sbuf-tmp", + .post_load = sbuf_tmp_post_load, + .pre_save = sbuf_tmp_pre_save, + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(woff, struct sbuf_tmp), + VMSTATE_UINT32(roff, struct sbuf_tmp), + VMSTATE_END_OF_LIST() + } +}; - qemu_put_be16(f, slirp->ip_id); +static const VMStateDescription vmstate_slirp_sbuf = { + .name = "slirp-sbuf", + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sb_cc, struct sbuf), + VMSTATE_UINT32(sb_datalen, struct sbuf), + VMSTATE_WITH_TMP(struct sbuf, struct sbuf_tmp, vmstate_slirp_sbuf_tmp), + VMSTATE_VBUFFER_UINT32(sb_data, struct sbuf, 0, NULL, sb_datalen), + VMSTATE_END_OF_LIST() + } +}; - slirp_bootp_save(f, slirp); +static bool slirp_older_than_v4(void *opaque, int version_id) +{ + return version_id < 4; } -static void slirp_tcp_load(QEMUFile *f, struct tcpcb *tp) +static bool slirp_family_inet(void *opaque, int version_id) { - int i; - - tp->t_state = qemu_get_sbe16(f); - for (i = 0; i < TCPT_NTIMERS; i++) - tp->t_timer[i] = qemu_get_sbe16(f); - tp->t_rxtshift = qemu_get_sbe16(f); - tp->t_rxtcur = qemu_get_sbe16(f); - tp->t_dupacks = qemu_get_sbe16(f); - tp->t_maxseg = qemu_get_be16(f); - tp->t_force = qemu_get_sbyte(f); - tp->t_flags = qemu_get_be16(f); - tp->snd_una = qemu_get_be32(f); - tp->snd_nxt = qemu_get_be32(f); - tp->snd_up = qemu_get_be32(f); - tp->snd_wl1 = qemu_get_be32(f); - tp->snd_wl2 = qemu_get_be32(f); - tp->iss = qemu_get_be32(f); - tp->snd_wnd = qemu_get_be32(f); - tp->rcv_wnd = qemu_get_be32(f); - tp->rcv_nxt = qemu_get_be32(f); - tp->rcv_up = qemu_get_be32(f); - tp->irs = qemu_get_be32(f); - tp->rcv_adv = qemu_get_be32(f); - tp->snd_max = qemu_get_be32(f); - tp->snd_cwnd = qemu_get_be32(f); - tp->snd_ssthresh = qemu_get_be32(f); - tp->t_idle = qemu_get_sbe16(f); - tp->t_rtt = qemu_get_sbe16(f); - tp->t_rtseq = qemu_get_be32(f); - tp->t_srtt = qemu_get_sbe16(f); - tp->t_rttvar = qemu_get_sbe16(f); - tp->t_rttmin = qemu_get_be16(f); - tp->max_sndwnd = qemu_get_be32(f); - tp->t_oobflags = qemu_get_byte(f); - tp->t_iobc = qemu_get_byte(f); - tp->t_softerror = qemu_get_sbe16(f); - tp->snd_scale = qemu_get_byte(f); - tp->rcv_scale = qemu_get_byte(f); - tp->request_r_scale = qemu_get_byte(f); - tp->requested_s_scale = qemu_get_byte(f); - tp->ts_recent = qemu_get_be32(f); - tp->ts_recent_age = qemu_get_be32(f); - tp->last_ack_sent = qemu_get_be32(f); - tcp_template(tp); + union slirp_sockaddr *ssa = (union slirp_sockaddr *)opaque; + return ssa->ss.ss_family == AF_INET; } -static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf) +static int slirp_socket_pre_load(void *opaque) { - uint32_t off, sb_cc, sb_datalen; + struct socket *so = opaque; + if (tcp_attach(so) < 0) { + return -ENOMEM; + } + /* Older versions don't load these fields */ + so->so_ffamily = AF_INET; + so->so_lfamily = AF_INET; + return 0; +} - sb_cc = qemu_get_be32(f); - sb_datalen = qemu_get_be32(f); +#ifndef _WIN32 +#define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_UINT32_TEST(f, s, t) +#else +/* Win uses u_long rather than uint32_t - but it's still 32bits long */ +#define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_SINGLE_TEST(f, s, t, 0, \ + vmstate_info_uint32, u_long) +#endif - sbreserve(sbuf, sb_datalen); +/* The OS provided ss_family field isn't that portable; it's size + * and type varies (16/8 bit, signed, unsigned) + * and the values it contains aren't fully portable. + */ +typedef struct SS_FamilyTmpStruct { + union slirp_sockaddr *parent; + uint16_t portable_family; +} SS_FamilyTmpStruct; - if (sbuf->sb_datalen != sb_datalen) - return -ENOMEM; +#define SS_FAMILY_MIG_IPV4 2 /* Linux, BSD, Win... */ +#define SS_FAMILY_MIG_IPV6 10 /* Linux */ +#define SS_FAMILY_MIG_OTHER 0xffff - sbuf->sb_cc = sb_cc; +static void ss_family_pre_save(void *opaque) +{ + SS_FamilyTmpStruct *tss = opaque; - off = qemu_get_sbe32(f); - sbuf->sb_wptr = sbuf->sb_data + off; - off = qemu_get_sbe32(f); - sbuf->sb_rptr = sbuf->sb_data + off; - qemu_get_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen); + tss->portable_family = SS_FAMILY_MIG_OTHER; - return 0; + if (tss->parent->ss.ss_family == AF_INET) { + tss->portable_family = SS_FAMILY_MIG_IPV4; + } else if (tss->parent->ss.ss_family == AF_INET6) { + tss->portable_family = SS_FAMILY_MIG_IPV6; + } } -static int slirp_socket_load(QEMUFile *f, struct socket *so, int version_id) +static int ss_family_post_load(void *opaque, int version_id) { - if (tcp_attach(so) < 0) - return -ENOMEM; + SS_FamilyTmpStruct *tss = opaque; - so->so_urgc = qemu_get_be32(f); - if (version_id <= 3) { - so->so_ffamily = AF_INET; - so->so_faddr.s_addr = qemu_get_be32(f); - so->so_laddr.s_addr = qemu_get_be32(f); - so->so_fport = qemu_get_be16(f); - so->so_lport = qemu_get_be16(f); - } else { - so->so_ffamily = qemu_get_be16(f); - switch (so->so_ffamily) { - case AF_INET: - so->so_faddr.s_addr = qemu_get_be32(f); - so->so_fport = qemu_get_be16(f); - break; - default: - error_report( - "so_ffamily unknown, unable to restore so_faddr and so_lport"); - } - so->so_lfamily = qemu_get_be16(f); - switch (so->so_lfamily) { - case AF_INET: - so->so_laddr.s_addr = qemu_get_be32(f); - so->so_lport = qemu_get_be16(f); - break; - default: - error_report( - "so_ffamily unknown, unable to restore so_laddr and so_lport"); - } + switch (tss->portable_family) { + case SS_FAMILY_MIG_IPV4: + tss->parent->ss.ss_family = AF_INET; + break; + case SS_FAMILY_MIG_IPV6: + case 23: /* compatibility: AF_INET6 from mingw */ + case 28: /* compatibility: AF_INET6 from FreeBSD sys/socket.h */ + tss->parent->ss.ss_family = AF_INET6; + break; + default: + error_report("invalid ss_family type %x", tss->portable_family); + return -EINVAL; } - so->so_iptos = qemu_get_byte(f); - so->so_emu = qemu_get_byte(f); - so->so_type = qemu_get_byte(f); - so->so_state = qemu_get_be32(f); - if (slirp_sbuf_load(f, &so->so_rcv) < 0) - return -ENOMEM; - if (slirp_sbuf_load(f, &so->so_snd) < 0) - return -ENOMEM; - slirp_tcp_load(f, so->so_tcpcb); return 0; } -static void slirp_bootp_load(QEMUFile *f, Slirp *slirp) -{ - int i; +static const VMStateDescription vmstate_slirp_ss_family = { + .name = "slirp-socket-addr/ss_family", + .pre_save = ss_family_pre_save, + .post_load = ss_family_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(portable_family, SS_FamilyTmpStruct), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slirp_socket_addr = { + .name = "slirp-socket-addr", + .version_id = 4, + .fields = (VMStateField[]) { + VMSTATE_WITH_TMP(union slirp_sockaddr, SS_FamilyTmpStruct, + vmstate_slirp_ss_family), + VMSTATE_SIN4_ADDR(sin.sin_addr.s_addr, union slirp_sockaddr, + slirp_family_inet), + VMSTATE_UINT16_TEST(sin.sin_port, union slirp_sockaddr, + slirp_family_inet), + +#if 0 + /* Untested: Needs checking by someone with IPv6 test */ + VMSTATE_BUFFER_TEST(sin6.sin6_addr, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT16_TEST(sin6.sin6_port, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT32_TEST(sin6.sin6_flowinfo, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT32_TEST(sin6.sin6_scope_id, union slirp_sockaddr, + slirp_family_inet6), +#endif + + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slirp_socket = { + .name = "slirp-socket", + .version_id = 4, + .pre_load = slirp_socket_pre_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(so_urgc, struct socket), + /* Pre-v4 versions */ + VMSTATE_SIN4_ADDR(so_faddr.s_addr, struct socket, + slirp_older_than_v4), + VMSTATE_SIN4_ADDR(so_laddr.s_addr, struct socket, + slirp_older_than_v4), + VMSTATE_UINT16_TEST(so_fport, struct socket, slirp_older_than_v4), + VMSTATE_UINT16_TEST(so_lport, struct socket, slirp_older_than_v4), + /* v4 and newer */ + VMSTATE_STRUCT(fhost, struct socket, 4, vmstate_slirp_socket_addr, + union slirp_sockaddr), + VMSTATE_STRUCT(lhost, struct socket, 4, vmstate_slirp_socket_addr, + union slirp_sockaddr), + + VMSTATE_UINT8(so_iptos, struct socket), + VMSTATE_UINT8(so_emu, struct socket), + VMSTATE_UINT8(so_type, struct socket), + VMSTATE_INT32(so_state, struct socket), + VMSTATE_STRUCT(so_rcv, struct socket, 0, vmstate_slirp_sbuf, + struct sbuf), + VMSTATE_STRUCT(so_snd, struct socket, 0, vmstate_slirp_sbuf, + struct sbuf), + VMSTATE_STRUCT_POINTER(so_tcpcb, struct socket, vmstate_slirp_tcp, + struct tcpcb), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slirp_bootp_client = { + .name = "slirp_bootpclient", + .fields = (VMStateField[]) { + VMSTATE_UINT16(allocated, BOOTPClient), + VMSTATE_BUFFER(macaddr, BOOTPClient), + VMSTATE_END_OF_LIST() + } +}; - for (i = 0; i < NB_BOOTP_CLIENTS; i++) { - slirp->bootp_clients[i].allocated = qemu_get_be16(f); - qemu_get_buffer(f, slirp->bootp_clients[i].macaddr, 6); +static const VMStateDescription vmstate_slirp = { + .name = "slirp", + .version_id = 4, + .fields = (VMStateField[]) { + VMSTATE_UINT16_V(ip_id, Slirp, 2), + VMSTATE_STRUCT_ARRAY(bootp_clients, Slirp, NB_BOOTP_CLIENTS, 3, + vmstate_slirp_bootp_client, BOOTPClient), + VMSTATE_END_OF_LIST() } +}; + +static void slirp_state_save(QEMUFile *f, void *opaque) +{ + Slirp *slirp = opaque; + struct ex_list *ex_ptr; + + for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) + if (ex_ptr->ex_pty == 3) { + struct socket *so; + so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr, + ntohs(ex_ptr->ex_fport)); + if (!so) + continue; + + qemu_put_byte(f, 42); + vmstate_save_state(f, &vmstate_slirp_socket, so, NULL); + } + qemu_put_byte(f, 0); + + vmstate_save_state(f, &vmstate_slirp, slirp, NULL); } + static int slirp_state_load(QEMUFile *f, void *opaque, int version_id) { Slirp *slirp = opaque; @@ -1394,7 +1456,7 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id) if (!so) return -ENOMEM; - ret = slirp_socket_load(f, so, version_id); + ret = vmstate_load_state(f, &vmstate_slirp_socket, so, version_id); if (ret < 0) return ret; @@ -1416,13 +1478,5 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id) so->extra = (void *)ex_ptr->ex_exec; } - if (version_id >= 2) { - slirp->ip_id = qemu_get_be16(f); - } - - if (version_id >= 3) { - slirp_bootp_load(f, slirp); - } - - return 0; + return vmstate_load_state(f, &vmstate_slirp, slirp, version_id); } diff --git a/slirp/slirp.h b/slirp/slirp.h index 3877f667f0..5af4f482b5 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -231,6 +231,9 @@ extern Slirp *slirp_instance; void if_start(Slirp *); +/* ncsi.c */ +void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); + #ifndef _WIN32 #include <netdb.h> #endif diff --git a/slirp/socket.h b/slirp/socket.h index 8feed2aea4..2f224bc34f 100644 --- a/slirp/socket.h +++ b/slirp/socket.h @@ -15,6 +15,12 @@ * Our socket structure */ +union slirp_sockaddr { + struct sockaddr_storage ss; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; + struct socket { struct socket *so_next,*so_prev; /* For a linked list of sockets */ @@ -30,23 +36,15 @@ struct socket { * PING reply's */ struct tcpiphdr *so_ti; /* Pointer to the original ti within * so_mconn, for non-blocking connections */ - int so_urgc; - union { /* foreign host */ - struct sockaddr_storage ss; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - } fhost; + uint32_t so_urgc; + union slirp_sockaddr fhost; /* Foreign host */ #define so_faddr fhost.sin.sin_addr #define so_fport fhost.sin.sin_port #define so_faddr6 fhost.sin6.sin6_addr #define so_fport6 fhost.sin6.sin6_port #define so_ffamily fhost.ss.ss_family - union { /* local host */ - struct sockaddr_storage ss; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - } lhost; + union slirp_sockaddr lhost; /* Local host */ #define so_laddr lhost.sin.sin_addr #define so_lport lhost.sin.sin_port #define so_laddr6 lhost.sin6.sin6_addr @@ -56,8 +54,8 @@ struct socket { uint8_t so_iptos; /* Type of service */ uint8_t so_emu; /* Is the socket emulated? */ - u_char so_type; /* Type of socket, UDP or TCP */ - int so_state; /* internal state flags SS_*, below */ + uint8_t so_type; /* Type of socket, UDP or TCP */ + int32_t so_state; /* internal state flags SS_*, below */ struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */ u_int so_expire; /* When the socket will expire */ diff --git a/slirp/tcp_var.h b/slirp/tcp_var.h index 0f8f187c5c..895ef6df1e 100644 --- a/slirp/tcp_var.h +++ b/slirp/tcp_var.h @@ -48,7 +48,7 @@ struct tcpcb { short t_rxtcur; /* current retransmit value */ short t_dupacks; /* consecutive dup acks recd */ u_short t_maxseg; /* maximum segment size */ - char t_force; /* 1 if forcing out a byte */ + uint8_t t_force; /* 1 if forcing out a byte */ u_short t_flags; #define TF_ACKNOW 0x0001 /* ack peer immediately */ #define TF_DELACK 0x0002 /* ack, but try to delay it */ @@ -109,8 +109,8 @@ struct tcpcb { uint32_t max_sndwnd; /* largest window peer has offered */ /* out-of-band data */ - char t_oobflags; /* have some */ - char t_iobc; /* input character */ + uint8_t t_oobflags; /* have some */ + uint8_t t_iobc; /* input character */ #define TCPOOB_HAVEDATA 0x01 #define TCPOOB_HADDATA 0x02 short t_softerror; /* possible error not yet reported */ diff --git a/slirp/tftp.c b/slirp/tftp.c index 50e714807d..a9bc4bb1b6 100644 --- a/slirp/tftp.c +++ b/slirp/tftp.c @@ -70,7 +70,7 @@ static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas, found: memset(spt, 0, sizeof(*spt)); - spt->client_addr = *srcsas; + memcpy(&spt->client_addr, srcsas, sockaddr_size(srcsas)); spt->fd = -1; spt->block_size = 512; spt->client_port = tp->udp.uh_sport; diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 224f04ba69..f5b47bfd74 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -37,3 +37,5 @@ stub-obj-y += target-monitor-defs.o stub-obj-y += target-get-monitor-def.o stub-obj-y += pc_madt_cpu_entry.o stub-obj-y += vmgenid.o +stub-obj-y += xen-common.o +stub-obj-y += xen-hvm.o diff --git a/xen-common-stub.c b/stubs/xen-common.c index 09fce2dd36..09fce2dd36 100644 --- a/xen-common-stub.c +++ b/stubs/xen-common.c diff --git a/xen-hvm-stub.c b/stubs/xen-hvm.c index c5003251cb..3ca6c51b21 100644 --- a/xen-hvm-stub.c +++ b/stubs/xen-hvm.c @@ -50,7 +50,7 @@ void xen_register_framebuffer(MemoryRegion *mr) { } -void xen_modified_memory(ram_addr_t start, ram_addr_t length) +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) { } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 04b062cb7e..b357aee778 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -304,33 +304,6 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) -static void arm_v7m_unassigned_access(CPUState *cpu, hwaddr addr, - bool is_write, bool is_exec, int opaque, - unsigned size) -{ - ARMCPU *arm = ARM_CPU(cpu); - CPUARMState *env = &arm->env; - - /* ARMv7-M interrupt return works by loading a magic value into the PC. - * On real hardware the load causes the return to occur. The qemu - * implementation performs the jump normally, then does the exception - * return by throwing a special exception when when the CPU tries to - * execute code at the magic address. - */ - if (env->v7m.exception != 0 && addr >= 0xfffffff0 && is_exec) { - cpu->exception_index = EXCP_EXCEPTION_EXIT; - cpu_loop_exit(cpu); - } - - /* In real hardware an attempt to access parts of the address space - * with nothing there will usually cause an external abort. - * However our QEMU board models are often missing device models where - * the guest can boot anyway with the default read-as-zero/writes-ignored - * behaviour that you get without a QEMU unassigned_access hook. - * So just return here to retain that default behaviour. - */ -} - static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { CPUClass *cc = CPU_GET_CLASS(cs); @@ -338,17 +311,7 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) CPUARMState *env = &cpu->env; bool ret = false; - /* ARMv7-M interrupt return works by loading a magic value - * into the PC. On real hardware the load causes the - * return to occur. The qemu implementation performs the - * jump normally, then does the exception return when the - * CPU tries to execute code at the magic address. - * This will cause the magic PC value to be pushed to - * the stack if an interrupt occurred at the wrong time. - * We avoid this by disabling interrupts when - * pc contains a magic address. - * - * ARMv7-M interrupt masking works differently than -A or -R. + /* ARMv7-M interrupt masking works differently than -A or -R. * There is no FIQ/IRQ distinction. Instead of I and F bits * masking FIQ and IRQ interrupts, an exception is taken only * if it is higher priority than the current execution priority @@ -356,8 +319,7 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) * currently active exception). */ if (interrupt_request & CPU_INTERRUPT_HARD - && (armv7m_nvic_can_take_pending_exception(env->nvic)) - && (env->regs[15] < 0xfffffff0)) { + && (armv7m_nvic_can_take_pending_exception(env->nvic))) { cs->exception_index = EXCP_IRQ; cc->do_interrupt(cs); ret = true; @@ -1091,7 +1053,6 @@ static void arm_v7m_class_init(ObjectClass *oc, void *data) cc->do_interrupt = arm_v7m_cpu_do_interrupt; #endif - cc->do_unassigned_access = arm_v7m_unassigned_access; cc->cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a8aabce7dd..1055bfef3d 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -58,6 +58,7 @@ #define EXCP_SEMIHOST 16 /* semihosting call */ #define EXCP_NOCP 17 /* v7M NOCP UsageFault */ #define EXCP_INVSTATE 18 /* v7M INVSTATE UsageFault */ +/* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 #define ARMV7M_EXCP_NMI 2 @@ -2290,6 +2291,9 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) #define ARM_TBFLAG_NS_MASK (1 << ARM_TBFLAG_NS_SHIFT) #define ARM_TBFLAG_BE_DATA_SHIFT 20 #define ARM_TBFLAG_BE_DATA_MASK (1 << ARM_TBFLAG_BE_DATA_SHIFT) +/* For M profile only, Handler (ie not Thread) mode */ +#define ARM_TBFLAG_HANDLER_SHIFT 21 +#define ARM_TBFLAG_HANDLER_MASK (1 << ARM_TBFLAG_HANDLER_SHIFT) /* Bit usage when in AArch64 state */ #define ARM_TBFLAG_TBI0_SHIFT 0 /* TBI0 for EL0/1 or TBI for EL2/3 */ @@ -2326,6 +2330,8 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) (((F) & ARM_TBFLAG_NS_MASK) >> ARM_TBFLAG_NS_SHIFT) #define ARM_TBFLAG_BE_DATA(F) \ (((F) & ARM_TBFLAG_BE_DATA_MASK) >> ARM_TBFLAG_BE_DATA_SHIFT) +#define ARM_TBFLAG_HANDLER(F) \ + (((F) & ARM_TBFLAG_HANDLER_MASK) >> ARM_TBFLAG_HANDLER_SHIFT) #define ARM_TBFLAG_TBI0(F) \ (((F) & ARM_TBFLAG_TBI0_MASK) >> ARM_TBFLAG_TBI0_SHIFT) #define ARM_TBFLAG_TBI1(F) \ @@ -2516,6 +2522,10 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, } *flags |= fp_exception_el(env) << ARM_TBFLAG_FPEXC_EL_SHIFT; + if (env->v7m.exception != 0) { + *flags |= ARM_TBFLAG_HANDLER_MASK; + } + *cs_base = 0; } diff --git a/target/arm/helper.c b/target/arm/helper.c index 8cb7a9451c..8a3e4480aa 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6271,6 +6271,25 @@ static void arm_log_exception(int idx) { if (qemu_loglevel_mask(CPU_LOG_INT)) { const char *exc = NULL; + static const char * const excnames[] = { + [EXCP_UDEF] = "Undefined Instruction", + [EXCP_SWI] = "SVC", + [EXCP_PREFETCH_ABORT] = "Prefetch Abort", + [EXCP_DATA_ABORT] = "Data Abort", + [EXCP_IRQ] = "IRQ", + [EXCP_FIQ] = "FIQ", + [EXCP_BKPT] = "Breakpoint", + [EXCP_EXCEPTION_EXIT] = "QEMU v7M exception exit", + [EXCP_KERNEL_TRAP] = "QEMU intercept of kernel commpage", + [EXCP_HVC] = "Hypervisor Call", + [EXCP_HYP_TRAP] = "Hypervisor Trap", + [EXCP_SMC] = "Secure Monitor Call", + [EXCP_VIRQ] = "Virtual IRQ", + [EXCP_VFIQ] = "Virtual FIQ", + [EXCP_SEMIHOST] = "Semihosting call", + [EXCP_NOCP] = "v7M NOCP UsageFault", + [EXCP_INVSTATE] = "v7M INVSTATE UsageFault", + }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { exc = excnames[idx]; diff --git a/target/arm/internals.h b/target/arm/internals.h index f742a419ff..1f6efef7c4 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -51,27 +51,6 @@ static inline bool excp_is_internal(int excp) || excp == EXCP_SEMIHOST; } -/* Exception names for debug logging; note that not all of these - * precisely correspond to architectural exceptions. - */ -static const char * const excnames[] = { - [EXCP_UDEF] = "Undefined Instruction", - [EXCP_SWI] = "SVC", - [EXCP_PREFETCH_ABORT] = "Prefetch Abort", - [EXCP_DATA_ABORT] = "Data Abort", - [EXCP_IRQ] = "IRQ", - [EXCP_FIQ] = "FIQ", - [EXCP_BKPT] = "Breakpoint", - [EXCP_EXCEPTION_EXIT] = "QEMU v7M exception exit", - [EXCP_KERNEL_TRAP] = "QEMU intercept of kernel commpage", - [EXCP_HVC] = "Hypervisor Call", - [EXCP_HYP_TRAP] = "Hypervisor Trap", - [EXCP_SMC] = "Secure Monitor Call", - [EXCP_VIRQ] = "Virtual IRQ", - [EXCP_VFIQ] = "Virtual FIQ", - [EXCP_SEMIHOST] = "Semihosting call", -}; - /* Scale factor for generic timers, ie number of ns per tick. * This gives a 62.5MHz timer. */ diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 61111091ad..a16abc8d12 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -940,7 +940,7 @@ bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit) * single step at this point so something has gone wrong. */ error_report("%s: guest single-step while debugging unsupported" - " (%"PRIx64", %"PRIx32")\n", + " (%"PRIx64", %"PRIx32")", __func__, env->pc, debug_exit->hsr); return false; } @@ -965,7 +965,7 @@ bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit) break; } default: - error_report("%s: unhandled debug exit (%"PRIx32", %"PRIx64")\n", + error_report("%s: unhandled debug exit (%"PRIx32", %"PRIx64")", __func__, debug_exit->hsr, env->pc); } diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index d64c8670fa..156b825040 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -130,7 +130,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, if (unlikely(ret)) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - uint32_t syn, exc; + uint32_t syn, exc, fsc; unsigned int target_el; bool same_el; @@ -145,19 +145,32 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, env->cp15.hpfar_el2 = extract64(fi.s2addr, 12, 47) << 4; } same_el = arm_current_el(env) == target_el; - /* AArch64 syndrome does not have an LPAE bit */ - syn = fsr & ~(1 << 9); + + if (fsr & (1 << 9)) { + /* LPAE format fault status register : bottom 6 bits are + * status code in the same form as needed for syndrome + */ + fsc = extract32(fsr, 0, 6); + } else { + /* Short format FSR : this fault will never actually be reported + * to an EL that uses a syndrome register. Check that here, + * and use a (currently) reserved FSR code in case the constructed + * syndrome does leak into the guest somehow. + */ + assert(target_el != 2 && !arm_el_is_aa64(env, target_el)); + fsc = 0x3f; + } /* For insn and data aborts we assume there is no instruction syndrome * information; this is always true for exceptions reported to EL1. */ if (access_type == MMU_INST_FETCH) { - syn = syn_insn_abort(same_el, 0, fi.s1ptw, syn); + syn = syn_insn_abort(same_el, 0, fi.s1ptw, fsc); exc = EXCP_PREFETCH_ABORT; } else { syn = merge_syn_data_abort(env->exception.syndrome, target_el, same_el, fi.s1ptw, - access_type == MMU_DATA_STORE, syn); + access_type == MMU_DATA_STORE, fsc); if (access_type == MMU_DATA_STORE && arm_feature(env, ARM_FEATURE_V6)) { fsr |= (1 << 11); diff --git a/target/arm/translate.c b/target/arm/translate.c index e32e38cadd..0b5a0bca06 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -296,6 +296,30 @@ static void gen_step_complete_exception(DisasContext *s) s->is_jmp = DISAS_EXC; } +static void gen_singlestep_exception(DisasContext *s) +{ + /* Generate the right kind of exception for singlestep, which is + * either the architectural singlestep or EXCP_DEBUG for QEMU's + * gdb singlestepping. + */ + if (s->ss_active) { + gen_step_complete_exception(s); + } else { + gen_exception_internal(EXCP_DEBUG); + } +} + +static inline bool is_singlestepping(DisasContext *s) +{ + /* Return true if we are singlestepping either because of + * architectural singlestep or QEMU gdbstub singlestep. This does + * not include the command line '-singlestep' mode which is rather + * misnamed as it only means "one instruction per TB" and doesn't + * affect the code we generate. + */ + return s->singlestep_enabled || s->ss_active; +} + static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b) { TCGv_i32 tmp1 = tcg_temp_new_i32(); @@ -880,6 +904,21 @@ static const uint8_t table_logic_cc[16] = { 1, /* mvn */ }; +static inline void gen_set_condexec(DisasContext *s) +{ + if (s->condexec_mask) { + uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_movi_i32(tmp, val); + store_cpu_field(tmp, condexec_bits); + } +} + +static inline void gen_set_pc_im(DisasContext *s, target_ulong val) +{ + tcg_gen_movi_i32(cpu_R[15], val); +} + /* Set PC and Thumb state from an immediate address. */ static inline void gen_bx_im(DisasContext *s, uint32_t addr) { @@ -904,6 +943,51 @@ static inline void gen_bx(DisasContext *s, TCGv_i32 var) store_cpu_field(var, thumb); } +/* Set PC and Thumb state from var. var is marked as dead. + * For M-profile CPUs, include logic to detect exception-return + * branches and handle them. This is needed for Thumb POP/LDM to PC, LDR to PC, + * and BX reg, and no others, and happens only for code in Handler mode. + */ +static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var) +{ + /* Generate the same code here as for a simple bx, but flag via + * s->is_jmp that we need to do the rest of the work later. + */ + gen_bx(s, var); + if (s->v7m_handler_mode && arm_dc_feature(s, ARM_FEATURE_M)) { + s->is_jmp = DISAS_BX_EXCRET; + } +} + +static inline void gen_bx_excret_final_code(DisasContext *s) +{ + /* Generate the code to finish possible exception return and end the TB */ + TCGLabel *excret_label = gen_new_label(); + + /* Is the new PC value in the magic range indicating exception return? */ + tcg_gen_brcondi_i32(TCG_COND_GEU, cpu_R[15], 0xff000000, excret_label); + /* No: end the TB as we would for a DISAS_JMP */ + if (is_singlestepping(s)) { + gen_singlestep_exception(s); + } else { + tcg_gen_exit_tb(0); + } + gen_set_label(excret_label); + /* Yes: this is an exception return. + * At this point in runtime env->regs[15] and env->thumb will hold + * the exception-return magic number, which do_v7m_exception_exit() + * will read. Nothing else will be able to see those values because + * the cpu-exec main loop guarantees that we will always go straight + * from raising the exception to the exception-handling code. + * + * gen_ss_advance(s) does nothing on M profile currently but + * calling it is conceptually the right thing as we have executed + * this instruction (compare SWI, HVC, SMC handling). + */ + gen_ss_advance(s); + gen_exception_internal(EXCP_EXCEPTION_EXIT); +} + /* Variant of store_reg which uses branch&exchange logic when storing to r15 in ARM architecture v7 and above. The source must be a temporary and will be marked as dead. */ @@ -923,7 +1007,7 @@ static inline void store_reg_bx(DisasContext *s, int reg, TCGv_i32 var) static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) { if (reg == 15 && ENABLE_ARCH_5) { - gen_bx(s, var); + gen_bx_excret(s, var); } else { store_reg(s, reg, var); } @@ -1056,11 +1140,6 @@ DO_GEN_ST(8, MO_UB) DO_GEN_ST(16, MO_UW) DO_GEN_ST(32, MO_UL) -static inline void gen_set_pc_im(DisasContext *s, target_ulong val) -{ - tcg_gen_movi_i32(cpu_R[15], val); -} - static inline void gen_hvc(DisasContext *s, int imm16) { /* The pre HVC helper handles cases when HVC gets trapped @@ -1094,17 +1173,6 @@ static inline void gen_smc(DisasContext *s) s->is_jmp = DISAS_SMC; } -static inline void -gen_set_condexec (DisasContext *s) -{ - if (s->condexec_mask) { - uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, val); - store_cpu_field(tmp, condexec_bits); - } -} - static void gen_exception_internal_insn(DisasContext *s, int offset, int excp) { gen_set_condexec(s); @@ -4092,7 +4160,7 @@ static inline void gen_goto_tb(DisasContext *s, int n, target_ulong dest) static inline void gen_jmp (DisasContext *s, uint32_t dest) { - if (unlikely(s->singlestep_enabled || s->ss_active)) { + if (unlikely(is_singlestepping(s))) { /* An indirect jump so that we still trigger the debug exception. */ if (s->thumb) dest |= 1; @@ -9858,7 +9926,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); if (i == 15) { - gen_bx(s, tmp); + gen_bx_excret(s, tmp); } else if (i == rn) { loaded_var = tmp; loaded_base = 1; @@ -9959,7 +10027,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw gen_arm_shift_reg(tmp, op, tmp2, logic_cc); if (logic_cc) gen_logic_CC(tmp); - store_reg_bx(s, rd, tmp); + store_reg(s, rd, tmp); break; case 1: /* Sign/zero extend. */ op = (insn >> 20) & 7; @@ -10485,7 +10553,12 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } break; case 4: /* bxj */ - /* Trivial implementation equivalent to bx. */ + /* Trivial implementation equivalent to bx. + * This instruction doesn't exist at all for M-profile. + */ + if (arm_dc_feature(s, ARM_FEATURE_M)) { + goto illegal_op; + } tmp = load_reg(s, rn); gen_bx(s, tmp); break; @@ -10885,7 +10958,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw goto illegal_op; } if (rs == 15) { - gen_bx(s, tmp); + gen_bx_excret(s, tmp); } else { store_reg(s, rs, tmp); } @@ -11075,9 +11148,11 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, val); store_reg(s, 14, tmp2); + gen_bx(s, tmp); + } else { + /* Only BX works as exception-return, not BLX */ + gen_bx_excret(s, tmp); } - /* already thumb, no need to check */ - gen_bx(s, tmp); break; } break; @@ -11752,6 +11827,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); dc->c15_cpar = ARM_TBFLAG_XSCALE_CPAR(tb->flags); + dc->v7m_handler_mode = ARM_TBFLAG_HANDLER(tb->flags); dc->cp_regs = cpu->cp_regs; dc->features = env->features; @@ -11851,14 +11927,6 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) dc->is_jmp = DISAS_EXC; break; } -#else - if (arm_dc_feature(dc, ARM_FEATURE_M)) { - /* Branches to the magic exception-return addresses should - * already have been caught via the arm_v7m_unassigned_access hook, - * and never get here. - */ - assert(dc->pc < 0xfffffff0); - } #endif if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { @@ -11953,9 +12021,8 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) ((dc->pc >= next_page_start - 3) && insn_crosses_page(env, dc)); } while (!dc->is_jmp && !tcg_op_buf_full() && - !cs->singlestep_enabled && + !is_singlestepping(dc) && !singlestep && - !dc->ss_active && !end_of_page && num_insns < max_insns); @@ -11971,9 +12038,16 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) /* At this stage dc->condjmp will only be set when the skipped instruction was a conditional branch or trap, and the PC has already been written. */ - if (unlikely(cs->singlestep_enabled || dc->ss_active)) { + gen_set_condexec(dc); + if (dc->is_jmp == DISAS_BX_EXCRET) { + /* Exception return branches need some special case code at the + * end of the TB, which is complex enough that it has to + * handle the single-step vs not and the condition-failed + * insn codepath itself. + */ + gen_bx_excret_final_code(dc); + } else if (unlikely(is_singlestepping(dc))) { /* Unconditional and "condition passed" instruction codepath. */ - gen_set_condexec(dc); switch (dc->is_jmp) { case DISAS_SWI: gen_ss_advance(dc); @@ -11993,24 +12067,8 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) gen_set_pc_im(dc, dc->pc); /* fall through */ default: - if (dc->ss_active) { - gen_step_complete_exception(dc); - } else { - /* FIXME: Single stepping a WFI insn will not halt - the CPU. */ - gen_exception_internal(EXCP_DEBUG); - } - } - if (dc->condjmp) { - /* "Condition failed" instruction codepath. */ - gen_set_label(dc->condlabel); - gen_set_condexec(dc); - gen_set_pc_im(dc, dc->pc); - if (dc->ss_active) { - gen_step_complete_exception(dc); - } else { - gen_exception_internal(EXCP_DEBUG); - } + /* FIXME: Single stepping a WFI insn will not halt the CPU. */ + gen_singlestep_exception(dc); } } else { /* While branches must always occur at the end of an IT block, @@ -12021,7 +12079,6 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) - Hardware watchpoints. Hardware breakpoints have already been handled and skip this code. */ - gen_set_condexec(dc); switch(dc->is_jmp) { case DISAS_NEXT: gen_goto_tb(dc, 1, dc->pc); @@ -12061,11 +12118,17 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) gen_exception(EXCP_SMC, syn_aa32_smc(), 3); break; } - if (dc->condjmp) { - gen_set_label(dc->condlabel); - gen_set_condexec(dc); + } + + if (dc->condjmp) { + /* "Condition failed" instruction codepath for the branch/trap insn */ + gen_set_label(dc->condlabel); + gen_set_condexec(dc); + if (unlikely(is_singlestepping(dc))) { + gen_set_pc_im(dc, dc->pc); + gen_singlestep_exception(dc); + } else { gen_goto_tb(dc, 1, dc->pc); - dc->condjmp = 0; } } diff --git a/target/arm/translate.h b/target/arm/translate.h index abb0760158..629dab945e 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -31,6 +31,7 @@ typedef struct DisasContext { bool vfp_enabled; /* FP enabled via FPSCR.EN */ int vec_len; int vec_stride; + bool v7m_handler_mode; /* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI * so that top level loop can generate correct syndrome information. */ @@ -134,6 +135,10 @@ static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) #define DISAS_HVC 8 #define DISAS_SMC 9 #define DISAS_YIELD 10 +/* M profile branch which might be an exception return (and so needs + * custom end-of-TB code) + */ +#define DISAS_BX_EXCRET 11 #ifdef TARGET_AARCH64 void a64_translate_init(void); diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index 28d9cc7d79..8e9397aa58 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -50,7 +50,7 @@ struct PPCUserRegStruct { struct PPCElfPrstatus { char pad1[112]; struct PPCUserRegStruct pr_reg; - reg_t pad2[4]; + char pad2[40]; } QEMU_PACKED; diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 81500e5748..d0cf6ca2a9 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -197,6 +197,7 @@ typedef struct PowerPCCPUClass { int bfd_mach; uint32_t l1_dcache_size, l1_icache_size; const struct ppc_segment_page_sizes *sps; + struct ppc_radix_page_info *radix_page_info; void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 5ee33b3fd3..e0ff0412d6 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -943,6 +943,10 @@ struct ppc_segment_page_sizes { struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ]; }; +struct ppc_radix_page_info { + uint32_t count; + uint32_t entries[PPC_PAGE_SIZES_MAX_SZ]; +}; /*****************************************************************************/ /* The whole PowerPC CPU context */ @@ -1196,6 +1200,7 @@ struct PowerPCCPU { uint32_t max_compat; uint32_t compat_pvr; PPCVirtualHypervisor *vhyp; + Object *intc; /* Fields related to migration compatibility hacks */ bool pre_2_8_migration; diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 6d77661f7c..bb6a94a8b3 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -709,6 +709,7 @@ DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env) #endif DEF_HELPER_2(store_sdr1, void, env, tl) +DEF_HELPER_2(store_pidr, void, env, tl) DEF_HELPER_FLAGS_2(store_tbl, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_tbu, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 9f1f132cef..8574c369e6 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -49,6 +49,8 @@ #if defined(TARGET_PPC64) #include "hw/ppc/spapr_cpu_core.h" #endif +#include "elf.h" +#include "sysemu/kvm_int.h" //#define DEBUG_KVM @@ -73,6 +75,7 @@ static int cap_booke_sregs; static int cap_ppc_smt; static int cap_ppc_rma; static int cap_spapr_tce; +static int cap_spapr_tce_64; static int cap_spapr_multitce; static int cap_spapr_vfio; static int cap_hior; @@ -83,6 +86,8 @@ static int cap_papr; static int cap_htab_fd; static int cap_fixup_hcalls; static int cap_htm; /* Hardware transactional memory support */ +static int cap_mmu_radix; +static int cap_mmu_hash_v3; static uint32_t debug_inst_opcode; @@ -125,6 +130,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT); cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA); cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE); + cap_spapr_tce_64 = kvm_check_extension(s, KVM_CAP_SPAPR_TCE_64); cap_spapr_multitce = kvm_check_extension(s, KVM_CAP_SPAPR_MULTITCE); cap_spapr_vfio = false; cap_one_reg = kvm_check_extension(s, KVM_CAP_ONE_REG); @@ -136,6 +142,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_htab_fd = kvm_check_extension(s, KVM_CAP_PPC_HTAB_FD); cap_fixup_hcalls = kvm_check_extension(s, KVM_CAP_PPC_FIXUP_HCALL); cap_htm = kvm_vm_check_extension(s, KVM_CAP_PPC_HTM); + cap_mmu_radix = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_RADIX); + cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3); if (!cap_interrupt_level) { fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " @@ -330,6 +338,61 @@ static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) kvm_get_fallback_smmu_info(cpu, info); } +struct ppc_radix_page_info *kvm_get_radix_page_info(void) +{ + KVMState *s = KVM_STATE(current_machine->accelerator); + struct ppc_radix_page_info *radix_page_info; + struct kvm_ppc_rmmu_info rmmu_info; + int i; + + if (!kvm_check_extension(s, KVM_CAP_PPC_MMU_RADIX)) { + return NULL; + } + if (kvm_vm_ioctl(s, KVM_PPC_GET_RMMU_INFO, &rmmu_info)) { + return NULL; + } + radix_page_info = g_malloc0(sizeof(*radix_page_info)); + radix_page_info->count = 0; + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + if (rmmu_info.ap_encodings[i]) { + radix_page_info->entries[i] = rmmu_info.ap_encodings[i]; + radix_page_info->count++; + } + } + return radix_page_info; +} + +target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, + bool radix, bool gtse, + uint64_t proc_tbl) +{ + CPUState *cs = CPU(cpu); + int ret; + uint64_t flags = 0; + struct kvm_ppc_mmuv3_cfg cfg = { + .process_table = proc_tbl, + }; + + if (radix) { + flags |= KVM_PPC_MMUV3_RADIX; + } + if (gtse) { + flags |= KVM_PPC_MMUV3_GTSE; + } + cfg.flags = flags; + ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_CONFIGURE_V3_MMU, &cfg); + switch (ret) { + case 0: + return H_SUCCESS; + case -EINVAL: + return H_PARAMETER; + case -ENODEV: + return H_NOT_AVAILABLE; + default: + return H_HARDWARE; + } +} + static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) { if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { @@ -509,8 +572,11 @@ int kvm_arch_init_vcpu(CPUState *cs) case POWERPC_MMU_2_07: if (!cap_htm && !kvmppc_is_pr(cs->kvm_state)) { /* KVM-HV has transactional memory on POWER8 also without the - * KVM_CAP_PPC_HTM extension, so enable it here instead. */ - cap_htm = true; + * KVM_CAP_PPC_HTM extension, so enable it here instead as + * long as it's availble to userspace on the host. */ + if (qemu_getauxval(AT_HWCAP2) & PPC_FEATURE2_HAS_HTM) { + cap_htm = true; + } } break; default: @@ -2132,13 +2198,24 @@ bool kvmppc_spapr_use_multitce(void) return cap_spapr_multitce; } -void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, - bool need_vfio) +int kvmppc_spapr_enable_inkernel_multitce(void) +{ + int ret; + + ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_ENABLE_HCALL, 0, + H_PUT_TCE_INDIRECT, 1); + if (!ret) { + ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_ENABLE_HCALL, 0, + H_STUFF_TCE, 1); + } + + return ret; +} + +void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, + uint64_t bus_offset, uint32_t nb_table, + int *pfd, bool need_vfio) { - struct kvm_create_spapr_tce args = { - .liobn = liobn, - .window_size = window_size, - }; long len; int fd; void *table; @@ -2151,14 +2228,41 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, return NULL; } - fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args); - if (fd < 0) { - fprintf(stderr, "KVM: Failed to create TCE table for liobn 0x%x\n", - liobn); + if (cap_spapr_tce_64) { + struct kvm_create_spapr_tce_64 args = { + .liobn = liobn, + .page_shift = page_shift, + .offset = bus_offset >> page_shift, + .size = nb_table, + .flags = 0 + }; + fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE_64, &args); + if (fd < 0) { + fprintf(stderr, + "KVM: Failed to create TCE64 table for liobn 0x%x\n", + liobn); + return NULL; + } + } else if (cap_spapr_tce) { + uint64_t window_size = (uint64_t) nb_table << page_shift; + struct kvm_create_spapr_tce args = { + .liobn = liobn, + .window_size = window_size, + }; + if ((window_size != args.window_size) || bus_offset) { + return NULL; + } + fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args); + if (fd < 0) { + fprintf(stderr, "KVM: Failed to create TCE table for liobn 0x%x\n", + liobn); + return NULL; + } + } else { return NULL; } - len = (window_size / SPAPR_TCE_PAGE_SIZE) * sizeof(uint64_t); + len = nb_table * sizeof(uint64_t); /* FIXME: round this up to page size */ table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); @@ -2245,14 +2349,8 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on) } } -static void kvmppc_host_cpu_initfn(Object *obj) -{ - assert(kvm_enabled()); -} - static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) { - DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); uint32_t vmx = kvmppc_get_vmx(); uint32_t dfp = kvmppc_get_dfp(); @@ -2280,8 +2378,9 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) pcc->l1_icache_size = icache_size; } - /* Reason: kvmppc_host_cpu_initfn() dies when !kvm_enabled() */ - dc->cannot_destroy_with_object_finalize_yet = true; +#if defined(TARGET_PPC64) + pcc->radix_page_info = kvm_get_radix_page_info(); +#endif /* defined(TARGET_PPC64) */ } bool kvmppc_has_cap_epr(void) @@ -2304,6 +2403,16 @@ bool kvmppc_has_cap_htm(void) return cap_htm; } +bool kvmppc_has_cap_mmu_radix(void) +{ + return cap_mmu_radix; +} + +bool kvmppc_has_cap_mmu_hash_v3(void) +{ + return cap_mmu_hash_v3; +} + static PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc) { ObjectClass *oc = OBJECT_CLASS(pcc); @@ -2333,7 +2442,6 @@ static int kvm_ppc_register_host_cpu_type(void) { TypeInfo type_info = { .name = TYPE_HOST_POWERPC_CPU, - .instance_init = kvmppc_host_cpu_initfn, .class_init = kvmppc_host_cpu_class_init, }; PowerPCCPUClass *pvr_pcc; diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 8e9f42d0c6..f48243d13f 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -33,11 +33,16 @@ int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); int kvmppc_set_tcr(PowerPCCPU *cpu); int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu); +target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, + bool radix, bool gtse, + uint64_t proc_tbl); #ifndef CONFIG_USER_ONLY off_t kvmppc_alloc_rma(void **rma); bool kvmppc_spapr_use_multitce(void); -void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, - bool need_vfio); +int kvmppc_spapr_enable_inkernel_multitce(void); +void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, + uint64_t bus_offset, uint32_t nb_table, + int *pfd, bool need_vfio); int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); int kvmppc_reset_htab(int shift_hint); uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); @@ -53,6 +58,8 @@ void kvmppc_read_hptes(ppc_hash_pte64_t *hptes, hwaddr ptex, int n); void kvmppc_write_hpte(hwaddr ptex, uint64_t pte0, uint64_t pte1); bool kvmppc_has_cap_fixup_hcalls(void); bool kvmppc_has_cap_htm(void); +bool kvmppc_has_cap_mmu_radix(void); +bool kvmppc_has_cap_mmu_hash_v3(void); int kvmppc_enable_hwrng(void); int kvmppc_put_books_sregs(PowerPCCPU *cpu); PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); @@ -156,6 +163,13 @@ static inline int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu) return -1; } +static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, + bool radix, bool gtse, + uint64_t proc_tbl) +{ + return 0; +} + #ifndef CONFIG_USER_ONLY static inline off_t kvmppc_alloc_rma(void **rma) { @@ -167,9 +181,15 @@ static inline bool kvmppc_spapr_use_multitce(void) return false; } -static inline void *kvmppc_create_spapr_tce(uint32_t liobn, - uint32_t window_size, int *fd, - bool need_vfio) +static inline int kvmppc_spapr_enable_inkernel_multitce(void) +{ + return -1; +} + +static inline void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, + uint64_t bus_offset, + uint32_t nb_table, + int *pfd, bool need_vfio) { return NULL; } @@ -252,6 +272,16 @@ static inline bool kvmppc_has_cap_htm(void) return false; } +static inline bool kvmppc_has_cap_mmu_radix(void) +{ + return false; +} + +static inline bool kvmppc_has_cap_mmu_hash_v3(void) +{ + return false; +} + static inline int kvmppc_enable_hwrng(void) { return -1; diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index fa573dd7d2..0e4217821b 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -88,6 +88,14 @@ void helper_store_sdr1(CPUPPCState *env, target_ulong val) } } +void helper_store_pidr(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + env->spr[SPR_BOOKS_PID] = val; + tlb_flush(CPU(cpu)); +} + void helper_store_hid0_601(CPUPPCState *env, target_ulong val) { target_ulong hid0; diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index c1a901455c..e82e3e65e1 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -66,7 +66,7 @@ static void spr_store_dump_spr(int sprn) #endif } -static void spr_write_generic (DisasContext *ctx, int sprn, int gprn) +static void spr_write_generic(DisasContext *ctx, int sprn, int gprn) { gen_store_spr(sprn, cpu_gpr[gprn]); spr_store_dump_spr(sprn); @@ -86,7 +86,7 @@ static void spr_write_generic32(DisasContext *ctx, int sprn, int gprn) #endif } -static void spr_write_clear (DisasContext *ctx, int sprn, int gprn) +static void spr_write_clear(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -106,47 +106,47 @@ static void spr_access_nop(DisasContext *ctx, int sprn, int gprn) /* SPR common to all PowerPC */ /* XER */ -static void spr_read_xer (DisasContext *ctx, int gprn, int sprn) +static void spr_read_xer(DisasContext *ctx, int gprn, int sprn) { gen_read_xer(ctx, cpu_gpr[gprn]); } -static void spr_write_xer (DisasContext *ctx, int sprn, int gprn) +static void spr_write_xer(DisasContext *ctx, int sprn, int gprn) { gen_write_xer(cpu_gpr[gprn]); } /* LR */ -static void spr_read_lr (DisasContext *ctx, int gprn, int sprn) +static void spr_read_lr(DisasContext *ctx, int gprn, int sprn) { tcg_gen_mov_tl(cpu_gpr[gprn], cpu_lr); } -static void spr_write_lr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_lr(DisasContext *ctx, int sprn, int gprn) { tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]); } /* CFAR */ #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) -static void spr_read_cfar (DisasContext *ctx, int gprn, int sprn) +static void spr_read_cfar(DisasContext *ctx, int gprn, int sprn) { tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar); } -static void spr_write_cfar (DisasContext *ctx, int sprn, int gprn) +static void spr_write_cfar(DisasContext *ctx, int sprn, int gprn) { tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]); } #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ /* CTR */ -static void spr_read_ctr (DisasContext *ctx, int gprn, int sprn) +static void spr_read_ctr(DisasContext *ctx, int gprn, int sprn) { tcg_gen_mov_tl(cpu_gpr[gprn], cpu_ctr); } -static void spr_write_ctr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_ctr(DisasContext *ctx, int sprn, int gprn) { tcg_gen_mov_tl(cpu_ctr, cpu_gpr[gprn]); } @@ -157,7 +157,7 @@ static void spr_write_ctr (DisasContext *ctx, int sprn, int gprn) /* UPMCx */ /* USIA */ /* UDECR */ -static void spr_read_ureg (DisasContext *ctx, int gprn, int sprn) +static void spr_read_ureg(DisasContext *ctx, int gprn, int sprn) { gen_load_spr(cpu_gpr[gprn], sprn + 0x10); } @@ -172,7 +172,7 @@ static void spr_write_ureg(DisasContext *ctx, int sprn, int gprn) /* SPR common to all non-embedded PowerPC */ /* DECR */ #if !defined(CONFIG_USER_ONLY) -static void spr_read_decr (DisasContext *ctx, int gprn, int sprn) +static void spr_read_decr(DisasContext *ctx, int gprn, int sprn) { if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); @@ -184,7 +184,7 @@ static void spr_read_decr (DisasContext *ctx, int gprn, int sprn) } } -static void spr_write_decr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_decr(DisasContext *ctx, int sprn, int gprn) { if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); @@ -199,7 +199,7 @@ static void spr_write_decr (DisasContext *ctx, int sprn, int gprn) /* SPR common to all non-embedded PowerPC, except 601 */ /* Time base */ -static void spr_read_tbl (DisasContext *ctx, int gprn, int sprn) +static void spr_read_tbl(DisasContext *ctx, int gprn, int sprn) { if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); @@ -211,7 +211,7 @@ static void spr_read_tbl (DisasContext *ctx, int gprn, int sprn) } } -static void spr_read_tbu (DisasContext *ctx, int gprn, int sprn) +static void spr_read_tbu(DisasContext *ctx, int gprn, int sprn) { if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); @@ -224,19 +224,19 @@ static void spr_read_tbu (DisasContext *ctx, int gprn, int sprn) } __attribute__ (( unused )) -static void spr_read_atbl (DisasContext *ctx, int gprn, int sprn) +static void spr_read_atbl(DisasContext *ctx, int gprn, int sprn) { gen_helper_load_atbl(cpu_gpr[gprn], cpu_env); } __attribute__ (( unused )) -static void spr_read_atbu (DisasContext *ctx, int gprn, int sprn) +static void spr_read_atbu(DisasContext *ctx, int gprn, int sprn) { gen_helper_load_atbu(cpu_gpr[gprn], cpu_env); } #if !defined(CONFIG_USER_ONLY) -static void spr_write_tbl (DisasContext *ctx, int sprn, int gprn) +static void spr_write_tbl(DisasContext *ctx, int sprn, int gprn) { if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); @@ -248,7 +248,7 @@ static void spr_write_tbl (DisasContext *ctx, int sprn, int gprn) } } -static void spr_write_tbu (DisasContext *ctx, int sprn, int gprn) +static void spr_write_tbu(DisasContext *ctx, int sprn, int gprn) { if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); @@ -261,20 +261,20 @@ static void spr_write_tbu (DisasContext *ctx, int sprn, int gprn) } __attribute__ (( unused )) -static void spr_write_atbl (DisasContext *ctx, int sprn, int gprn) +static void spr_write_atbl(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_atbl(cpu_env, cpu_gpr[gprn]); } __attribute__ (( unused )) -static void spr_write_atbu (DisasContext *ctx, int sprn, int gprn) +static void spr_write_atbu(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_atbu(cpu_env, cpu_gpr[gprn]); } #if defined(TARGET_PPC64) __attribute__ (( unused )) -static void spr_read_purr (DisasContext *ctx, int gprn, int sprn) +static void spr_read_purr(DisasContext *ctx, int gprn, int sprn) { gen_helper_load_purr(cpu_gpr[gprn], cpu_env); } @@ -310,38 +310,38 @@ static void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) /* IBAT0U...IBAT0U */ /* IBAT0L...IBAT7L */ -static void spr_read_ibat (DisasContext *ctx, int gprn, int sprn) +static void spr_read_ibat(DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); } -static void spr_read_ibat_h (DisasContext *ctx, int gprn, int sprn) +static void spr_read_ibat_h(DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][((sprn - SPR_IBAT4U) / 2) + 4])); } -static void spr_write_ibatu (DisasContext *ctx, int sprn, int gprn) +static void spr_write_ibatu(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_ibatu_h (DisasContext *ctx, int sprn, int gprn) +static void spr_write_ibatu_h(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4); gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_ibatl (DisasContext *ctx, int sprn, int gprn) +static void spr_write_ibatl(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2); gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_ibatl_h (DisasContext *ctx, int sprn, int gprn) +static void spr_write_ibatl_h(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4); gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); @@ -350,38 +350,38 @@ static void spr_write_ibatl_h (DisasContext *ctx, int sprn, int gprn) /* DBAT0U...DBAT7U */ /* DBAT0L...DBAT7L */ -static void spr_read_dbat (DisasContext *ctx, int gprn, int sprn) +static void spr_read_dbat(DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, DBAT[sprn & 1][(sprn - SPR_DBAT0U) / 2])); } -static void spr_read_dbat_h (DisasContext *ctx, int gprn, int sprn) +static void spr_read_dbat_h(DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, DBAT[sprn & 1][((sprn - SPR_DBAT4U) / 2) + 4])); } -static void spr_write_dbatu (DisasContext *ctx, int sprn, int gprn) +static void spr_write_dbatu(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2); gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_dbatu_h (DisasContext *ctx, int sprn, int gprn) +static void spr_write_dbatu_h(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4); gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_dbatl (DisasContext *ctx, int sprn, int gprn) +static void spr_write_dbatl(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2); gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_dbatl_h (DisasContext *ctx, int sprn, int gprn) +static void spr_write_dbatl_h(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4); gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); @@ -389,19 +389,25 @@ static void spr_write_dbatl_h (DisasContext *ctx, int sprn, int gprn) } /* SDR1 */ -static void spr_write_sdr1 (DisasContext *ctx, int sprn, int gprn) +static void spr_write_sdr1(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_sdr1(cpu_env, cpu_gpr[gprn]); } -/* 64 bits PowerPC specific SPRs */ #if defined(TARGET_PPC64) -static void spr_read_hior (DisasContext *ctx, int gprn, int sprn) +/* 64 bits PowerPC specific SPRs */ +/* PIDR */ +static void spr_write_pidr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_pidr(cpu_env, cpu_gpr[gprn]); +} + +static void spr_read_hior(DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix)); } -static void spr_write_hior (DisasContext *ctx, int sprn, int gprn) +static void spr_write_hior(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0x3FFFFF00000ULL); @@ -413,28 +419,28 @@ static void spr_write_hior (DisasContext *ctx, int sprn, int gprn) /* PowerPC 601 specific registers */ /* RTC */ -static void spr_read_601_rtcl (DisasContext *ctx, int gprn, int sprn) +static void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn) { gen_helper_load_601_rtcl(cpu_gpr[gprn], cpu_env); } -static void spr_read_601_rtcu (DisasContext *ctx, int gprn, int sprn) +static void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn) { gen_helper_load_601_rtcu(cpu_gpr[gprn], cpu_env); } #if !defined(CONFIG_USER_ONLY) -static void spr_write_601_rtcu (DisasContext *ctx, int sprn, int gprn) +static void spr_write_601_rtcu(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_601_rtcu(cpu_env, cpu_gpr[gprn]); } -static void spr_write_601_rtcl (DisasContext *ctx, int sprn, int gprn) +static void spr_write_601_rtcl(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_601_rtcl(cpu_env, cpu_gpr[gprn]); } -static void spr_write_hid0_601 (DisasContext *ctx, int sprn, int gprn) +static void spr_write_hid0_601(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_hid0_601(cpu_env, cpu_gpr[gprn]); /* Must stop the translation as endianness may have changed */ @@ -444,19 +450,19 @@ static void spr_write_hid0_601 (DisasContext *ctx, int sprn, int gprn) /* Unified bats */ #if !defined(CONFIG_USER_ONLY) -static void spr_read_601_ubat (DisasContext *ctx, int gprn, int sprn) +static void spr_read_601_ubat(DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); } -static void spr_write_601_ubatu (DisasContext *ctx, int sprn, int gprn) +static void spr_write_601_ubatu(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); gen_helper_store_601_batl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_601_ubatl (DisasContext *ctx, int sprn, int gprn) +static void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); gen_helper_store_601_batu(cpu_env, t0, cpu_gpr[gprn]); @@ -466,34 +472,34 @@ static void spr_write_601_ubatl (DisasContext *ctx, int sprn, int gprn) /* PowerPC 40x specific registers */ #if !defined(CONFIG_USER_ONLY) -static void spr_read_40x_pit (DisasContext *ctx, int gprn, int sprn) +static void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) { gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); } -static void spr_write_40x_pit (DisasContext *ctx, int sprn, int gprn) +static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); } -static void spr_write_40x_dbcr0 (DisasContext *ctx, int sprn, int gprn) +static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ gen_stop_exception(ctx); } -static void spr_write_40x_sler (DisasContext *ctx, int sprn, int gprn) +static void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); } -static void spr_write_booke_tcr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); } -static void spr_write_booke_tsr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); } @@ -502,19 +508,19 @@ static void spr_write_booke_tsr (DisasContext *ctx, int sprn, int gprn) /* PowerPC 403 specific registers */ /* PBL1 / PBU1 / PBL2 / PBU2 */ #if !defined(CONFIG_USER_ONLY) -static void spr_read_403_pbr (DisasContext *ctx, int gprn, int sprn) +static void spr_read_403_pbr(DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, pb[sprn - SPR_403_PBL1])); } -static void spr_write_403_pbr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_403_pbr(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1); gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_pir (DisasContext *ctx, int sprn, int gprn) +static void spr_write_pir(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xF); @@ -524,7 +530,7 @@ static void spr_write_pir (DisasContext *ctx, int sprn, int gprn) #endif /* SPE specific registers */ -static void spr_read_spefscr (DisasContext *ctx, int gprn, int sprn) +static void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn) { TCGv_i32 t0 = tcg_temp_new_i32(); tcg_gen_ld_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); @@ -532,7 +538,7 @@ static void spr_read_spefscr (DisasContext *ctx, int gprn, int sprn) tcg_temp_free_i32(t0); } -static void spr_write_spefscr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(t0, cpu_gpr[gprn]); @@ -542,7 +548,7 @@ static void spr_write_spefscr (DisasContext *ctx, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) /* Callback used to write the exception vector base */ -static void spr_write_excp_prefix (DisasContext *ctx, int sprn, int gprn) +static void spr_write_excp_prefix(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivpr_mask)); @@ -552,7 +558,7 @@ static void spr_write_excp_prefix (DisasContext *ctx, int sprn, int gprn) tcg_temp_free(t0); } -static void spr_write_excp_vector (DisasContext *ctx, int sprn, int gprn) +static void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn) { int sprn_offs; @@ -578,7 +584,7 @@ static void spr_write_excp_vector (DisasContext *ctx, int sprn, int gprn) } #endif -static inline void vscr_init (CPUPPCState *env, uint32_t val) +static inline void vscr_init(CPUPPCState *env, uint32_t val) { env->vscr = val; /* Altivec always uses round-to-nearest */ @@ -679,7 +685,7 @@ static inline void _spr_register(CPUPPCState *env, int num, } /* Generic PowerPC SPRs */ -static void gen_spr_generic (CPUPPCState *env) +static void gen_spr_generic(CPUPPCState *env) { /* Integer processing */ spr_register(env, SPR_XER, "XER", @@ -764,7 +770,7 @@ static void gen_spr_sdr1(CPUPPCState *env) } /* BATs 0-3 */ -static void gen_low_BATs (CPUPPCState *env) +static void gen_low_BATs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) spr_register(env, SPR_IBAT0U, "IBAT0U", @@ -836,7 +842,7 @@ static void gen_low_BATs (CPUPPCState *env) } /* BATs 4-7 */ -static void gen_high_BATs (CPUPPCState *env) +static void gen_high_BATs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) spr_register(env, SPR_IBAT4U, "IBAT4U", @@ -908,7 +914,7 @@ static void gen_high_BATs (CPUPPCState *env) } /* Generic PowerPC time base */ -static void gen_tbl (CPUPPCState *env) +static void gen_tbl(CPUPPCState *env) { spr_register(env, SPR_VTBL, "TBL", &spr_read_tbl, SPR_NOACCESS, @@ -929,7 +935,7 @@ static void gen_tbl (CPUPPCState *env) } /* Softare table search registers */ -static void gen_6xx_7xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) +static void gen_6xx_7xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) { #if !defined(CONFIG_USER_ONLY) env->nb_tlb = nb_tlbs; @@ -968,7 +974,7 @@ static void gen_6xx_7xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) } /* SPR common to MPC755 and G2 */ -static void gen_spr_G2_755 (CPUPPCState *env) +static void gen_spr_G2_755(CPUPPCState *env) { /* SGPRs */ spr_register(env, SPR_SPRG4, "SPRG4", @@ -990,7 +996,7 @@ static void gen_spr_G2_755 (CPUPPCState *env) } /* SPR common to all 7xx PowerPC implementations */ -static void gen_spr_7xx (CPUPPCState *env) +static void gen_spr_7xx(CPUPPCState *env) { /* Breakpoints */ /* XXX : not implemented */ @@ -1235,7 +1241,7 @@ static void spr_read_thrm(DisasContext *ctx, int gprn, int sprn) } #endif /* !CONFIG_USER_ONLY */ -static void gen_spr_thrm (CPUPPCState *env) +static void gen_spr_thrm(CPUPPCState *env) { /* Thermal management */ /* XXX : not implemented */ @@ -1256,7 +1262,7 @@ static void gen_spr_thrm (CPUPPCState *env) } /* SPR specific to PowerPC 604 implementation */ -static void gen_spr_604 (CPUPPCState *env) +static void gen_spr_604(CPUPPCState *env) { /* Processor identification */ spr_register(env, SPR_PIR, "PIR", @@ -1309,7 +1315,7 @@ static void gen_spr_604 (CPUPPCState *env) } /* SPR specific to PowerPC 603 implementation */ -static void gen_spr_603 (CPUPPCState *env) +static void gen_spr_603(CPUPPCState *env) { /* External access control */ /* XXX : not implemented */ @@ -1327,7 +1333,7 @@ static void gen_spr_603 (CPUPPCState *env) } /* SPR specific to PowerPC G2 implementation */ -static void gen_spr_G2 (CPUPPCState *env) +static void gen_spr_G2(CPUPPCState *env) { /* Memory base address */ /* MBAR */ @@ -1379,7 +1385,7 @@ static void gen_spr_G2 (CPUPPCState *env) } /* SPR specific to PowerPC 602 implementation */ -static void gen_spr_602 (CPUPPCState *env) +static void gen_spr_602(CPUPPCState *env) { /* ESA registers */ /* XXX : not implemented */ @@ -1427,7 +1433,7 @@ static void gen_spr_602 (CPUPPCState *env) } /* SPR specific to PowerPC 601 implementation */ -static void gen_spr_601 (CPUPPCState *env) +static void gen_spr_601(CPUPPCState *env) { /* Multiplication/division register */ /* MQ */ @@ -1503,7 +1509,7 @@ static void gen_spr_601 (CPUPPCState *env) #endif } -static void gen_spr_74xx (CPUPPCState *env) +static void gen_spr_74xx(CPUPPCState *env) { /* Processor identification */ spr_register(env, SPR_PIR, "PIR", @@ -1555,7 +1561,7 @@ static void gen_spr_74xx (CPUPPCState *env) vscr_init(env, 0x00010000); } -static void gen_l3_ctrl (CPUPPCState *env) +static void gen_l3_ctrl(CPUPPCState *env) { /* L3CR */ /* XXX : not implemented */ @@ -1577,7 +1583,7 @@ static void gen_l3_ctrl (CPUPPCState *env) 0x00000000); } -static void gen_74xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) +static void gen_74xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) { #if !defined(CONFIG_USER_ONLY) env->nb_tlb = nb_tlbs; @@ -1603,7 +1609,7 @@ static void gen_74xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) } #if !defined(CONFIG_USER_ONLY) -static void spr_write_e500_l1csr0 (DisasContext *ctx, int sprn, int gprn) +static void spr_write_e500_l1csr0(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); @@ -1621,12 +1627,12 @@ static void spr_write_e500_l1csr1(DisasContext *ctx, int sprn, int gprn) tcg_temp_free(t0); } -static void spr_write_booke206_mmucsr0 (DisasContext *ctx, int sprn, int gprn) +static void spr_write_booke206_mmucsr0(DisasContext *ctx, int sprn, int gprn) { gen_helper_booke206_tlbflush(cpu_env, cpu_gpr[gprn]); } -static void spr_write_booke_pid (DisasContext *ctx, int sprn, int gprn) +static void spr_write_booke_pid(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(sprn); gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); @@ -1634,7 +1640,15 @@ static void spr_write_booke_pid (DisasContext *ctx, int sprn, int gprn) } #endif -static void gen_spr_usprgh (CPUPPCState *env) +static void gen_spr_usprg3(CPUPPCState *env) +{ + spr_register(env, SPR_USPRG3, "USPRG3", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); +} + +static void gen_spr_usprgh(CPUPPCState *env) { spr_register(env, SPR_USPRG4, "USPRG4", &spr_read_ureg, SPR_NOACCESS, @@ -1655,7 +1669,7 @@ static void gen_spr_usprgh (CPUPPCState *env) } /* PowerPC BookE SPR */ -static void gen_spr_BookE (CPUPPCState *env, uint64_t ivor_mask) +static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask) { const char *ivor_names[64] = { "IVOR0", "IVOR1", "IVOR2", "IVOR3", @@ -1907,7 +1921,7 @@ static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask, } /* SPR specific to PowerPC 440 implementation */ -static void gen_spr_440 (CPUPPCState *env) +static void gen_spr_440(CPUPPCState *env) { /* Cache control */ /* XXX : not implemented */ @@ -2048,7 +2062,7 @@ static void gen_spr_440 (CPUPPCState *env) } /* SPR shared between PowerPC 40x implementations */ -static void gen_spr_40x (CPUPPCState *env) +static void gen_spr_40x(CPUPPCState *env) { /* Cache */ /* not emulated, as QEMU do not emulate caches */ @@ -2103,7 +2117,7 @@ static void gen_spr_40x (CPUPPCState *env) } /* SPR specific to PowerPC 405 implementation */ -static void gen_spr_405 (CPUPPCState *env) +static void gen_spr_405(CPUPPCState *env) { /* MMU */ spr_register(env, SPR_40x_PID, "PID", @@ -2209,7 +2223,7 @@ static void gen_spr_405 (CPUPPCState *env) } /* SPR shared between PowerPC 401 & 403 implementations */ -static void gen_spr_401_403 (CPUPPCState *env) +static void gen_spr_401_403(CPUPPCState *env) { /* Time base */ spr_register(env, SPR_403_VTBL, "TBL", @@ -2237,7 +2251,7 @@ static void gen_spr_401_403 (CPUPPCState *env) } /* SPR specific to PowerPC 401 implementation */ -static void gen_spr_401 (CPUPPCState *env) +static void gen_spr_401(CPUPPCState *env) { /* Debug interface */ /* XXX : not implemented */ @@ -2279,7 +2293,7 @@ static void gen_spr_401 (CPUPPCState *env) 0x00000000); } -static void gen_spr_401x2 (CPUPPCState *env) +static void gen_spr_401x2(CPUPPCState *env) { gen_spr_401(env); spr_register(env, SPR_40x_PID, "PID", @@ -2293,7 +2307,7 @@ static void gen_spr_401x2 (CPUPPCState *env) } /* SPR specific to PowerPC 403 implementation */ -static void gen_spr_403 (CPUPPCState *env) +static void gen_spr_403(CPUPPCState *env) { /* Debug interface */ /* XXX : not implemented */ @@ -2329,7 +2343,7 @@ static void gen_spr_403 (CPUPPCState *env) 0x00000000); } -static void gen_spr_403_real (CPUPPCState *env) +static void gen_spr_403_real(CPUPPCState *env) { spr_register(env, SPR_403_PBL1, "PBL1", SPR_NOACCESS, SPR_NOACCESS, @@ -2349,7 +2363,7 @@ static void gen_spr_403_real (CPUPPCState *env) 0x00000000); } -static void gen_spr_403_mmu (CPUPPCState *env) +static void gen_spr_403_mmu(CPUPPCState *env) { /* MMU */ spr_register(env, SPR_40x_PID, "PID", @@ -2363,7 +2377,7 @@ static void gen_spr_403_mmu (CPUPPCState *env) } /* SPR specific to PowerPC compression coprocessor extension */ -static void gen_spr_compress (CPUPPCState *env) +static void gen_spr_compress(CPUPPCState *env) { /* XXX : not implemented */ spr_register(env, SPR_401_SKR, "SKR", @@ -2372,7 +2386,7 @@ static void gen_spr_compress (CPUPPCState *env) 0x00000000); } -static void gen_spr_5xx_8xx (CPUPPCState *env) +static void gen_spr_5xx_8xx(CPUPPCState *env) { /* Exception processing */ spr_register_kvm(env, SPR_DSISR, "DSISR", @@ -2490,7 +2504,7 @@ static void gen_spr_5xx_8xx (CPUPPCState *env) 0x00000000); } -static void gen_spr_5xx (CPUPPCState *env) +static void gen_spr_5xx(CPUPPCState *env) { /* XXX : not implemented */ spr_register(env, SPR_RCPU_MI_GRA, "MI_GRA", @@ -2599,7 +2613,7 @@ static void gen_spr_5xx (CPUPPCState *env) 0x00000000); } -static void gen_spr_8xx (CPUPPCState *env) +static void gen_spr_8xx(CPUPPCState *env) { /* XXX : not implemented */ spr_register(env, SPR_MPC_IC_CST, "IC_CST", @@ -2761,7 +2775,7 @@ static void gen_spr_8xx (CPUPPCState *env) /*****************************************************************************/ /* Exception vectors models */ -static void init_excp_4xx_real (CPUPPCState *env) +static void init_excp_4xx_real(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000100; @@ -2781,7 +2795,7 @@ static void init_excp_4xx_real (CPUPPCState *env) #endif } -static void init_excp_4xx_softmmu (CPUPPCState *env) +static void init_excp_4xx_softmmu(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000100; @@ -2805,7 +2819,7 @@ static void init_excp_4xx_softmmu (CPUPPCState *env) #endif } -static void init_excp_MPC5xx (CPUPPCState *env) +static void init_excp_MPC5xx(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -2830,7 +2844,7 @@ static void init_excp_MPC5xx (CPUPPCState *env) #endif } -static void init_excp_MPC8xx (CPUPPCState *env) +static void init_excp_MPC8xx(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -2861,7 +2875,7 @@ static void init_excp_MPC8xx (CPUPPCState *env) #endif } -static void init_excp_G2 (CPUPPCState *env) +static void init_excp_G2(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -2916,7 +2930,7 @@ static void init_excp_e200(CPUPPCState *env, target_ulong ivpr_mask) #endif } -static void init_excp_BookE (CPUPPCState *env) +static void init_excp_BookE(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000000; @@ -2942,7 +2956,7 @@ static void init_excp_BookE (CPUPPCState *env) #endif } -static void init_excp_601 (CPUPPCState *env) +static void init_excp_601(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -2962,7 +2976,7 @@ static void init_excp_601 (CPUPPCState *env) #endif } -static void init_excp_602 (CPUPPCState *env) +static void init_excp_602(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* XXX: exception prefix has a special behavior on 602 */ @@ -2989,7 +3003,7 @@ static void init_excp_602 (CPUPPCState *env) #endif } -static void init_excp_603 (CPUPPCState *env) +static void init_excp_603(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3013,7 +3027,7 @@ static void init_excp_603 (CPUPPCState *env) #endif } -static void init_excp_604 (CPUPPCState *env) +static void init_excp_604(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3035,7 +3049,7 @@ static void init_excp_604 (CPUPPCState *env) #endif } -static void init_excp_7x0 (CPUPPCState *env) +static void init_excp_7x0(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3058,7 +3072,7 @@ static void init_excp_7x0 (CPUPPCState *env) #endif } -static void init_excp_750cl (CPUPPCState *env) +static void init_excp_750cl(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3080,7 +3094,7 @@ static void init_excp_750cl (CPUPPCState *env) #endif } -static void init_excp_750cx (CPUPPCState *env) +static void init_excp_750cx(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3103,7 +3117,7 @@ static void init_excp_750cx (CPUPPCState *env) } /* XXX: Check if this is correct */ -static void init_excp_7x5 (CPUPPCState *env) +static void init_excp_7x5(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3129,7 +3143,7 @@ static void init_excp_7x5 (CPUPPCState *env) #endif } -static void init_excp_7400 (CPUPPCState *env) +static void init_excp_7400(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3154,7 +3168,7 @@ static void init_excp_7400 (CPUPPCState *env) #endif } -static void init_excp_7450 (CPUPPCState *env) +static void init_excp_7450(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3181,8 +3195,8 @@ static void init_excp_7450 (CPUPPCState *env) #endif } -#if defined (TARGET_PPC64) -static void init_excp_970 (CPUPPCState *env) +#if defined(TARGET_PPC64) +static void init_excp_970(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3210,7 +3224,7 @@ static void init_excp_970 (CPUPPCState *env) #endif } -static void init_excp_POWER7 (CPUPPCState *env) +static void init_excp_POWER7(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; @@ -3255,17 +3269,17 @@ static void init_excp_POWER8(CPUPPCState *env) /*****************************************************************************/ /* Power management enable checks */ -static int check_pow_none (CPUPPCState *env) +static int check_pow_none(CPUPPCState *env) { return 0; } -static int check_pow_nocheck (CPUPPCState *env) +static int check_pow_nocheck(CPUPPCState *env) { return 1; } -static int check_pow_hid0 (CPUPPCState *env) +static int check_pow_hid0(CPUPPCState *env) { if (env->spr[SPR_HID0] & 0x00E00000) return 1; @@ -3273,7 +3287,7 @@ static int check_pow_hid0 (CPUPPCState *env) return 0; } -static int check_pow_hid0_74xx (CPUPPCState *env) +static int check_pow_hid0_74xx(CPUPPCState *env) { if (env->spr[SPR_HID0] & 0x00600000) return 1; @@ -3318,7 +3332,7 @@ static bool ppc_cpu_interrupts_big_endian_lpcr(PowerPCCPU *cpu) \ static void glue(glue(ppc_, _name), _cpu_family_class_init) -static void init_proc_401 (CPUPPCState *env) +static void init_proc_401(CPUPPCState *env) { gen_spr_40x(env); gen_spr_401_403(env); @@ -3364,7 +3378,7 @@ POWERPC_FAMILY(401)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_401x2 (CPUPPCState *env) +static void init_proc_401x2(CPUPPCState *env) { gen_spr_40x(env); gen_spr_401_403(env); @@ -3422,7 +3436,7 @@ POWERPC_FAMILY(401x2)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_401x3 (CPUPPCState *env) +static void init_proc_401x3(CPUPPCState *env) { gen_spr_40x(env); gen_spr_401_403(env); @@ -3475,7 +3489,7 @@ POWERPC_FAMILY(401x3)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_IOP480 (CPUPPCState *env) +static void init_proc_IOP480(CPUPPCState *env) { gen_spr_40x(env); gen_spr_401_403(env); @@ -3533,7 +3547,7 @@ POWERPC_FAMILY(IOP480)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_403 (CPUPPCState *env) +static void init_proc_403(CPUPPCState *env) { gen_spr_40x(env); gen_spr_401_403(env); @@ -3580,7 +3594,7 @@ POWERPC_FAMILY(403)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_403GCX (CPUPPCState *env) +static void init_proc_403GCX(CPUPPCState *env) { gen_spr_40x(env); gen_spr_401_403(env); @@ -3647,7 +3661,7 @@ POWERPC_FAMILY(403GCX)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_405 (CPUPPCState *env) +static void init_proc_405(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -3713,7 +3727,7 @@ POWERPC_FAMILY(405)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_440EP (CPUPPCState *env) +static void init_proc_440EP(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -3817,7 +3831,7 @@ POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_440GP (CPUPPCState *env) +static void init_proc_440GP(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -3900,7 +3914,7 @@ POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_440x4 (CPUPPCState *env) +static void init_proc_440x4(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -3983,7 +3997,7 @@ POWERPC_FAMILY(440x4)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_440x5 (CPUPPCState *env) +static void init_proc_440x5(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -4229,7 +4243,7 @@ POWERPC_FAMILY(460)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_460F (CPUPPCState *env) +static void init_proc_460F(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -4339,7 +4353,7 @@ POWERPC_FAMILY(460F)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_MPC5xx (CPUPPCState *env) +static void init_proc_MPC5xx(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -4383,7 +4397,7 @@ POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_MPC8xx (CPUPPCState *env) +static void init_proc_MPC8xx(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -4428,7 +4442,7 @@ POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) /* Freescale 82xx cores (aka PowerQUICC-II) */ -static void init_proc_G2 (CPUPPCState *env) +static void init_proc_G2(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -4507,7 +4521,7 @@ POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_G2LE (CPUPPCState *env) +static void init_proc_G2LE(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -4589,7 +4603,7 @@ POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_e200 (CPUPPCState *env) +static void init_proc_e200(CPUPPCState *env) { /* Time base */ gen_tbl(env); @@ -4743,7 +4757,7 @@ POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_e300 (CPUPPCState *env) +static void init_proc_e300(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -4875,7 +4889,7 @@ enum fsl_e500_version { fsl_e5500, }; -static void init_proc_e500 (CPUPPCState *env, int version) +static void init_proc_e500(CPUPPCState *env, int version) { PowerPCCPU *cpu = ppc_env_get_cpu(env); uint32_t tlbncfg[2]; @@ -4908,6 +4922,7 @@ static void init_proc_e500 (CPUPPCState *env, int version) break; } gen_spr_BookE(env, ivor_mask); + gen_spr_usprg3(env); /* Processor identification */ spr_register(env, SPR_BOOKE_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, @@ -5243,7 +5258,7 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) #define POWERPC_MSRR_601 (0x0000000000001040ULL) -static void init_proc_601 (CPUPPCState *env) +static void init_proc_601(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -5316,7 +5331,7 @@ POWERPC_FAMILY(601)(ObjectClass *oc, void *data) #define POWERPC_MSRR_601v (0x0000000000001040ULL) -static void init_proc_601v (CPUPPCState *env) +static void init_proc_601v(CPUPPCState *env) { init_proc_601(env); /* XXX : not implemented */ @@ -5358,7 +5373,7 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data) pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK; } -static void init_proc_602 (CPUPPCState *env) +static void init_proc_602(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -5428,7 +5443,7 @@ POWERPC_FAMILY(602)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_603 (CPUPPCState *env) +static void init_proc_603(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -5495,7 +5510,7 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_603E (CPUPPCState *env) +static void init_proc_603E(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -5562,7 +5577,7 @@ POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_604 (CPUPPCState *env) +static void init_proc_604(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -5626,7 +5641,7 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_604E (CPUPPCState *env) +static void init_proc_604E(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -5710,7 +5725,7 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_740 (CPUPPCState *env) +static void init_proc_740(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -5781,7 +5796,7 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_750 (CPUPPCState *env) +static void init_proc_750(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -5860,7 +5875,7 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_750cl (CPUPPCState *env) +static void init_proc_750cl(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6062,7 +6077,7 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_750cx (CPUPPCState *env) +static void init_proc_750cx(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6145,7 +6160,7 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_750fx (CPUPPCState *env) +static void init_proc_750fx(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6233,7 +6248,7 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_750gx (CPUPPCState *env) +static void init_proc_750gx(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6321,7 +6336,7 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_745 (CPUPPCState *env) +static void init_proc_745(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6397,7 +6412,7 @@ POWERPC_FAMILY(745)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_755 (CPUPPCState *env) +static void init_proc_755(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6484,7 +6499,7 @@ POWERPC_FAMILY(755)(ObjectClass *oc, void *data) POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; } -static void init_proc_7400 (CPUPPCState *env) +static void init_proc_7400(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6563,7 +6578,7 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_7410 (CPUPPCState *env) +static void init_proc_7410(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6648,7 +6663,7 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_7440 (CPUPPCState *env) +static void init_proc_7440(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6756,7 +6771,7 @@ POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_7450 (CPUPPCState *env) +static void init_proc_7450(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -6890,7 +6905,7 @@ POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_7445 (CPUPPCState *env) +static void init_proc_7445(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -7027,7 +7042,7 @@ POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_7455 (CPUPPCState *env) +static void init_proc_7455(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -7166,7 +7181,7 @@ POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_7457 (CPUPPCState *env) +static void init_proc_7457(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -7329,7 +7344,7 @@ POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static void init_proc_e600 (CPUPPCState *env) +static void init_proc_e600(CPUPPCState *env) { gen_spr_ne_601(env); gen_spr_sdr1(env); @@ -7471,7 +7486,7 @@ POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -#if defined (TARGET_PPC64) +#if defined(TARGET_PPC64) #if defined(CONFIG_USER_ONLY) #define POWERPC970_HID5_INIT 0x00000080 #else @@ -7530,7 +7545,7 @@ static void spr_write_prev_upper32(DisasContext *ctx, int sprn, int gprn) tcg_temp_free(spr); } -static int check_pow_970 (CPUPPCState *env) +static int check_pow_970(CPUPPCState *env) { if (env->spr[SPR_HID0] & (HID0_DEEPNAP | HID0_DOZE | HID0_NAP)) { return 1; @@ -8200,7 +8215,7 @@ static void gen_spr_power8_book4(CPUPPCState *env) KVM_REG_PPC_ACOP, 0); spr_register_kvm(env, SPR_BOOKS_PID, "PID", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_pidr, KVM_REG_PPC_PID, 0); spr_register_kvm(env, SPR_WORT, "WORT", SPR_NOACCESS, SPR_NOACCESS, @@ -8239,6 +8254,7 @@ static void init_proc_book3s_common(CPUPPCState *env) { gen_spr_ne_601(env); gen_tbl(env); + gen_spr_usprg3(env); gen_spr_book3s_altivec(env); gen_spr_book3s_pmu_sup(env); gen_spr_book3s_pmu_user(env); @@ -8497,7 +8513,7 @@ static const struct ppc_segment_page_sizes POWER7_POWER8_sps = { }; #endif /* CONFIG_SOFTMMU */ -static void init_proc_POWER7 (CPUPPCState *env) +static void init_proc_POWER7(CPUPPCState *env) { /* Common Registers */ init_proc_book3s_common(env); @@ -8808,6 +8824,25 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; } +#ifdef CONFIG_SOFTMMU +/* + * Radix pg sizes and AP encodings for dt node ibm,processor-radix-AP-encodings + * Encoded as array of int_32s in the form: + * 0bxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyy + * x -> AP encoding + * y -> radix mode supported page size (encoded as a shift) + */ +static struct ppc_radix_page_info POWER9_radix_page_info = { + .count = 4, + .entries = { + 0x0000000c, /* 4K - enc: 0x0 */ + 0xa0000010, /* 64K - enc: 0x5 */ + 0x20000015, /* 2M - enc: 0x1 */ + 0x4000001e /* 1G - enc: 0x2 */ + } +}; +#endif /* CONFIG_SOFTMMU */ + static void init_proc_POWER9(CPUPPCState *env) { /* Common Registers */ @@ -8959,6 +8994,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; /* segment page size remain the same */ pcc->sps = &POWER7_POWER8_sps; + pcc->radix_page_info = &POWER9_radix_page_info; #endif pcc->excp_model = POWERPC_EXCP_POWER8; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; @@ -9043,7 +9079,7 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) #endif /* !defined(CONFIG_USER_ONLY) */ -#endif /* defined (TARGET_PPC64) */ +#endif /* defined(TARGET_PPC64) */ /*****************************************************************************/ /* Generic CPU instantiation routine */ @@ -9214,7 +9250,7 @@ static void init_ppc_proc(PowerPCCPU *cpu) } #if defined(PPC_DUMP_CPU) -static void dump_ppc_sprs (CPUPPCState *env) +static void dump_ppc_sprs(CPUPPCState *env) { ppc_spr_t *spr; #if !defined(CONFIG_USER_ONLY) @@ -9263,7 +9299,7 @@ enum { #define PPC_OPCODE_MASK 0x3 -static inline int is_indirect_opcode (void *handler) +static inline int is_indirect_opcode(void *handler) { return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT; } @@ -9275,7 +9311,7 @@ static inline opc_handler_t **ind_table(void *handler) /* Instruction table creation */ /* Opcodes tables creation */ -static void fill_new_table (opc_handler_t **table, int len) +static void fill_new_table(opc_handler_t **table, int len) { int i; @@ -9283,7 +9319,7 @@ static void fill_new_table (opc_handler_t **table, int len) table[i] = &invalid_handler; } -static int create_new_table (opc_handler_t **table, unsigned char idx) +static int create_new_table(opc_handler_t **table, unsigned char idx) { opc_handler_t **tmp; @@ -9294,7 +9330,7 @@ static int create_new_table (opc_handler_t **table, unsigned char idx) return 0; } -static int insert_in_table (opc_handler_t **table, unsigned char idx, +static int insert_in_table(opc_handler_t **table, unsigned char idx, opc_handler_t *handler) { if (table[idx] != &invalid_handler) @@ -9304,8 +9340,8 @@ static int insert_in_table (opc_handler_t **table, unsigned char idx, return 0; } -static int register_direct_insn (opc_handler_t **ppc_opcodes, - unsigned char idx, opc_handler_t *handler) +static int register_direct_insn(opc_handler_t **ppc_opcodes, + unsigned char idx, opc_handler_t *handler) { if (insert_in_table(ppc_opcodes, idx, handler) < 0) { printf("*** ERROR: opcode %02x already assigned in main " @@ -9320,9 +9356,9 @@ static int register_direct_insn (opc_handler_t **ppc_opcodes, return 0; } -static int register_ind_in_table (opc_handler_t **table, - unsigned char idx1, unsigned char idx2, - opc_handler_t *handler) +static int register_ind_in_table(opc_handler_t **table, + unsigned char idx1, unsigned char idx2, + opc_handler_t *handler) { if (table[idx1] == &invalid_handler) { if (create_new_table(table, idx1) < 0) { @@ -9355,16 +9391,16 @@ static int register_ind_in_table (opc_handler_t **table, return 0; } -static int register_ind_insn (opc_handler_t **ppc_opcodes, - unsigned char idx1, unsigned char idx2, - opc_handler_t *handler) +static int register_ind_insn(opc_handler_t **ppc_opcodes, + unsigned char idx1, unsigned char idx2, + opc_handler_t *handler) { return register_ind_in_table(ppc_opcodes, idx1, idx2, handler); } -static int register_dblind_insn (opc_handler_t **ppc_opcodes, - unsigned char idx1, unsigned char idx2, - unsigned char idx3, opc_handler_t *handler) +static int register_dblind_insn(opc_handler_t **ppc_opcodes, + unsigned char idx1, unsigned char idx2, + unsigned char idx3, opc_handler_t *handler) { if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) { printf("*** ERROR: unable to join indirect table idx " @@ -9407,7 +9443,7 @@ static int register_trplind_insn(opc_handler_t **ppc_opcodes, } return 0; } -static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn) +static int register_insn(opc_handler_t **ppc_opcodes, opcode_t *insn) { if (insn->opc2 != 0xFF) { if (insn->opc3 != 0xFF) { @@ -9435,7 +9471,7 @@ static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn) return 0; } -static int test_opcode_table (opc_handler_t **table, int len) +static int test_opcode_table(opc_handler_t **table, int len) { int i, count, tmp; @@ -9462,7 +9498,7 @@ static int test_opcode_table (opc_handler_t **table, int len) return count; } -static void fix_opcode_tables (opc_handler_t **ppc_opcodes) +static void fix_opcode_tables(opc_handler_t **ppc_opcodes) { if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) printf("*** WARNING: no opcode defined !\n"); @@ -9493,7 +9529,7 @@ static void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp) } #if defined(PPC_DUMP_CPU) -static void dump_ppc_insns (CPUPPCState *env) +static void dump_ppc_insns(CPUPPCState *env) { opc_handler_t **table, *handler; const char *p, *q; @@ -9907,7 +9943,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) case POWERPC_MMU_601: mmu_model = "PowerPC 601"; break; -#if defined (TARGET_PPC64) +#if defined(TARGET_PPC64) case POWERPC_MMU_64B: mmu_model = "PowerPC 64"; break; @@ -9950,7 +9986,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) case POWERPC_EXCP_BOOKE: excp_model = "PowerPC BookE"; break; -#if defined (TARGET_PPC64) +#if defined(TARGET_PPC64) case POWERPC_EXCP_970: excp_model = "PowerPC 970"; break; @@ -9975,7 +10011,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) case PPC_FLAGS_INPUT_RCPU: bus_model = "RCPU / MPC8xx"; break; -#if defined (TARGET_PPC64) +#if defined(TARGET_PPC64) case PPC_FLAGS_INPUT_970: bus_model = "PowerPC 970"; break; diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 1434d15315..ce461cc905 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -376,12 +376,12 @@ static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, static void qdict_add_disabled_feat(const char *name, void *opaque) { - qdict_put((QDict *) opaque, name, qbool_from_bool(false)); + qdict_put(opaque, name, qbool_from_bool(false)); } static void qdict_add_enabled_feat(const char *name, void *opaque) { - qdict_put((QDict *) opaque, name, qbool_from_bool(true)); + qdict_put(opaque, name, qbool_from_bool(true)); } /* convert S390CPUDef into a static CpuModelInfo */ diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index ac47154b83..1a249d8359 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -47,16 +47,16 @@ #include "exec/memattrs.h" #include "hw/s390x/s390-virtio-ccw.h" -/* #define DEBUG_KVM */ - -#ifdef DEBUG_KVM -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) +#ifndef DEBUG_KVM +#define DEBUG_KVM 0 #endif +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_KVM) { \ + fprintf(stderr, fmt, ## __VA_ARGS__); \ + } \ +} while (0); + #define kvm_vm_check_mem_attr(s, attr) \ kvm_vm_check_attr(s, KVM_S390_VM_MEM_CTRL, attr) diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index 93b0e61366..eca82441d0 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -288,7 +288,9 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) switch (num) { case 0x500: /* KVM hypercall */ + qemu_mutex_lock_iothread(); r = s390_virtio_hypercall(env); + qemu_mutex_unlock_iothread(); break; case 0x44: /* yield */ @@ -515,7 +517,8 @@ uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, /* Remember: Use "R1 or R1 + 1, whichever is the odd-numbered register" as parameter (input). Status (output) is always R1. */ - switch (order_code) { + /* sigp contains the order code in bit positions 56-63, mask it here. */ + switch (order_code & 0xff) { case SIGP_SET_ARCH: /* switch arch */ break; diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 09a19c6f35..75ea247bc4 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -130,14 +130,7 @@ enum { static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { -#if QEMU_GNUC_PREREQ(4, 1) __builtin___clear_cache((char *) start, (char *) stop); -#else - register uintptr_t _beg __asm("a1") = start; - register uintptr_t _end __asm("a2") = stop; - register uintptr_t _flg __asm("a3") = 0; - __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg)); -#endif } #endif diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 95a39b7d8c..6b1f41500c 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2861,6 +2861,9 @@ void tcg_gen_atomic_cmpxchg_i64(TCGv_i64 retv, TCGv addr, TCGv_i64 cmpv, #endif #else gen_helper_exit_atomic(tcg_ctx.tcg_env); + /* Produce a result, so that we have a well-formed opcode stream + with respect to uses of the result in the (dead) code following. */ + tcg_gen_movi_i64(retv, 0); #endif /* CONFIG_ATOMIC64 */ } else { TCGv_i32 c32 = tcg_temp_new_i32(); @@ -2966,6 +2969,9 @@ static void do_atomic_op_i64(TCGv_i64 ret, TCGv addr, TCGv_i64 val, #endif #else gen_helper_exit_atomic(tcg_ctx.tcg_env); + /* Produce a result, so that we have a well-formed opcode stream + with respect to uses of the result in the (dead) code following. */ + tcg_gen_movi_i64(ret, 0); #endif /* CONFIG_ATOMIC64 */ } else { TCGv_i32 v32 = tcg_temp_new_i32(); diff --git a/tests/Makefile.include b/tests/Makefile.include index f3de81fcfb..31931c0d77 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -331,6 +331,7 @@ check-qtest-xtensaeb-y = $(check-qtest-xtensa-y) check-qtest-s390x-y = tests/boot-serial-test$(EXESUF) check-qtest-generic-y += tests/qom-test$(EXESUF) +check-qtest-generic-y += tests/test-hmp$(EXESUF) qapi-schema += alternate-any.json qapi-schema += alternate-array.json @@ -519,7 +520,7 @@ QEMU_CFLAGS += -I$(SRC_PATH)/tests # Deps that are common to various different sets of tests below -test-util-obj-y = $(trace-obj-y) libqemuutil.a libqemustub.a +test-util-obj-y = libqemuutil.a libqemustub.a test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y) test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ tests/test-qapi-event.o tests/test-qmp-introspect.o \ @@ -720,6 +721,7 @@ tests/tpci200-test$(EXESUF): tests/tpci200-test.o tests/display-vga-test$(EXESUF): tests/display-vga-test.o tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o tests/qom-test$(EXESUF): tests/qom-test.o +tests/test-hmp$(EXESUF): tests/test-hmp.o tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-pc-obj-y) tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y) tests/nvme-test$(EXESUF): tests/nvme-test.o diff --git a/tests/libqtest.c b/tests/libqtest.c index 99b1195355..512c150266 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -588,6 +588,12 @@ char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) " 'arguments': {'command-line': %s}}", cmd); ret = g_strdup(qdict_get_try_str(resp, "return")); + while (ret == NULL && qdict_get_try_str(resp, "event")) { + /* Ignore asynchronous QMP events */ + QDECREF(resp); + resp = qtest_qmp_receive(s); + ret = g_strdup(qdict_get_try_str(resp, "return")); + } g_assert(ret); QDECREF(resp); g_free(cmd); @@ -940,3 +946,33 @@ bool qtest_big_endian(QTestState *s) { return s->big_endian; } + +void qtest_cb_for_every_machine(void (*cb)(const char *machine)) +{ + QDict *response, *minfo; + QList *list; + const QListEntry *p; + QObject *qobj; + QString *qstr; + const char *mname; + + qtest_start("-machine none"); + response = qmp("{ 'execute': 'query-machines' }"); + g_assert(response); + list = qdict_get_qlist(response, "return"); + g_assert(list); + + for (p = qlist_first(list); p; p = qlist_next(p)) { + minfo = qobject_to_qdict(qlist_entry_obj(p)); + g_assert(minfo); + qobj = qdict_get(minfo, "name"); + g_assert(qobj); + qstr = qobject_to_qstring(qobj); + g_assert(qstr); + mname = qstring_get_str(qstr); + cb(mname); + } + + qtest_end(); + QDECREF(response); +} diff --git a/tests/libqtest.h b/tests/libqtest.h index 2c9962d94f..38bc1e9953 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -132,11 +132,12 @@ void qtest_qmp_eventwait(QTestState *s, const char *event); QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event); /** - * qtest_hmpv: + * qtest_hmp: * @s: #QTestState instance to operate on. * @fmt...: HMP command to send to QEMU * * Send HMP command to QEMU via QMP's human-monitor-command. + * QMP events are discarded. * * Returns: the command's output. The caller should g_free() it. */ @@ -149,6 +150,7 @@ char *qtest_hmp(QTestState *s, const char *fmt, ...); * @ap: HMP command arguments * * Send HMP command to QEMU via QMP's human-monitor-command. + * QMP events are discarded. * * Returns: the command's output. The caller should g_free() it. */ @@ -917,4 +919,12 @@ void qmp_fd_send(int fd, const char *fmt, ...); QDict *qmp_fdv(int fd, const char *fmt, va_list ap); QDict *qmp_fd(int fd, const char *fmt, ...); +/** + * qtest_cb_for_every_machine: + * @cb: Pointer to the callback function + * + * Call a callback function for every name of all available machines. + */ +void qtest_cb_for_every_machine(void (*cb)(const char *machine)); + #endif diff --git a/tests/pc-cpu-test.c b/tests/pc-cpu-test.c index c3a2633d3c..c4211a4e85 100644 --- a/tests/pc-cpu-test.c +++ b/tests/pc-cpu-test.c @@ -79,69 +79,46 @@ static void test_data_free(gpointer data) g_free(pc); } -static void add_pc_test_cases(void) +static void add_pc_test_case(const char *mname) { - QDict *response, *minfo; - QList *list; - const QListEntry *p; - QObject *qobj; - QString *qstr; - const char *mname; char *path; PCTestData *data; - qtest_start("-machine none"); - response = qmp("{ 'execute': 'query-machines' }"); - g_assert(response); - list = qdict_get_qlist(response, "return"); - g_assert(list); - - for (p = qlist_first(list); p; p = qlist_next(p)) { - minfo = qobject_to_qdict(qlist_entry_obj(p)); - g_assert(minfo); - qobj = qdict_get(minfo, "name"); - g_assert(qobj); - qstr = qobject_to_qstring(qobj); - g_assert(qstr); - mname = qstring_get_str(qstr); - if (!g_str_has_prefix(mname, "pc-")) { - continue; - } - data = g_malloc(sizeof(PCTestData)); - data->machine = g_strdup(mname); - data->cpu_model = "Haswell"; /* 1.3+ theoretically */ - data->sockets = 1; - data->cores = 3; - data->threads = 2; - data->maxcpus = data->sockets * data->cores * data->threads * 2; - if (g_str_has_suffix(mname, "-1.4") || - (strcmp(mname, "pc-1.3") == 0) || - (strcmp(mname, "pc-1.2") == 0) || - (strcmp(mname, "pc-1.1") == 0) || - (strcmp(mname, "pc-1.0") == 0) || - (strcmp(mname, "pc-0.15") == 0) || - (strcmp(mname, "pc-0.14") == 0) || - (strcmp(mname, "pc-0.13") == 0) || - (strcmp(mname, "pc-0.12") == 0) || - (strcmp(mname, "pc-0.11") == 0) || - (strcmp(mname, "pc-0.10") == 0)) { - path = g_strdup_printf("cpu/%s/init/%ux%ux%u&maxcpus=%u", - mname, data->sockets, data->cores, - data->threads, data->maxcpus); - qtest_add_data_func_full(path, data, test_pc_without_cpu_add, - test_data_free); - g_free(path); - } else { - path = g_strdup_printf("cpu/%s/add/%ux%ux%u&maxcpus=%u", - mname, data->sockets, data->cores, - data->threads, data->maxcpus); - qtest_add_data_func_full(path, data, test_pc_with_cpu_add, - test_data_free); - g_free(path); - } + if (!g_str_has_prefix(mname, "pc-")) { + return; + } + data = g_malloc(sizeof(PCTestData)); + data->machine = g_strdup(mname); + data->cpu_model = "Haswell"; /* 1.3+ theoretically */ + data->sockets = 1; + data->cores = 3; + data->threads = 2; + data->maxcpus = data->sockets * data->cores * data->threads * 2; + if (g_str_has_suffix(mname, "-1.4") || + (strcmp(mname, "pc-1.3") == 0) || + (strcmp(mname, "pc-1.2") == 0) || + (strcmp(mname, "pc-1.1") == 0) || + (strcmp(mname, "pc-1.0") == 0) || + (strcmp(mname, "pc-0.15") == 0) || + (strcmp(mname, "pc-0.14") == 0) || + (strcmp(mname, "pc-0.13") == 0) || + (strcmp(mname, "pc-0.12") == 0) || + (strcmp(mname, "pc-0.11") == 0) || + (strcmp(mname, "pc-0.10") == 0)) { + path = g_strdup_printf("cpu/%s/init/%ux%ux%u&maxcpus=%u", + mname, data->sockets, data->cores, + data->threads, data->maxcpus); + qtest_add_data_func_full(path, data, test_pc_without_cpu_add, + test_data_free); + g_free(path); + } else { + path = g_strdup_printf("cpu/%s/add/%ux%ux%u&maxcpus=%u", + mname, data->sockets, data->cores, + data->threads, data->maxcpus); + qtest_add_data_func_full(path, data, test_pc_with_cpu_add, + test_data_free); + g_free(path); } - QDECREF(response); - qtest_end(); } int main(int argc, char **argv) @@ -151,7 +128,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - add_pc_test_cases(); + qtest_cb_for_every_machine(add_pc_test_case); } return g_test_run(); diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017 index e3f9e75967..4f9302db42 100755 --- a/tests/qemu-iotests/017 +++ b/tests/qemu-iotests/017 @@ -41,6 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow qcow2 vmdk qed _supported_proto generic +_unsupported_proto vxhs _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 index 9c4a68c977..7a111100ec 100755 --- a/tests/qemu-iotests/020 +++ b/tests/qemu-iotests/020 @@ -43,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow qcow2 vmdk qed _supported_proto generic +_unsupported_proto vxhs _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index f5a7f02b25..7fadfbace5 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -119,7 +119,7 @@ done echo -echo === Refcout table growth tests === +echo === Refcount table growth tests === echo CLUSTER_SIZE=512 diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index 59b8f74cd4..86a50a2e13 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -462,7 +462,7 @@ 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 === +=== Refcount table growth tests === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache index b4aeebcc61..ea2e166a4e 100644 --- a/tests/qemu-iotests/026.out.nocache +++ b/tests/qemu-iotests/026.out.nocache @@ -470,7 +470,7 @@ 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 === +=== Refcount table growth tests === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index 7783e57c71..97a8869251 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -32,6 +32,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu rm -f "${TEST_IMG}.copy" _cleanup_test_img } diff --git a/tests/qemu-iotests/028.out b/tests/qemu-iotests/028.out index acd2870bae..7d54aeb003 100644 --- a/tests/qemu-iotests/028.out +++ b/tests/qemu-iotests/028.out @@ -469,7 +469,7 @@ No errors were found on the image. block-backup Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT -(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[D[D[D[D[D[D[D[D[D[Dinfo block-[K[D[D[D[D[D[D[D[D[D[D[Dinfo block-j[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block-jo[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block-job[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block-jobs[K +(qemu) info block-jobs No active jobs === IO: pattern 195 read 512/512 bytes at offset 3221194240 diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index e639ac0ddf..30bab24dc0 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -42,6 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting intenal snapshots _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux # Internal snapshots are (currently) impossible with refcount_bits=1 _unsupported_imgopts 'refcount_bits=1[^0-9]' diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 630cb7a114..26c29deb51 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -60,7 +60,8 @@ function do_run_qemu() function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_generated_node_ids + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | + _filter_generated_node_ids | _filter_hmp } size=128M @@ -231,6 +232,7 @@ echo === Leaving out required options === echo run_qemu -drive driver=file +run_qemu -drive driver=file,filename= run_qemu -drive driver=nbd run_qemu -drive driver=raw run_qemu -drive file.driver=file diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 7524c62025..4d3b1ff316 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -58,12 +58,12 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig,if=none,id=drive0 -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 +(qemu) info block drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files @@ -79,11 +79,11 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.f Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit 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' @@ -103,7 +103,7 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: Lazy ref Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit === No medium === @@ -117,93 +117,93 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit === Cache modes === Testing: -drive driver=null-co,cache=none QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=directsync QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=writeback QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=writethrough QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=unsafe QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=invalid_value QEMU_PROG: -drive driver=null-co,cache=invalid_value: invalid cache option Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -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 +(qemu) info block drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block f[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fi[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block file[K +(qemu) info block file file: TEST_DIR/t.qcow2 (file) Cache mode: writeback -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K +(qemu) info block backing backing: TEST_DIR/t.qcow2.base (qcow2, read-only) Cache mode: writeback, ignore flushes -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-f[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-file[K +(qemu) info block backing-file backing-file: TEST_DIR/t.qcow2.base (file, read-only) Cache mode: writeback, ignore flushes -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -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 +(qemu) info block drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Removable device: not locked, tray closed Cache mode: writethrough Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block f[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fi[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block file[K +(qemu) info block file file: TEST_DIR/t.qcow2 (file) Cache mode: writeback -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K +(qemu) info block backing backing: TEST_DIR/t.qcow2.base (qcow2, read-only) Cache mode: writeback, ignore flushes -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-f[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-file[K +(qemu) info block backing-file backing-file: TEST_DIR/t.qcow2.base (file, read-only) Cache mode: writeback, ignore flushes -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -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 +(qemu) info block drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Removable device: not locked, tray closed Cache mode: writeback, ignore flushes Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block f[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fi[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block file[K +(qemu) info block file file: TEST_DIR/t.qcow2 (file) Cache mode: writeback, ignore flushes -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K +(qemu) info block backing backing: TEST_DIR/t.qcow2.base (qcow2, read-only) Cache mode: writeback, ignore flushes -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-f[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-file[K +(qemu) info block backing-file backing-file: TEST_DIR/t.qcow2.base (file, read-only) Cache mode: writeback, ignore flushes -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults QEMU_PROG: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0: invalid cache option @@ -213,7 +213,7 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filenam Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit === Leaving out required options === @@ -221,6 +221,9 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive driver=file QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name +Testing: -drive driver=file,filename= +QEMU_PROG: -drive driver=file,filename=: The 'file' block driver requires a file name + Testing: -drive driver=nbd QEMU_PROG: -drive driver=nbd: NBD server address missing @@ -307,15 +310,15 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,throttling.bps-total=-5: bps/iops/max va Testing: -drive file=TEST_DIR/t.qcow2,bps=0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,bps=1 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,bps=1000000000000000 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,bps=1000000000000001 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=1000000000000001: bps/iops/max values must be within [0, 1000000000000000] @@ -337,11 +340,11 @@ QEMU_PROG: -drive file.filename=foo:bar: Could not open 'foo:bar': No such file Testing: -hda file:TEST_DIR/t.qcow2 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=file:TEST_DIR/t.qcow2 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file.filename=file:TEST_DIR/t.qcow2 QEMU_PROG: -drive file.filename=file:TEST_DIR/t.qcow2: Could not open 'file:TEST_DIR/t.qcow2': No such file or directory @@ -353,78 +356,78 @@ wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=file:TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=file:TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Testing: -drive file=TEST_DIR/t.qcow2,snapshot=off,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x3[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x33 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) c[K[Dco[K[D[Dcom[K[D[D[Dcomm[K[D[D[D[Dcommi[K[D[D[D[D[Dcommit[K[D[D[D[D[D[Dcommit [K[D[D[D[D[D[D[Dcommit d[K[D[D[D[D[D[D[D[Dcommit dr[K[D[D[D[D[D[D[D[D[Dcommit dri[K[D[D[D[D[D[D[D[D[D[Dcommit driv[K[D[D[D[D[D[D[D[D[D[D[Dcommit drive[K[D[D[D[D[D[D[D[D[D[D[D[Dcommit drive0[K -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) commit drive0 +(qemu) quit read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index c6f4eef215..76d7205460 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -58,12 +58,12 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig,if=none,id=drive0 -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 +(qemu) info block drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files @@ -79,11 +79,11 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.f Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit 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' @@ -103,23 +103,23 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: Lazy ref Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit === No medium === Testing: -drive if=floppy QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive if=ide,media=cdrom QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive if=scsi,media=cdrom QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: -drive if=scsi,media=cdrom: warning: bus=0,unit=0 is deprecated with this machine type -q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +quit Testing: -drive if=ide QEMU X.Y.Z monitor - type 'help' for more information @@ -137,11 +137,11 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive if=none,id=disk -device ide-cd,drive=disk QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive if=none,id=disk -device ide-drive,drive=disk QEMU X.Y.Z monitor - type 'help' for more information @@ -166,16 +166,16 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,if=floppy,readonly=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=ide,media=cdrom,readonly=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on: warning: bus=0,unit=0 is deprecated with this machine type -q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +quit Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on QEMU X.Y.Z monitor - type 'help' for more information @@ -185,19 +185,19 @@ QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on: warning: bus=0,unit=0 is deprecated with this machine type -q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +quit Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-cd,drive=disk QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk QEMU X.Y.Z monitor - type 'help' for more information @@ -211,97 +211,97 @@ QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed. Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit === Cache modes === Testing: -drive driver=null-co,cache=none QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=directsync QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=writeback QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=writethrough QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=unsafe QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive driver=null-co,cache=invalid_value QEMU_PROG: -drive driver=null-co,cache=invalid_value: invalid cache option Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -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 +(qemu) info block drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block f[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fi[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block file[K +(qemu) info block file file: TEST_DIR/t.qcow2 (file) Cache mode: writeback -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K +(qemu) info block backing backing: TEST_DIR/t.qcow2.base (qcow2, read-only) Cache mode: writeback, ignore flushes -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-f[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-file[K +(qemu) info block backing-file backing-file: TEST_DIR/t.qcow2.base (file, read-only) Cache mode: writeback, ignore flushes -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -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 +(qemu) info block drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Removable device: not locked, tray closed Cache mode: writethrough Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block f[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fi[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block file[K +(qemu) info block file file: TEST_DIR/t.qcow2 (file) Cache mode: writeback -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K +(qemu) info block backing backing: TEST_DIR/t.qcow2.base (qcow2, read-only) Cache mode: writeback, ignore flushes -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-f[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-file[K +(qemu) info block backing-file backing-file: TEST_DIR/t.qcow2.base (file, read-only) Cache mode: writeback, ignore flushes -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -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 +(qemu) info block drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Removable device: not locked, tray closed Cache mode: writeback, ignore flushes Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block f[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fi[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block file[K +(qemu) info block file file: TEST_DIR/t.qcow2 (file) Cache mode: writeback, ignore flushes -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K +(qemu) info block backing backing: TEST_DIR/t.qcow2.base (qcow2, read-only) Cache mode: writeback, ignore flushes -(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[D[D[D[D[D[D[D[D[D[Dinfo block [K[D[D[D[D[D[D[D[D[D[D[Dinfo block b[K[D[D[D[D[D[D[D[D[D[D[D[Dinfo block ba[K[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block bac[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block back[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backin[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-f[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fi[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-fil[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dinfo block backing-file[K +(qemu) info block backing-file backing-file: TEST_DIR/t.qcow2.base (file, read-only) Cache mode: writeback, ignore flushes -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults QEMU_PROG: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0: invalid cache option @@ -311,7 +311,7 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,cache=invalid_value,backing.file.filenam Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit === Leaving out required options === @@ -319,6 +319,9 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive driver=file QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name +Testing: -drive driver=file,filename= +QEMU_PROG: -drive driver=file,filename=: The 'file' block driver requires a file name + Testing: -drive driver=nbd QEMU_PROG: -drive driver=nbd: NBD server address missing @@ -405,15 +408,15 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,throttling.bps-total=-5: bps/iops/max va Testing: -drive file=TEST_DIR/t.qcow2,bps=0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,bps=1 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,bps=1000000000000000 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,bps=1000000000000001 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=1000000000000001: bps/iops/max values must be within [0, 1000000000000000] @@ -435,11 +438,11 @@ QEMU_PROG: -drive file.filename=foo:bar: Could not open 'foo:bar': No such file Testing: -hda file:TEST_DIR/t.qcow2 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=file:TEST_DIR/t.qcow2 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file.filename=file:TEST_DIR/t.qcow2 QEMU_PROG: -drive file.filename=file:TEST_DIR/t.qcow2: Could not open 'file:TEST_DIR/t.qcow2': No such file or directory @@ -451,78 +454,78 @@ wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file.filename=TEST_DIR/t.qcow2,driver=qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=file:TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=file:TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -snapshot QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Testing: -drive file=TEST_DIR/t.qcow2,snapshot=off,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x2[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x22 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x22 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,if=none,id=drive0 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io d[K[D[D[D[D[D[D[D[D[Dqemu-io dr[K[D[D[D[D[D[D[D[D[D[Dqemu-io dri[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io driv[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x3[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io drive0 "write -P 0x33 0 4k"[K +(qemu) qemu-io drive0 "write -P 0x33 0 4k" wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -(qemu) c[K[Dco[K[D[Dcom[K[D[D[Dcomm[K[D[D[D[Dcommi[K[D[D[D[D[Dcommit[K[D[D[D[D[D[Dcommit [K[D[D[D[D[D[D[Dcommit d[K[D[D[D[D[D[D[D[Dcommit dr[K[D[D[D[D[D[D[D[D[Dcommit dri[K[D[D[D[D[D[D[D[D[D[Dcommit driv[K[D[D[D[D[D[D[D[D[D[D[Dcommit drive[K[D[D[D[D[D[D[D[D[D[D[D[Dcommit drive0[K -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) commit drive0 +(qemu) quit read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/066 b/tests/qemu-iotests/066 index 364166d3b2..c2116a3088 100755 --- a/tests/qemu-iotests/066 +++ b/tests/qemu-iotests/066 @@ -42,16 +42,18 @@ _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# Intentionally create an unaligned image IMGOPTS="compat=1.1" -IMG_SIZE=64M +IMG_SIZE=$((64 * 1024 * 1024 + 512)) echo -echo "=== Testing snapshotting an image with zero clusters ===" +echo "=== Testing cluster discards ===" echo _make_test_img $IMG_SIZE -# Write some normal clusters, zero them (creating preallocated zero clusters) -# and discard those -$QEMU_IO -c "write 0 256k" -c "write -z 0 256k" -c "discard 0 256k" "$TEST_IMG" \ +# Write some normal clusters, zero some of them (creating preallocated +# zero clusters) and discard everything. Everything should now read as 0. +$QEMU_IO -c "write 0 256k" -c "write -z 0 256k" -c "write 64M 512" \ + -c "discard 0 $IMG_SIZE" -c "read -P 0 0 $IMG_SIZE" "$TEST_IMG" \ | _filter_qemu_io # Check the image (there shouldn't be any leaks) _check_test_img diff --git a/tests/qemu-iotests/066.out b/tests/qemu-iotests/066.out index 7bc9a107d5..7c1f31a1b1 100644 --- a/tests/qemu-iotests/066.out +++ b/tests/qemu-iotests/066.out @@ -1,13 +1,17 @@ QA output created by 066 -=== Testing snapshotting an image with zero clusters === +=== Testing cluster discards === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67109376 wrote 262144/262144 bytes at offset 0 256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 262144/262144 bytes at offset 0 256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -discard 262144/262144 bytes at offset 0 -256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 67108864 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 67109376/67109376 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 67109376/67109376 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. *** done diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068 index 68f6e825d9..9c1687d01d 100755 --- a/tests/qemu-iotests/068 +++ b/tests/qemu-iotests/068 @@ -62,11 +62,11 @@ esac # Give qemu some time to boot before saving the VM state bash -c 'sleep 1; echo -e "savevm 0\nquit"' |\ $QEMU $platform_parm -nographic -monitor stdio -serial none -hda "$TEST_IMG" |\ - _filter_qemu + _filter_qemu | _filter_hmp # Now try to continue from that VM state (this should just work) echo quit |\ $QEMU $platform_parm -nographic -monitor stdio -serial none -hda "$TEST_IMG" -loadvm 0 |\ - _filter_qemu + _filter_qemu | _filter_hmp # success, all done echo "*** done" diff --git a/tests/qemu-iotests/068.out b/tests/qemu-iotests/068.out index 84f19b4ffd..0fa5340c22 100644 --- a/tests/qemu-iotests/068.out +++ b/tests/qemu-iotests/068.out @@ -4,8 +4,8 @@ QA output created by 068 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) s[K[Dsa[K[D[Dsav[K[D[D[Dsave[K[D[D[D[Dsavev[K[D[D[D[D[Dsavevm[K[D[D[D[D[D[Dsavevm [K[D[D[D[D[D[D[Dsavevm 0[K -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) savevm 0 +(qemu) quit QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit *** done diff --git a/tests/qemu-iotests/073 b/tests/qemu-iotests/073 index ad37a617b2..40f85b18b9 100755 --- a/tests/qemu-iotests/073 +++ b/tests/qemu-iotests/073 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux CLUSTER_SIZE=64k diff --git a/tests/qemu-iotests/094 b/tests/qemu-iotests/094 index 0ba0b0c361..9aa01e3627 100755 --- a/tests/qemu-iotests/094 +++ b/tests/qemu-iotests/094 @@ -27,7 +27,14 @@ echo "QA output created by $seq" here="$PWD" status=1 # failure is the default! -trap "exit \$status" 0 1 2 3 15 +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img + rm -f "$TEST_DIR/source.$IMGFMT" +} + +trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks . ./common.rc @@ -73,8 +80,6 @@ _send_qemu_cmd $QEMU_HANDLE \ wait=1 _cleanup_qemu -_cleanup_test_img -rm -f "$TEST_DIR/source.$IMGFMT" # success, all done echo '*** done' diff --git a/tests/qemu-iotests/102 b/tests/qemu-iotests/102 index 64b4af9441..87db1bb1bf 100755 --- a/tests/qemu-iotests/102 +++ b/tests/qemu-iotests/102 @@ -25,11 +25,12 @@ seq=$(basename $0) echo "QA output created by $seq" here=$PWD -status=1 # failure is the default! +status=1 # failure is the default! _cleanup() { - _cleanup_test_img + _cleanup_qemu + _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index 927151a285..3b496a3918 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -29,6 +29,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu rm -f $TEST_IMG.src _cleanup_test_img } @@ -80,7 +81,8 @@ for fmt in qcow qcow2 qed vdi vmdk vpc; do # This first test should fail: The image format was probed, we may not # write an image header at the start of the image - run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_ERROR" + run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_ERROR" | + _filter_block_job_len $QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io @@ -103,7 +105,8 @@ for sample_img in empty.bochs iotest-dirtylog-10G-4M.vhdx parallels-v1 \ _make_test_img 64M bzcat "$SAMPLE_IMG_DIR/$sample_img.bz2" > "$TEST_IMG.src" - run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_ERROR" | _filter_block_job_offset + run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_ERROR" | + _filter_block_job_offset | _filter_block_job_len $QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io run_qemu "$TEST_IMG" "$TEST_IMG.src" "'format': 'raw'," "BLOCK_JOB_READY" diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 55fe536d56..dc02f9eefa 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -10,7 +10,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -31,7 +31,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -52,7 +52,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -73,7 +73,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -94,7 +94,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -115,7 +115,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -135,7 +135,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -155,7 +155,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -175,7 +175,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -195,7 +195,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} {"return": []} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/114 b/tests/qemu-iotests/114 index f110d4f65a..5b7dc5496c 100755 --- a/tests/qemu-iotests/114 +++ b/tests/qemu-iotests/114 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux diff --git a/tests/qemu-iotests/117 b/tests/qemu-iotests/117 index e955d52de3..6c83461182 100755 --- a/tests/qemu-iotests/117 +++ b/tests/qemu-iotests/117 @@ -29,6 +29,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out index 98814de5d6..9317d801ad 100644 --- a/tests/qemu-iotests/122.out +++ b/tests/qemu-iotests/122.out @@ -61,8 +61,8 @@ read 65536/65536 bytes at offset 4194304 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 65536/65536 bytes at offset 8388608 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: -B makes no sense when concatenating multiple input images -qemu-img: -B makes no sense when concatenating multiple input images +qemu-img: Having a backing file for the target makes no sense when concatenating multiple input images +qemu-img: Having a backing file for the target makes no sense when concatenating multiple input images === Compression with misaligned allocations and image sizes === diff --git a/tests/qemu-iotests/130 b/tests/qemu-iotests/130 index ecc8a5ba1b..e7e43de6d6 100755 --- a/tests/qemu-iotests/130 +++ b/tests/qemu-iotests/130 @@ -31,6 +31,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -42,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux qemu_comm_method="monitor" diff --git a/tests/qemu-iotests/130.out b/tests/qemu-iotests/130.out index ae95b5027a..93020c328e 100644 --- a/tests/qemu-iotests/130.out +++ b/tests/qemu-iotests/130.out @@ -9,14 +9,14 @@ virtual size: 64M (67108864 bytes) === HMP commit === QEMU X.Y.Z monitor - type 'help' for more information -(qemu) c[K[Dco[K[D[Dcom[K[D[D[Dcomm[K[D[D[D[Dcommi[K[D[D[D[D[Dcommit[K[D[D[D[D[D[Dcommit [K[D[D[D[D[D[D[Dcommit t[K[D[D[D[D[D[D[D[Dcommit te[K[D[D[D[D[D[D[D[D[Dcommit tes[K[D[D[D[D[D[D[D[D[D[Dcommit test[K[D[D[D[D[D[D[D[D[D[D[Dcommit testd[K[D[D[D[D[D[D[D[D[D[D[D[Dcommit testdi[K[D[D[D[D[D[D[D[D[D[D[D[D[Dcommit testdis[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dcommit testdisk[K +(qemu) commit testdisk (qemu) image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64M (67108864 bytes) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.orig backing_fmt=raw QEMU X.Y.Z monitor - type 'help' for more information -(qemu) c[K[Dco[K[D[Dcom[K[D[D[Dcomm[K[D[D[D[Dcommi[K[D[D[D[D[Dcommit[K[D[D[D[D[D[Dcommit [K[D[D[D[D[D[D[Dcommit t[K[D[D[D[D[D[D[D[Dcommit te[K[D[D[D[D[D[D[D[D[Dcommit tes[K[D[D[D[D[D[D[D[D[D[Dcommit test[K[D[D[D[D[D[D[D[D[D[D[Dcommit testd[K[D[D[D[D[D[D[D[D[D[D[D[Dcommit testdi[K[D[D[D[D[D[D[D[D[D[D[D[D[Dcommit testdis[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dcommit testdisk[K +(qemu) commit testdisk (qemu) image: TEST_DIR/t.IMGFMT file format: IMGFMT diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134 index af618b8817..acce946e75 100755 --- a/tests/qemu-iotests/134 +++ b/tests/qemu-iotests/134 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux diff --git a/tests/qemu-iotests/140 b/tests/qemu-iotests/140 index 49f9df4eb0..8c80a5a866 100755 --- a/tests/qemu-iotests/140 +++ b/tests/qemu-iotests/140 @@ -33,6 +33,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu _cleanup_test_img rm -f "$TEST_DIR/nbd" } diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index 27fb1cc92c..40a3405968 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -29,6 +29,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu _cleanup_test_img rm -f "$TEST_DIR/{b,m,o}.$IMGFMT" } diff --git a/tests/qemu-iotests/142 b/tests/qemu-iotests/142 index 29c0606bd7..9a5b713256 100755 --- a/tests/qemu-iotests/142 +++ b/tests/qemu-iotests/142 @@ -62,7 +62,7 @@ function do_run_qemu() function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_hmp } size=128M diff --git a/tests/qemu-iotests/142.out b/tests/qemu-iotests/142.out index 600beca8fb..3667e38def 100644 --- a/tests/qemu-iotests/142.out +++ b/tests/qemu-iotests/142.out @@ -7,23 +7,23 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ Testing: -drive file=TEST_DIR/t.qcow2,cache=none QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=directsync QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,cache=invalid_value QEMU_PROG: -drive file=TEST_DIR/t.qcow2,cache=invalid_value: invalid cache option diff --git a/tests/qemu-iotests/143 b/tests/qemu-iotests/143 index ec4ef2221a..5ff1944507 100755 --- a/tests/qemu-iotests/143 +++ b/tests/qemu-iotests/143 @@ -29,6 +29,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu rm -f "$TEST_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/145 b/tests/qemu-iotests/145 index 1eca0e8990..e6c6bc4a4f 100755 --- a/tests/qemu-iotests/145 +++ b/tests/qemu-iotests/145 @@ -43,7 +43,8 @@ _supported_proto generic _supported_os Linux _make_test_img 1M -echo quit | $QEMU -nographic -hda "$TEST_IMG" -incoming 'exec:true' -snapshot -serial none -monitor stdio | _filter_qemu +echo quit | $QEMU -nographic -hda "$TEST_IMG" -incoming 'exec:true' -snapshot -serial none -monitor stdio | + _filter_qemu | _filter_hmp # success, all done echo "*** done" diff --git a/tests/qemu-iotests/145.out b/tests/qemu-iotests/145.out index 75b5c8ac36..9a90009e44 100644 --- a/tests/qemu-iotests/145.out +++ b/tests/qemu-iotests/145.out @@ -1,5 +1,5 @@ QA output created by 145 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) quit *** done diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index cc95ff1f98..d799b73e1e 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -37,6 +37,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu rm -f "$TEST_IMG{,.target}{,.backing,.overlay}" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -48,6 +49,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 qed _supported_proto generic +_unsupported_proto vxhs _supported_os Linux # Create source disk diff --git a/tests/qemu-iotests/158 b/tests/qemu-iotests/158 index a6cdd6d8cf..ef8d70f109 100755 --- a/tests/qemu-iotests/158 +++ b/tests/qemu-iotests/158 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 new file mode 100755 index 0000000000..e969a2a94f --- /dev/null +++ b/tests/qemu-iotests/181 @@ -0,0 +1,119 @@ +#!/bin/bash +# +# Test postcopy live migration with shared storage +# +# Copyright (C) 2017 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +MIG_SOCKET="${TEST_DIR}/migrate" + +_cleanup() +{ + rm -f "${MIG_SOCKET}" + _cleanup_test_img + _cleanup_qemu +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt generic +_supported_proto generic +_supported_os Linux + +size=64M +_make_test_img $size + +echo +echo === Starting VMs === +echo + +qemu_comm_method="monitor" + +_launch_qemu \ + -drive file="${TEST_IMG}",cache=${CACHEMODE},driver=$IMGFMT,id=disk +src=$QEMU_HANDLE + +_launch_qemu \ + -drive file="${TEST_IMG}",cache=${CACHEMODE},driver=$IMGFMT,id=disk \ + -incoming "unix:${MIG_SOCKET}" +dest=$QEMU_HANDLE + +echo +echo === Write something on the source === +echo + +silent= +_send_qemu_cmd $src 'qemu-io disk "write -P 0x55 0 64k"' "(qemu)" +_send_qemu_cmd $src "" "ops/sec" +_send_qemu_cmd $src 'qemu-io disk "read -P 0x55 0 64k"' "(qemu)" +_send_qemu_cmd $src "" "ops/sec" + +echo +echo === Do postcopy migration to destination === +echo + +# Slow down migration so much that it definitely won't finish before we can +# switch to postcopy +silent=yes +_send_qemu_cmd $src 'migrate_set_speed 4k' "(qemu)" +_send_qemu_cmd $src 'migrate_set_capability postcopy-ram on' "(qemu)" +_send_qemu_cmd $src "migrate -d unix:${MIG_SOCKET}" "(qemu)" +_send_qemu_cmd $src 'migrate_start_postcopy' "(qemu)" + +QEMU_COMM_TIMEOUT=1 qemu_cmd_repeat=10 silent=yes \ + _send_qemu_cmd $src "info migrate" "completed\|failed" +silent=yes _send_qemu_cmd $src "" "(qemu)" + +echo +echo === Do some I/O on the destination === +echo + +# It is important that we use the BlockBackend of the guest device here instead +# of the node name, which would create a new BlockBackend and not test whether +# the guest has the necessary permissions to access the image now +silent= +_send_qemu_cmd $dest 'qemu-io disk "read -P 0x55 0 64k"' "(qemu)" +_send_qemu_cmd $dest "" "ops/sec" +_send_qemu_cmd $dest 'qemu-io disk "write -P 0x66 1M 64k"' "(qemu)" +_send_qemu_cmd $dest "" "ops/sec" + +echo +echo === Shut down and check image === +echo + +_send_qemu_cmd $src 'quit' "" +_send_qemu_cmd $dest 'quit' "" +wait=1 _cleanup_qemu + +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/181.out b/tests/qemu-iotests/181.out new file mode 100644 index 0000000000..6534ba2a76 --- /dev/null +++ b/tests/qemu-iotests/181.out @@ -0,0 +1,38 @@ +QA output created by 181 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + +=== Starting VMs === + + +=== Write something on the source === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io disk "write -P 0x55 0 64k" +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) +(qemu) qemu-io disk "read -P 0x55 0 64k" +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Do postcopy migration to destination === + + +=== Do some I/O on the destination === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io disk "read -P 0x55 0 64k" +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) +(qemu) qemu-io disk "write -P 0x66 1M 64k" +wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Shut down and check image === + +(qemu) quit +(qemu) +(qemu) quit +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 4d5650d7c8..f2a7199c4b 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -86,7 +86,8 @@ s/ .*//p 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 + # Populate $tmp.list with all tests + awk '/^[0-9]{3,}/ {print $1}' "${source_iotests}/group" > $tmp.list 2>/dev/null group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ s/ .*//p }'` @@ -138,7 +139,7 @@ common options -v verbose -d debug -check options +image format options -raw test raw (default) -bochs test bochs -cloop test cloop @@ -150,13 +151,18 @@ check options -vpc test vpc -vhdx test vhdx -vmdk test vmdk + -luks test luks + +image protocol options -file test file (default) -rbd test rbd -sheepdog test sheepdog -nbd test nbd -ssh test ssh -nfs test nfs - -luks test luks + -vxhs test vxhs + +other options -xdiff graphical mode diff -nocache use O_DIRECT on backing file -misalign misalign memory allocations @@ -260,6 +266,11 @@ testlist options xpand=false ;; + -vxhs) + IMGPROTO=vxhs + xpand=false + ;; + -ssh) IMGPROTO=ssh xpand=false diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index 55527aac87..d1b45f5447 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -75,18 +75,12 @@ _fatal() exit 1 } -export PERL_PROG="`set_prog_path perl`" -[ "$PERL_PROG" = "" ] && _fatal "perl not found" - export AWK_PROG="`set_prog_path awk`" [ "$AWK_PROG" = "" ] && _fatal "awk not found" export SED_PROG="`set_prog_path sed`" [ "$SED_PROG" = "" ] && _fatal "sed not found" -export BC_PROG="`set_prog_path bc`" -[ "$BC_PROG" = "" ] && _fatal "bc not found" - export PS_ALL_FLAGS="-ef" if [ -z "$QEMU_PROG" ]; then @@ -105,6 +99,10 @@ if [ -z "$QEMU_NBD_PROG" ]; then export QEMU_NBD_PROG="`set_prog_path qemu-nbd`" fi +if [ -z "$QEMU_VXHS_PROG" ]; then + export QEMU_VXHS_PROG="`set_prog_path qnio_server`" +fi + _qemu_wrapper() { ( @@ -156,10 +154,19 @@ _qemu_nbd_wrapper() ) } +_qemu_vxhs_wrapper() +{ + ( + echo $BASHPID > "${TEST_DIR}/qemu-vxhs.pid" + exec "$QEMU_VXHS_PROG" $QEMU_VXHS_OPTIONS "$@" + ) +} + export QEMU=_qemu_wrapper export QEMU_IMG=_qemu_img_wrapper export QEMU_IO=_qemu_io_wrapper export QEMU_NBD=_qemu_nbd_wrapper +export QEMU_VXHS=_qemu_vxhs_wrapper QEMU_IMG_EXTRA_ARGS= if [ "$IMGOPTSSYNTAX" = "true" ]; then @@ -210,23 +217,5 @@ fi export SAMPLE_IMG_DIR -_readlink() -{ - if [ $# -ne 1 ]; then - echo "Usage: _readlink filename" 1>&2 - exit 1 - fi - - perl -e "\$in=\"$1\";" -e ' - $lnk = readlink($in); - if ($lnk =~ m!^/.*!) { - print "$lnk\n"; - } - else { - chomp($dir = `dirname $in`); - print "$dir/$lnk\n"; - }' -} - # make sure this script returns success true diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 104001358b..f58548dc44 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -86,12 +86,25 @@ _filter_qmp() -e ' QMP_VERSION' } +# readline makes HMP command strings so long that git complains +_filter_hmp() +{ + sed -e $'s/^\\((qemu) \\)\\?.*\e\\[D/\\1/g' \ + -e $'s/\e\\[K//g' +} + # replace block job offset _filter_block_job_offset() { sed -e 's/, "offset": [0-9]\+,/, "offset": OFFSET,/' } +# replace block job len +_filter_block_job_len() +{ + sed -e 's/, "len": [0-9]\+,/, "len": LEN,/g' +} + # replace driver-specific options in the "Formatting..." line _filter_img_create() { @@ -122,6 +135,7 @@ _filter_img_info() -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \ + -e 's#json.*vdisk-id.*vxhs"}}#TEST_DIR/t.IMGFMT#' \ -e "/encrypted: yes/d" \ -e "/cluster_size: [0-9]\\+/d" \ -e "/table_size: [0-9]\\+/d" \ diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu index 42787896af..7a78a00999 100644 --- a/tests/qemu-iotests/common.qemu +++ b/tests/qemu-iotests/common.qemu @@ -59,7 +59,7 @@ function _timed_wait_for() do if [ -z "${silent}" ]; then echo "${resp}" | _filter_testdir | _filter_qemu \ - | _filter_qemu_io | _filter_qmp + | _filter_qemu_io | _filter_qmp | _filter_hmp fi grep -q "${*}" < <(echo ${resp}) if [ $? -eq 0 ]; then @@ -217,7 +217,7 @@ function _cleanup_qemu() if [ -n "${wait}" ]; then cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \ - | _filter_qemu_io | _filter_qmp + | _filter_qemu_io | _filter_qmp | _filter_hmp fi rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}" eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 7d4781d4ad..9fd3130bde 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -85,6 +85,9 @@ else elif [ "$IMGPROTO" = "nfs" ]; then TEST_DIR="nfs://127.0.0.1/$TEST_DIR" TEST_IMG=$TEST_DIR/t.$IMGFMT + elif [ "$IMGPROTO" = "vxhs" ]; then + TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT + TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT" else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi @@ -168,9 +171,17 @@ _make_test_img() # Start an NBD server on the image file, which is what we'll be talking to if [ $IMGPROTO = "nbd" ]; then - eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE >/dev/null &" + # Pass a sufficiently high number to -e that should be enough for all + # tests + eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT -e 42 $TEST_IMG_FILE >/dev/null &" sleep 1 # FIXME: qemu-nbd needs to be listening before we continue fi + + # Start QNIO server on image directory for vxhs protocol + if [ $IMGPROTO = "vxhs" ]; then + eval "$QEMU_VXHS -d $TEST_DIR > /dev/null &" + sleep 1 # Wait for server to come up. + fi } _rm_test_img() @@ -197,6 +208,16 @@ _cleanup_test_img() fi rm -f "$TEST_IMG_FILE" ;; + vxhs) + if [ -f "${TEST_DIR}/qemu-vxhs.pid" ]; then + local QEMU_VXHS_PID + read QEMU_VXHS_PID < "${TEST_DIR}/qemu-vxhs.pid" + kill ${QEMU_VXHS_PID} >/dev/null 2>&1 + rm -f "${TEST_DIR}/qemu-vxhs.pid" + fi + rm -f "$TEST_IMG_FILE" + ;; + file) _rm_test_img "$TEST_DIR/t.$IMGFMT" _rm_test_img "$TEST_DIR/t.$IMGFMT.orig" diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 43142ddfcf..893962d41e 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -169,3 +169,4 @@ 174 auto 175 auto quick 176 rw auto backing +181 rw auto migration diff --git a/tests/qom-test.c b/tests/qom-test.c index d48f890e84..ab0595dc75 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -107,46 +107,22 @@ static void test_machine(gconstpointer data) g_free((void *)machine); } -static void add_machine_test_cases(void) +static void add_machine_test_case(const char *mname) { const char *arch = qtest_get_arch(); - QDict *response, *minfo; - QList *list; - const QListEntry *p; - QObject *qobj; - QString *qstr; - const char *mname; - qtest_start("-machine none"); - response = qmp("{ 'execute': 'query-machines' }"); - g_assert(response); - list = qdict_get_qlist(response, "return"); - g_assert(list); - - for (p = qlist_first(list); p; p = qlist_next(p)) { - minfo = qobject_to_qdict(qlist_entry_obj(p)); - g_assert(minfo); - qobj = qdict_get(minfo, "name"); - g_assert(qobj); - qstr = qobject_to_qstring(qobj); - g_assert(qstr); - mname = qstring_get_str(qstr); - if (!is_blacklisted(arch, mname)) { - char *path = g_strdup_printf("qom/%s", mname); - qtest_add_data_func(path, g_strdup(mname), test_machine); - g_free(path); - } + if (!is_blacklisted(arch, mname)) { + char *path = g_strdup_printf("qom/%s", mname); + qtest_add_data_func(path, g_strdup(mname), test_machine); + g_free(path); } - - qtest_end(); - QDECREF(response); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - add_machine_test_cases(); + qtest_cb_for_every_machine(add_machine_test_case); return g_test_run(); } diff --git a/tests/test-crypto-block.c b/tests/test-crypto-block.c index 1957a86743..85e6603d59 100644 --- a/tests/test-crypto-block.c +++ b/tests/test-crypto-block.c @@ -187,11 +187,11 @@ static struct QCryptoBlockTestData { static ssize_t test_block_read_func(QCryptoBlock *block, + void *opaque, size_t offset, uint8_t *buf, size_t buflen, - Error **errp, - void *opaque) + Error **errp) { Buffer *header = opaque; @@ -204,9 +204,9 @@ static ssize_t test_block_read_func(QCryptoBlock *block, static ssize_t test_block_init_func(QCryptoBlock *block, + void *opaque, size_t headerlen, - Error **errp, - void *opaque) + Error **errp) { Buffer *header = opaque; @@ -219,11 +219,11 @@ static ssize_t test_block_init_func(QCryptoBlock *block, static ssize_t test_block_write_func(QCryptoBlock *block, + void *opaque, size_t offset, const uint8_t *buf, size_t buflen, - Error **errp, - void *opaque) + Error **errp) { Buffer *header = opaque; diff --git a/tests/test-hmp.c b/tests/test-hmp.c new file mode 100644 index 0000000000..99e35ec15a --- /dev/null +++ b/tests/test-hmp.c @@ -0,0 +1,161 @@ +/* + * Test HMP commands. + * + * Copyright (c) 2017 Red Hat Inc. + * + * Author: + * Thomas Huth <thuth@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 + * or later. See the COPYING file in the top-level directory. + * + * This test calls some HMP commands for all machines that the current + * QEMU binary provides, to check whether they terminate successfully + * (i.e. do not crash QEMU). + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +static int verbose; + +static const char *hmp_cmds[] = { + "boot_set ndc", + "chardev-add null,id=testchardev1", + "chardev-remove testchardev1", + "commit all", + "cpu-add 1", + "cpu 0", + "device_add ?", + "device_add usb-mouse,id=mouse1", + "mouse_button 7", + "mouse_move 10 10", + "mouse_button 0", + "device_del mouse1", + "dump-guest-memory /dev/null 0 4096", + "gdbserver", + "host_net_add user id=net0", + "hostfwd_add tcp::43210-:43210", + "hostfwd_remove tcp::43210-:43210", + "host_net_remove 0 net0", + "i /w 0", + "log all", + "log none", + "memsave 0 4096 \"/dev/null\"", + "migrate_set_cache_size 1", + "migrate_set_downtime 1", + "migrate_set_speed 1", + "netdev_add user,id=net1", + "set_link net1 off", + "set_link net1 on", + "netdev_del net1", + "nmi", + "o /w 0 0x1234", + "object_add memory-backend-ram,id=mem1,size=256M", + "object_del mem1", + "pmemsave 0 4096 \"/dev/null\"", + "p $pc + 8", + "qom-list /", + "qom-set /machine initrd test", + "screendump /dev/null", + "sendkey x", + "singlestep on", + "wavcapture /dev/null", + "stopcapture 0", + "sum 0 512", + "x /8i 0x100", + "xp /16x 0", + NULL +}; + +/* Run through the list of pre-defined commands */ +static void test_commands(void) +{ + char *response; + int i; + + for (i = 0; hmp_cmds[i] != NULL; i++) { + if (verbose) { + fprintf(stderr, "\t%s\n", hmp_cmds[i]); + } + response = hmp(hmp_cmds[i]); + g_free(response); + } + +} + +/* Run through all info commands and call them blindly (without arguments) */ +static void test_info_commands(void) +{ + char *resp, *info, *info_buf, *endp; + + info_buf = info = hmp("help info"); + + while (*info) { + /* Extract the info command, ignore parameters and description */ + g_assert(strncmp(info, "info ", 5) == 0); + endp = strchr(&info[5], ' '); + g_assert(endp != NULL); + *endp = '\0'; + /* Now run the info command */ + if (verbose) { + fprintf(stderr, "\t%s\n", info); + } + resp = hmp(info); + g_free(resp); + /* And move forward to the next line */ + info = strchr(endp + 1, '\n'); + if (!info) { + break; + } + info += 1; + } + + g_free(info_buf); +} + +static void test_machine(gconstpointer data) +{ + const char *machine = data; + char *args; + + args = g_strdup_printf("-S -M %s", machine); + qtest_start(args); + + test_info_commands(); + test_commands(); + + qtest_end(); + g_free(args); + g_free((void *)data); +} + +static void add_machine_test_case(const char *mname) +{ + char *path; + + /* Ignore blacklisted machines that have known problems */ + if (!strcmp("puv3", mname) || !strcmp("tricore_testboard", mname) || + !strcmp("xenfv", mname) || !strcmp("xenpv", mname)) { + return; + } + + path = g_strdup_printf("hmp/%s", mname); + qtest_add_data_func(path, g_strdup(mname), test_machine); + g_free(path); +} + +int main(int argc, char **argv) +{ + char *v_env = getenv("V"); + + if (v_env && *v_env >= '2') { + verbose = true; + } + + g_test_init(&argc, &argv, NULL); + + qtest_cb_for_every_machine(add_machine_test_case); + + return g_test_run(); +} diff --git a/tests/test-replication.c b/tests/test-replication.c index fac2da3f58..3016c6f2e0 100644 --- a/tests/test-replication.c +++ b/tests/test-replication.c @@ -144,18 +144,18 @@ static void prepare_imgs(void) /* Primary */ bdrv_img_create(p_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, - BDRV_O_RDWR, &local_err, true); + BDRV_O_RDWR, true, &local_err); g_assert(!local_err); /* Secondary */ bdrv_img_create(s_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, - BDRV_O_RDWR, &local_err, true); + BDRV_O_RDWR, true, &local_err); g_assert(!local_err); bdrv_img_create(s_active_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, - BDRV_O_RDWR, &local_err, true); + BDRV_O_RDWR, true, &local_err); g_assert(!local_err); bdrv_img_create(s_hidden_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, - BDRV_O_RDWR, &local_err, true); + BDRV_O_RDWR, true, &local_err); g_assert(!local_err); } diff --git a/tests/test-throttle.c b/tests/test-throttle.c index bd7c501b2e..a9201b1fea 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -205,8 +205,8 @@ static void test_config_functions(void) 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_TOTAL].max = 0; + orig_cfg.buckets[THROTTLE_BPS_READ].max = 56; orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120; orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150; @@ -246,8 +246,8 @@ static void test_config_functions(void) 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_TOTAL].max == 0); + g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max == 56); g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120); g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150); diff --git a/trace-events b/trace-events index b07a09ba95..e582d6315d 100644 --- a/trace-events +++ b/trace-events @@ -48,22 +48,6 @@ spice_vmc_register_interface(void *scd) "spice vmc registered interface %p" spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p" spice_vmc_event(int event) "spice vmc event %d" -# xen-hvm.c -xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx" -xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "%#"PRIx64" size %#lx, log_dirty %i" -handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" -handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" -handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" -cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" -cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=%#"PRIx64" port=%#"PRIx64" size=%d" -cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=%#"PRIx64" port=%#"PRIx64" size=%d" -cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d" - -# xen-mapcache.c -xen_map_cache(uint64_t phys_addr) "want %#"PRIx64 -xen_remap_bucket(uint64_t index) "index %#"PRIx64 -xen_map_cache_return(void* ptr) "%p" - # monitor.c monitor_protocol_event_handler(uint32_t event, void *qdict) "event=%d data=%p" monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p" diff --git a/ui/cocoa.m b/ui/cocoa.m index c81f7b6183..207555edf7 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -45,7 +45,36 @@ #ifndef MAC_OS_X_VERSION_10_10 #define MAC_OS_X_VERSION_10_10 101000 #endif +#ifndef MAC_OS_X_VERSION_10_12 +#define MAC_OS_X_VERSION_10_12 101200 +#endif +/* macOS 10.12 deprecated many constants, #define the new names for older SDKs */ +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 +#define NSEventMaskAny NSAnyEventMask +#define NSEventModifierFlagCommand NSCommandKeyMask +#define NSEventModifierFlagControl NSControlKeyMask +#define NSEventModifierFlagOption NSAlternateKeyMask +#define NSEventTypeFlagsChanged NSFlagsChanged +#define NSEventTypeKeyUp NSKeyUp +#define NSEventTypeKeyDown NSKeyDown +#define NSEventTypeMouseMoved NSMouseMoved +#define NSEventTypeLeftMouseDown NSLeftMouseDown +#define NSEventTypeRightMouseDown NSRightMouseDown +#define NSEventTypeOtherMouseDown NSOtherMouseDown +#define NSEventTypeLeftMouseDragged NSLeftMouseDragged +#define NSEventTypeRightMouseDragged NSRightMouseDragged +#define NSEventTypeOtherMouseDragged NSOtherMouseDragged +#define NSEventTypeLeftMouseUp NSLeftMouseUp +#define NSEventTypeRightMouseUp NSRightMouseUp +#define NSEventTypeOtherMouseUp NSOtherMouseUp +#define NSEventTypeScrollWheel NSScrollWheel +#define NSTextAlignmentCenter NSCenterTextAlignment +#define NSWindowStyleMaskBorderless NSBorderlessWindowMask +#define NSWindowStyleMaskClosable NSClosableWindowMask +#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask +#define NSWindowStyleMaskTitled NSTitledWindowMask +#endif //#define DEBUG @@ -494,7 +523,7 @@ QemuCocoaView *cocoaView; } else { [NSMenu setMenuBarVisible:NO]; fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] - styleMask:NSBorderlessWindowMask + styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:NO]; [fullScreenWindow setAcceptsMouseMovedEvents: YES]; @@ -517,7 +546,7 @@ QemuCocoaView *cocoaView; NSPoint p = [event locationInWindow]; switch ([event type]) { - case NSFlagsChanged: + case NSEventTypeFlagsChanged: keycode = cocoa_keycode_to_qemu([event keyCode]); if ((keycode == Q_KEY_CODE_META_L || keycode == Q_KEY_CODE_META_R) @@ -544,15 +573,15 @@ QemuCocoaView *cocoaView; } // release Mouse grab when pressing ctrl+alt - if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { + if (([event modifierFlags] & NSEventModifierFlagControl) && ([event modifierFlags] & NSEventModifierFlagOption)) { [self ungrabMouse]; } break; - case NSKeyDown: + case NSEventTypeKeyDown: keycode = cocoa_keycode_to_qemu([event keyCode]); // forward command key combos to the host UI unless the mouse is grabbed - if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) { + if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) { [NSApp sendEvent:event]; return; } @@ -560,7 +589,7 @@ QemuCocoaView *cocoaView; // default // handle control + alt Key Combos (ctrl+alt is reserved for QEMU) - if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { + if (([event modifierFlags] & NSEventModifierFlagControl) && ([event modifierFlags] & NSEventModifierFlagOption)) { switch (keycode) { // enable graphic console @@ -609,12 +638,12 @@ QemuCocoaView *cocoaView; kbd_put_keysym(keysym); } break; - case NSKeyUp: + case NSEventTypeKeyUp: keycode = cocoa_keycode_to_qemu([event keyCode]); // don't pass the guest a spurious key-up if we treated this // command-key combo as a host UI action - if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) { + if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) { return; } @@ -622,7 +651,7 @@ QemuCocoaView *cocoaView; qemu_input_event_send_key_qcode(dcl->con, keycode, false); } break; - case NSMouseMoved: + case NSEventTypeMouseMoved: if (isAbsoluteEnabled) { if (![self screenContainsPoint:p] || ![[self window] isKeyWindow]) { if (isMouseGrabbed) { @@ -636,39 +665,39 @@ QemuCocoaView *cocoaView; } mouse_event = true; break; - case NSLeftMouseDown: - if ([event modifierFlags] & NSCommandKeyMask) { + case NSEventTypeLeftMouseDown: + if ([event modifierFlags] & NSEventModifierFlagCommand) { buttons |= MOUSE_EVENT_RBUTTON; } else { buttons |= MOUSE_EVENT_LBUTTON; } mouse_event = true; break; - case NSRightMouseDown: + case NSEventTypeRightMouseDown: buttons |= MOUSE_EVENT_RBUTTON; mouse_event = true; break; - case NSOtherMouseDown: + case NSEventTypeOtherMouseDown: buttons |= MOUSE_EVENT_MBUTTON; mouse_event = true; break; - case NSLeftMouseDragged: - if ([event modifierFlags] & NSCommandKeyMask) { + case NSEventTypeLeftMouseDragged: + if ([event modifierFlags] & NSEventModifierFlagCommand) { buttons |= MOUSE_EVENT_RBUTTON; } else { buttons |= MOUSE_EVENT_LBUTTON; } mouse_event = true; break; - case NSRightMouseDragged: + case NSEventTypeRightMouseDragged: buttons |= MOUSE_EVENT_RBUTTON; mouse_event = true; break; - case NSOtherMouseDragged: + case NSEventTypeOtherMouseDragged: buttons |= MOUSE_EVENT_MBUTTON; mouse_event = true; break; - case NSLeftMouseUp: + case NSEventTypeLeftMouseUp: mouse_event = true; if (!isMouseGrabbed && [self screenContainsPoint:p]) { if([[self window] isKeyWindow]) { @@ -676,13 +705,13 @@ QemuCocoaView *cocoaView; } } break; - case NSRightMouseUp: + case NSEventTypeRightMouseUp: mouse_event = true; break; - case NSOtherMouseUp: + case NSEventTypeOtherMouseUp: mouse_event = true; break; - case NSScrollWheel: + case NSEventTypeScrollWheel: if (isMouseGrabbed) { buttons |= ([event deltaY] < 0) ? MOUSE_EVENT_WHEELUP : MOUSE_EVENT_WHEELDN; @@ -847,7 +876,7 @@ QemuCocoaView *cocoaView; // create a window normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame] - styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask + styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskClosable backing:NSBackingStoreBuffered defer:NO]; if(!normalWindow) { fprintf(stderr, "(cocoa) can't create window\n"); @@ -1152,8 +1181,8 @@ QemuCocoaView *cocoaView; int x = 0, y = 0, about_width = 400, about_height = 200; NSRect window_rect = NSMakeRect(x, y, about_width, about_height); about_window = [[NSWindow alloc] initWithContentRect:window_rect - styleMask:NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable backing:NSBackingStoreBuffered defer:NO]; [about_window setTitle: @"About"]; @@ -1192,7 +1221,7 @@ QemuCocoaView *cocoaView; [name_label setEditable: NO]; [name_label setBezeled: NO]; [name_label setDrawsBackground: NO]; - [name_label setAlignment: NSCenterTextAlignment]; + [name_label setAlignment: NSTextAlignmentCenter]; NSString *qemu_name = [[NSString alloc] initWithCString: gArgv[0] encoding: NSASCIIStringEncoding]; qemu_name = [qemu_name lastPathComponent]; @@ -1208,7 +1237,7 @@ QemuCocoaView *cocoaView; version_rect]; [version_label setEditable: NO]; [version_label setBezeled: NO]; - [version_label setAlignment: NSCenterTextAlignment]; + [version_label setAlignment: NSTextAlignmentCenter]; [version_label setDrawsBackground: NO]; /* Create the version string*/ @@ -1228,7 +1257,7 @@ QemuCocoaView *cocoaView; [copyright_label setEditable: NO]; [copyright_label setBezeled: NO]; [copyright_label setDrawsBackground: NO]; - [copyright_label setAlignment: NSCenterTextAlignment]; + [copyright_label setAlignment: NSTextAlignmentCenter]; [copyright_label setStringValue: [NSString stringWithFormat: @"%s", QEMU_COPYRIGHT]]; [superView addSubview: copyright_label]; @@ -1285,7 +1314,7 @@ int main (int argc, const char * argv[]) { [menu addItem:[NSMenuItem separatorItem]]; //Separator [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others - [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All [menu addItem:[NSMenuItem separatorItem]]; //Separator [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"]; @@ -1399,7 +1428,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl) NSEvent *event; distantPast = [NSDate distantPast]; do { - event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast + event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast inMode: NSDefaultRunLoopMode dequeue:YES]; if (event != nil) { [cocoaView handleEvent:event]; diff --git a/ui/console.c b/ui/console.c index 419b098c11..189eecfd29 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1410,6 +1410,8 @@ void register_displaychangelistener(DisplayChangeListener *dcl) static DisplaySurface *dummy; QemuConsole *con; + assert(!dcl->ds); + if (dcl->ops->dpy_gl_ctx_create) { /* display has opengl support */ assert(dcl->con); @@ -1538,6 +1540,8 @@ void dpy_gfx_replace_surface(QemuConsole *con, DisplaySurface *old_surface = con->surface; DisplayChangeListener *dcl; + assert(old_surface != surface); + con->surface = surface; QLIST_FOREACH(dcl, &s->listeners, next) { if (con != (dcl->con ? dcl->con : active_console)) { diff --git a/util/bitmap.c b/util/bitmap.c index c1a84ca5e3..efced9a7d8 100644 --- a/util/bitmap.c +++ b/util/bitmap.c @@ -287,6 +287,17 @@ bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr) return dirty != 0; } +void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, + long nr) +{ + while (nr > 0) { + *dst = atomic_xchg(src, 0); + dst++; + src++; + nr -= BITS_PER_LONG; + } +} + #define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) /** diff --git a/util/error.c b/util/error.c index 9c40b1f458..020b86b9f0 100644 --- a/util/error.c +++ b/util/error.c @@ -134,6 +134,7 @@ void error_vprepend(Error **errp, const char *fmt, va_list ap) newmsg = g_string_new(NULL); g_string_vprintf(newmsg, fmt, ap); g_string_append(newmsg, (*errp)->msg); + g_free((*errp)->msg); (*errp)->msg = g_string_free(newmsg, 0); } diff --git a/util/qemu-config.c b/util/qemu-config.c index 5527100a01..405dd1a1d7 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -227,6 +227,12 @@ static QemuOptsList machine_opts = { .name = "dea-key-wrap", .type = QEMU_OPT_BOOL, .help = "enable/disable DEA key wrapping using the CPACF wrapping key", + },{ + .name = "loadparm", + .type = QEMU_OPT_STRING, + .help = "Up to 8 chars in set of [A-Za-z0-9. ](lower case chars" + " converted to upper case) to pass to machine" + " loader, boot manager, and guest kernel", }, { /* End of list */ } } diff --git a/util/qemu-progress.c b/util/qemu-progress.c index f745233763..3c2223c1a2 100644 --- a/util/qemu-progress.c +++ b/util/qemu-progress.c @@ -88,6 +88,9 @@ static void progress_dummy_init(void) action.sa_handler = sigusr_print; action.sa_flags = 0; sigaction(SIGUSR1, &action, NULL); +#ifdef SIGINFO + sigaction(SIGINFO, &action, NULL); +#endif /* * SIGUSR1 is SIG_IPI and gets blocked in qemu_init_main_loop(). In the diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 21442c30dc..8188d9a8d7 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -427,8 +427,9 @@ static struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr, * function succeeds, callback will be called when the connection * completes, with the file descriptor on success, or -1 on error. */ -int inet_connect_saddr(InetSocketAddress *saddr, Error **errp, - NonBlockingConnectHandler *callback, void *opaque) +int inet_connect_saddr(InetSocketAddress *saddr, + NonBlockingConnectHandler *callback, void *opaque, + Error **errp) { Error *local_err = NULL; struct addrinfo *res, *e; @@ -659,7 +660,7 @@ int inet_connect(const char *str, Error **errp) addr = inet_parse(str, errp); if (addr != NULL) { - sock = inet_connect_saddr(addr, errp, NULL, NULL); + sock = inet_connect_saddr(addr, NULL, NULL, errp); qapi_free_InetSocketAddress(addr); } return sock; @@ -727,9 +728,10 @@ static int vsock_connect_addr(const struct sockaddr_vm *svm, bool *in_progress, return sock; } -static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp, +static int vsock_connect_saddr(VsockSocketAddress *vaddr, NonBlockingConnectHandler *callback, - void *opaque) + void *opaque, + Error **errp) { struct sockaddr_vm svm; int sock = -1; @@ -818,9 +820,9 @@ static void vsock_unsupported(Error **errp) error_setg(errp, "socket family AF_VSOCK unsupported"); } -static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp, +static int vsock_connect_saddr(VsockSocketAddress *vaddr, NonBlockingConnectHandler *callback, - void *opaque) + void *opaque, Error **errp) { vsock_unsupported(errp); return -1; @@ -910,8 +912,9 @@ err: return -1; } -static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp, - NonBlockingConnectHandler *callback, void *opaque) +static int unix_connect_saddr(UnixSocketAddress *saddr, + NonBlockingConnectHandler *callback, void *opaque, + Error **errp) { struct sockaddr_un un; ConnectState *connect_state = NULL; @@ -978,8 +981,9 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, return -1; } -static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp, - NonBlockingConnectHandler *callback, void *opaque) +static int unix_connect_saddr(UnixSocketAddress *saddr, + NonBlockingConnectHandler *callback, void *opaque, + Error **errp) { error_setg(errp, "unix sockets are not available on windows"); errno = ENOTSUP; @@ -1025,7 +1029,7 @@ int unix_connect(const char *path, Error **errp) saddr = g_new0(UnixSocketAddress, 1); saddr->path = g_strdup(path); - sock = unix_connect_saddr(saddr, errp, NULL, NULL); + sock = unix_connect_saddr(saddr, NULL, NULL, errp); qapi_free_UnixSocketAddress(saddr); return sock; } @@ -1074,18 +1078,18 @@ fail: return NULL; } -int socket_connect(SocketAddress *addr, Error **errp, - NonBlockingConnectHandler *callback, void *opaque) +int socket_connect(SocketAddress *addr, NonBlockingConnectHandler *callback, + void *opaque, Error **errp) { int fd; switch (addr->type) { case SOCKET_ADDRESS_KIND_INET: - fd = inet_connect_saddr(addr->u.inet.data, errp, callback, opaque); + fd = inet_connect_saddr(addr->u.inet.data, callback, opaque, errp); break; case SOCKET_ADDRESS_KIND_UNIX: - fd = unix_connect_saddr(addr->u.q_unix.data, errp, callback, opaque); + fd = unix_connect_saddr(addr->u.q_unix.data, callback, opaque, errp); break; case SOCKET_ADDRESS_KIND_FD: @@ -1097,7 +1101,7 @@ int socket_connect(SocketAddress *addr, Error **errp, break; case SOCKET_ADDRESS_KIND_VSOCK: - fd = vsock_connect_saddr(addr->u.vsock.data, errp, callback, opaque); + fd = vsock_connect_saddr(addr->u.vsock.data, callback, opaque, errp); break; default: diff --git a/util/throttle.c b/util/throttle.c index 3817d9b904..3570ed25fc 100644 --- a/util/throttle.c +++ b/util/throttle.c @@ -380,6 +380,14 @@ static void throttle_fix_bucket(LeakyBucket *bkt) } } +/* undo internal bucket parameter changes (see throttle_fix_bucket()) */ +static void throttle_unfix_bucket(LeakyBucket *bkt) +{ + if (bkt->max < bkt->avg) { + bkt->max = 0; + } +} + /* take care of canceling a timer */ static void throttle_cancel_timer(QEMUTimer *timer) { @@ -420,7 +428,13 @@ void throttle_config(ThrottleState *ts, */ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg) { + int i; + *cfg = ts->cfg; + + for (i = 0; i < BUCKETS_COUNT; i++) { + throttle_unfix_bucket(&cfg->buckets[i]); + } } @@ -205,6 +205,7 @@ static NotifierList machine_init_done_notifiers = bool xen_allowed; uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; +bool xen_domid_restrict; static int has_defaults = 1; static int default_serial = 1; @@ -3230,6 +3231,8 @@ int main(int argc, char **argv, char **envp) } } } + error_report("'-hdachs' is deprecated, please use '-device" + " ide-hd,cyls=c,heads=h,secs=s,...' instead"); break; case QEMU_OPTION_numa: opts = qemu_opts_parse_noisily(qemu_find_opts("numa"), @@ -3933,6 +3936,13 @@ int main(int argc, char **argv, char **envp) } xen_mode = XEN_ATTACH; break; + case QEMU_OPTION_xen_domid_restrict: + if (!(xen_available())) { + error_report("Option not supported for this target"); + exit(1); + } + xen_domid_restrict = true; + break; case QEMU_OPTION_trace: g_free(trace_file); trace_file = trace_opt_parse(optarg); |