diff options
200 files changed, 4296 insertions, 1366 deletions
diff --git a/.editorconfig b/.editorconfig index 22681d91c6..7303759ed7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,6 +4,11 @@ # plugin. # # Check https://editorconfig.org for details. +# +# Emacs: you need https://github.com/10sr/editorconfig-custom-majormode-el +# to automatically enable the appropriate major-mode for your files +# that aren't already caught by your existing config. +# root = true @@ -15,17 +20,17 @@ charset = utf-8 [*.mak] indent_style = tab indent_size = 8 -file_type_emacs = makefile +emacs_mode = makefile [Makefile*] indent_style = tab indent_size = 8 -file_type_emacs = makefile +emacs_mode = makefile [*.{c,h,c.inc,h.inc}] indent_style = space indent_size = 4 -file_type_emacs = c +emacs_mode = c [*.sh] indent_style = space @@ -34,11 +39,11 @@ indent_size = 4 [*.{s,S}] indent_style = tab indent_size = 8 -file_type_emacs = asm +emacs_mode = asm [*.{vert,frag}] -file_type_emacs = glsl +emacs_mode = glsl [*.json] indent_style = space -file_type_emacs = python +emacs_mode = python diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 07202f6ffb..293a810656 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,6 +27,10 @@ include: else ../configure --enable-werror $CONFIGURE_ARGS ; fi || { cat config.log meson-logs/meson-log.txt && exit 1; } + - if test -n "$LD_JOBS"; + then + meson configure . -Dbackend_max_links="$LD_JOBS" ; + fi || exit 1; - make -j"$JOBS" - if test -n "$MAKE_CHECK_ARGS"; then @@ -481,6 +485,125 @@ clang-user: --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined MAKE_CHECK_ARGS: check-unit check-tcg +# Set LD_JOBS=1 because this requires LTO and ld consumes a large amount of memory. +# On gitlab runners, default value sometimes end up calling 2 lds concurrently and +# triggers an Out-Of-Memory error +# +# Since slirp callbacks are used in QEMU Timers, slirp needs to be compiled together +# with QEMU and linked as a static library to avoid false positives in CFI checks. +# This can be accomplished by using -enable-slirp=git, which avoids the use of +# a system-wide version of the library +# +# Split in three sets of build/check/acceptance to limit the execution time of each +# job +build-cfi-aarch64: + <<: *native_build_job_definition + needs: + - job: amd64-fedora-container + variables: + LD_JOBS: 1 + AR: llvm-ar + IMAGE: fedora + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + --enable-safe-stack --enable-slirp=git + TARGETS: aarch64-softmmu + MAKE_CHECK_ARGS: check-build + artifacts: + expire_in: 2 days + paths: + - build + +check-cfi-aarch64: + <<: *native_test_job_definition + needs: + - job: build-cfi-aarch64 + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check + +acceptance-cfi-aarch64: + <<: *native_test_job_definition + needs: + - job: build-cfi-aarch64 + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition + +build-cfi-ppc64-s390x: + <<: *native_build_job_definition + needs: + - job: amd64-fedora-container + variables: + LD_JOBS: 1 + AR: llvm-ar + IMAGE: fedora + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + --enable-safe-stack --enable-slirp=git + TARGETS: ppc64-softmmu s390x-softmmu + MAKE_CHECK_ARGS: check-build + artifacts: + expire_in: 2 days + paths: + - build + +check-cfi-ppc64-s390x: + <<: *native_test_job_definition + needs: + - job: build-cfi-ppc64-s390x + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check + +acceptance-cfi-ppc64-s390x: + <<: *native_test_job_definition + needs: + - job: build-cfi-ppc64-s390x + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition + +build-cfi-x86_64: + <<: *native_build_job_definition + needs: + - job: amd64-fedora-container + variables: + LD_JOBS: 1 + AR: llvm-ar + IMAGE: fedora + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + --enable-safe-stack --enable-slirp=git + TARGETS: x86_64-softmmu + MAKE_CHECK_ARGS: check-build + artifacts: + expire_in: 2 days + paths: + - build + +check-cfi-x86_64: + <<: *native_test_job_definition + needs: + - job: build-cfi-x86_64 + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check + +acceptance-cfi-x86_64: + <<: *native_test_job_definition + needs: + - job: build-cfi-x86_64 + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition + tsan-build: <<: *native_build_job_definition variables: diff --git a/Kconfig b/Kconfig index bf694c42af..d52ebd839b 100644 --- a/Kconfig +++ b/Kconfig @@ -2,3 +2,4 @@ source Kconfig.host source backends/Kconfig source accel/Kconfig source hw/Kconfig +source semihosting/Kconfig diff --git a/MAINTAINERS b/MAINTAINERS index e04ae21a2f..8e9f0d591e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1869,7 +1869,7 @@ S: Odd Fixes F: hw/9pfs/ X: hw/9pfs/xen-9p* F: fsdev/ -F: docs/interop/virtfs-proxy-helper.rst +F: docs/tools/virtfs-proxy-helper.rst F: tests/qtest/virtio-9p-test.c T: git https://gitlab.com/gkurz/qemu.git 9p-next T: git https://github.com/cschoenebeck/qemu.git 9p.next @@ -1900,7 +1900,7 @@ S: Supported F: tools/virtiofsd/* F: hw/virtio/vhost-user-fs* F: include/hw/virtio/vhost-user-fs.h -F: docs/interop/virtiofsd.rst +F: docs/tools/virtiofsd.rst virtio-input M: Gerd Hoffmann <kraxel@redhat.com> @@ -2027,7 +2027,14 @@ M: Alistair Francis <alistair@alistair23.me> S: Maintained F: hw/core/generic-loader.c F: include/hw/core/generic-loader.h -F: docs/generic-loader.txt +F: docs/system/generic-loader.rst + +Guest Loader +M: Alex BennĂ©e <alex.bennee@linaro.org> +S: Maintained +F: hw/core/guest-loader.c +F: docs/system/guest-loader.rst +F: tests/acceptance/boot_xen.py Intel Hexadecimal Object File Loader M: Su Hang <suhang16@mails.ucas.ac.cn> @@ -2207,7 +2214,7 @@ F: block/ F: hw/block/ F: include/block/ F: qemu-img* -F: docs/interop/qemu-img.rst +F: docs/tools/qemu-img.rst F: qemu-io* F: tests/qemu-iotests/ F: util/qemu-progress.c @@ -2664,7 +2671,7 @@ F: qapi/trace.json F: scripts/tracetool.py F: scripts/tracetool/ F: scripts/qemu-trace-stap* -F: docs/interop/qemu-trace-stap.rst +F: docs/tools/qemu-trace-stap.rst F: docs/devel/tracing.txt T: git https://github.com/stefanha/qemu.git tracing @@ -3050,6 +3057,7 @@ F: block/iscsi-opts.c Network Block Device (NBD) M: Eric Blake <eblake@redhat.com> +M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> L: qemu-block@nongnu.org S: Maintained F: block/nbd* @@ -3058,8 +3066,9 @@ F: include/block/nbd* F: qemu-nbd.* F: blockdev-nbd.c F: docs/interop/nbd.txt -F: docs/interop/qemu-nbd.rst +F: docs/tools/qemu-nbd.rst T: git https://repo.or.cz/qemu/ericb.git nbd +T: git https://src.openvz.org/scm/~vsementsov/qemu.git nbd NFS M: Peter Lieven <pl@kamp.de> @@ -3253,8 +3262,8 @@ F: qapi/rdma.json Semihosting M: Alex BennĂ©e <alex.bennee@linaro.org> S: Maintained -F: hw/semihosting/ -F: include/hw/semihosting/ +F: semihosting/ +F: include/semihosting/ Multi-process QEMU M: Elena Ufimtseva <elena.ufimtseva@oracle.com> diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index e9d2d6aaaa..69aa7d018b 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -122,7 +122,7 @@ static void xenstore_record_dm_state(struct xs_handle *xs, const char *state) } -static void xen_change_state_handler(void *opaque, int running, +static void xen_change_state_handler(void *opaque, bool running, RunState state) { if (running) { diff --git a/audio/audio.c b/audio/audio.c index 6734c8af70..534278edfe 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1540,7 +1540,7 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv, } } -static void audio_vm_change_state_handler (void *opaque, int running, +static void audio_vm_change_state_handler (void *opaque, bool running, RunState state) { AudioState *s = opaque; diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c index bd050e8e9c..2a0d2e4a31 100644 --- a/backends/dbus-vmstate.c +++ b/backends/dbus-vmstate.c @@ -229,7 +229,10 @@ static int dbus_vmstate_post_load(void *opaque, int version_id) &bytes_read, NULL, &err)) { goto error; } - g_return_val_if_fail(bytes_read == len, -1); + if (bytes_read != len) { + error_report("%s: Short read", __func__); + return -1; + } id[len] = 0; trace_dbus_vmstate_loading(id); diff --git a/block.c b/block.c index 2daff6d29a..f377158c42 100644 --- a/block.c +++ b/block.c @@ -2995,11 +2995,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, /* Hook up the backing file link; drop our reference, bs owns the * backing_hd reference now */ - bdrv_set_backing_hd(bs, backing_hd, &local_err); + ret = bdrv_set_backing_hd(bs, backing_hd, errp); bdrv_unref(backing_hd); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; + if (ret < 0) { goto free_exit; } diff --git a/block/blkdebug.c b/block/blkdebug.c index 7eaa8a28bf..2c0b9b0ee8 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -464,7 +464,6 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, { BDRVBlkdebugState *s = bs->opaque; QemuOpts *opts; - Error *local_err = NULL; int ret; uint64_t align; @@ -494,10 +493,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto out; } diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 13ae63983b..b7579370a3 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -157,19 +157,17 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, /* Open the file */ bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false, - &local_err); - if (local_err) { + errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } /* Open the log file */ s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_of_bds, - BDRV_CHILD_METADATA, false, &local_err); - if (local_err) { + BDRV_CHILD_METADATA, false, errp); + if (!s->log_file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } diff --git a/block/blkreplay.c b/block/blkreplay.c index 30a0f5d57a..4a247752fd 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -23,16 +23,14 @@ typedef struct Request { static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - Error *local_err = NULL; int ret; /* Open the image file */ bs->file = bdrv_open_child(NULL, options, "image", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } diff --git a/block/blkverify.c b/block/blkverify.c index 943e62be9c..188d7632fa 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -112,7 +112,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, { BDRVBlkverifyState *s = bs->opaque; QemuOpts *opts; - Error *local_err = NULL; int ret; opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); @@ -125,20 +124,18 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } /* Open the test file */ s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "test", bs, &child_of_bds, BDRV_CHILD_DATA, - false, &local_err); - if (local_err) { + false, errp); + if (!s->test_file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } diff --git a/block/block-backend.c b/block/block-backend.c index e493f17515..413af51f3b 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -163,7 +163,7 @@ static const char *blk_root_get_name(BdrvChild *child) return blk_name(child->opaque); } -static void blk_vm_state_changed(void *opaque, int running, RunState state) +static void blk_vm_state_changed(void *opaque, bool running, RunState state) { Error *local_err = NULL; BlockBackend *blk = opaque; diff --git a/block/mirror.c b/block/mirror.c index 1803c6988b..6af02a57c4 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1860,8 +1860,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, bool auto_complete, Error **errp) { bool base_read_only; - Error *local_err = NULL; - BlockJob *ret; + BlockJob *job; base_read_only = bdrv_is_read_only(base); @@ -1871,19 +1870,18 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, } } - ret = mirror_start_job( + job = mirror_start_job( job_id, bs, creation_flags, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN, false, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + errp); + if (!job) { goto error_restore_flags; } - return ret; + return job; error_restore_flags: /* ignore error and errp for bdrv_reopen, because we want to propagate diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 42d81c44cd..8fb4731551 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -950,25 +950,27 @@ static void set_readonly_helper(gpointer bitmap, gpointer value) bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value); } -/* qcow2_load_dirty_bitmaps() - * Return value is a hint for caller: true means that the Qcow2 header was - * updated. (false doesn't mean that the header should be updated by the - * caller, it just means that updating was not needed or the image cannot be - * written to). - * On failure the function returns false. +/* + * Return true on success, false on failure. + * If header_updated is not NULL then it is set appropriately regardless of + * the return value. */ -bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, + Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; Qcow2Bitmap *bm; GSList *created_dirty_bitmaps = NULL; - bool header_updated = false; bool needs_update = false; + if (header_updated) { + *header_updated = false; + } + if (s->nb_bitmaps == 0) { /* No bitmaps - nothing to do */ - return false; + return true; } bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, @@ -1024,7 +1026,9 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) error_setg_errno(errp, -ret, "Can't update bitmap directory"); goto fail; } - header_updated = true; + if (header_updated) { + *header_updated = true; + } } if (!can_write(bs)) { @@ -1035,7 +1039,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) g_slist_free(created_dirty_bitmaps); bitmap_list_free(bm_list); - return header_updated; + return true; fail: g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs); @@ -1077,30 +1081,32 @@ static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) /* * qcow2_get_bitmap_info_list() * Returns a list of QCOW2 bitmap details. - * In case of no bitmaps, the function returns NULL and - * the @errp parameter is not set. - * When bitmap information can not be obtained, the function returns - * NULL and the @errp parameter is set. + * On success return true with info_list set (note, that if there are no + * bitmaps, info_list is set to NULL). + * On failure return false with errp set. */ -Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, - Error **errp) +bool qcow2_get_bitmap_info_list(BlockDriverState *bs, + Qcow2BitmapInfoList **info_list, Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; Qcow2Bitmap *bm; - Qcow2BitmapInfoList *list = NULL; - Qcow2BitmapInfoList **tail = &list; + Qcow2BitmapInfoList **tail; if (s->nb_bitmaps == 0) { - return NULL; + *info_list = NULL; + return true; } bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, s->bitmap_directory_size, errp); - if (bm_list == NULL) { - return NULL; + if (!bm_list) { + return false; } + *info_list = NULL; + tail = info_list; + QSIMPLEQ_FOREACH(bm, bm_list, entry) { Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1); info->granularity = 1U << bm->granularity_bits; @@ -1111,7 +1117,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, bitmap_list_free(bm_list); - return list; + return true; } int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) @@ -1513,9 +1519,10 @@ out: * readonly to begin with, and whether we opened directly or reopened to that * state shouldn't matter for the state we get afterward. */ -void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, +bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, Error **errp) { + ERRP_GUARD(); BdrvDirtyBitmap *bitmap; BDRVQcow2State *s = bs->opaque; uint32_t new_nb_bitmaps = s->nb_bitmaps; @@ -1535,7 +1542,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, s->bitmap_directory_size, errp); if (bm_list == NULL) { - return; + return false; } } @@ -1650,7 +1657,7 @@ success: } bitmap_list_free(bm_list); - return; + return true; fail: QSIMPLEQ_FOREACH(bm, bm_list, entry) { @@ -1668,16 +1675,14 @@ fail: } bitmap_list_free(bm_list); + return false; } int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp) { BdrvDirtyBitmap *bitmap; - Error *local_err = NULL; - qcow2_store_persistent_dirty_bitmaps(bs, false, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + if (!qcow2_store_persistent_dirty_bitmaps(bs, false, errp)) { return -EINVAL; } diff --git a/block/qcow2.c b/block/qcow2.c index d9f49a52e7..0db1227ac9 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -868,7 +868,7 @@ static void qcow2_attach_aio_context(BlockDriverState *bs, cache_clean_timer_init(bs, new_context); } -static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, +static bool read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, uint64_t *l2_cache_size, uint64_t *l2_cache_entry_size, uint64_t *refcount_cache_size, Error **errp) @@ -906,16 +906,16 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " "at the same time"); - return; + return false; } else if (l2_cache_size_set && (l2_cache_max_setting > combined_cache_size)) { error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " QCOW2_OPT_CACHE_SIZE); - return; + return false; } else if (*refcount_cache_size > combined_cache_size) { error_setg(errp, QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not exceed " QCOW2_OPT_CACHE_SIZE); - return; + return false; } if (l2_cache_size_set) { @@ -954,8 +954,10 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, error_setg(errp, "L2 cache entry size must be a power of two " "between %d and the cluster size (%d)", 1 << MIN_CLUSTER_BITS, s->cluster_size); - return; + return false; } + + return true; } typedef struct Qcow2ReopenState { @@ -982,7 +984,6 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, int i; const char *encryptfmt; QDict *encryptopts = NULL; - Error *local_err = NULL; int ret; qdict_extract_subqdict(options, &encryptopts, "encrypt."); @@ -995,10 +996,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } /* get L2 table/refcount block cache size from command line options */ - read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, - &refcount_cache_size, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, + &refcount_cache_size, errp)) { ret = -EINVAL; goto fail; } @@ -1159,6 +1158,10 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } qdict_put_str(encryptopts, "format", "qcow"); r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); + if (!r->crypto_opts) { + ret = -EINVAL; + goto fail; + } break; case QCOW_CRYPT_LUKS: @@ -1171,14 +1174,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } qdict_put_str(encryptopts, "format", "luks"); r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); + if (!r->crypto_opts) { + ret = -EINVAL; + goto fail; + } break; default: error_setg(errp, "Unsupported encryption method %d", s->crypt_method_header); - break; - } - if (s->crypt_method_header != QCOW_CRYPT_NONE && !r->crypto_opts) { ret = -EINVAL; goto fail; } @@ -1292,11 +1296,11 @@ static int validate_compression_type(BDRVQcow2State *s, Error **errp) static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; unsigned int len, i; int ret = 0; QCowHeader header; - Error *local_err = NULL; uint64_t ext_end; uint64_t l1_vm_state_index; bool update_header = false; @@ -1611,9 +1615,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, /* Open external data file */ s->data_file = bdrv_open_child(NULL, options, "data-file", bs, &child_of_bds, BDRV_CHILD_DATA, - true, &local_err); - if (local_err) { - error_propagate(errp, local_err); + true, errp); + if (*errp) { ret = -EINVAL; goto fail; } @@ -1785,9 +1788,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) { /* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */ - bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + bool header_updated; + if (!qcow2_load_dirty_bitmaps(bs, &header_updated, errp)) { ret = -EINVAL; goto fail; } @@ -2719,11 +2721,11 @@ static void qcow2_close(BlockDriverState *bs) static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) { + ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; int flags = s->flags; QCryptoBlock *crypto = NULL; QDict *options; - Error *local_err = NULL; int ret; /* @@ -2741,16 +2743,11 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, flags &= ~BDRV_O_INACTIVE; qemu_co_mutex_lock(&s->lock); - ret = qcow2_do_open(bs, options, flags, &local_err); + ret = qcow2_do_open(bs, options, flags, errp); qemu_co_mutex_unlock(&s->lock); qobject_unref(options); - if (local_err) { - error_propagate_prepend(errp, local_err, - "Could not reopen qcow2 layer: "); - bs->drv = NULL; - return; - } else if (ret < 0) { - error_setg_errno(errp, -ret, "Could not reopen qcow2 layer"); + if (ret < 0) { + error_prepend(errp, "Could not reopen qcow2 layer: "); bs->drv = NULL; return; } @@ -5066,12 +5063,10 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, BDRVQcow2State *s = bs->opaque; ImageInfoSpecific *spec_info; QCryptoBlockInfo *encrypt_info = NULL; - Error *local_err = NULL; if (s->crypto != NULL) { - encrypt_info = qcrypto_block_get_info(s->crypto, &local_err); - if (local_err) { - error_propagate(errp, local_err); + encrypt_info = qcrypto_block_get_info(s->crypto, errp); + if (!encrypt_info) { return NULL; } } @@ -5088,9 +5083,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, }; } else if (s->qcow_version == 3) { Qcow2BitmapInfoList *bitmaps; - bitmaps = qcow2_get_bitmap_info_list(bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!qcow2_get_bitmap_info_list(bs, &bitmaps, errp)) { qapi_free_ImageInfoSpecific(spec_info); qapi_free_QCryptoBlockInfo(encrypt_info); return NULL; diff --git a/block/qcow2.h b/block/qcow2.h index 0678073b74..0fe5f74ed3 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -978,12 +978,13 @@ void qcow2_cache_discard(Qcow2Cache *c, void *table); int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size); -bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); -Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, - Error **errp); +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, + Error **errp); +bool qcow2_get_bitmap_info_list(BlockDriverState *bs, + Qcow2BitmapInfoList **info_list, Error **errp); int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); -void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, +bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, Error **errp); int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, diff --git a/block/qed.c b/block/qed.c index b27e7546ca..f45c640513 100644 --- a/block/qed.c +++ b/block/qed.c @@ -393,6 +393,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); if (ret < 0) { + error_setg(errp, "Failed to read QED header"); return ret; } qed_header_le_to_cpu(&le_header, &s->header); @@ -408,25 +409,30 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, return -ENOTSUP; } if (!qed_is_cluster_size_valid(s->header.cluster_size)) { + error_setg(errp, "QED cluster size is invalid"); return -EINVAL; } /* Round down file size to the last cluster */ file_size = bdrv_getlength(bs->file->bs); if (file_size < 0) { + error_setg(errp, "Failed to get file length"); return file_size; } s->file_size = qed_start_of_cluster(s, file_size); if (!qed_is_table_size_valid(s->header.table_size)) { + error_setg(errp, "QED table size is invalid"); return -EINVAL; } if (!qed_is_image_size_valid(s->header.image_size, s->header.cluster_size, s->header.table_size)) { + error_setg(errp, "QED image size is invalid"); return -EINVAL; } if (!qed_check_table_offset(s, s->header.l1_table_offset)) { + error_setg(errp, "QED table offset is invalid"); return -EINVAL; } @@ -438,6 +444,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, /* Header size calculation must not overflow uint32_t */ if (s->header.header_size > UINT32_MAX / s->header.cluster_size) { + error_setg(errp, "QED header size is too large"); return -EINVAL; } @@ -445,6 +452,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, if ((uint64_t)s->header.backing_filename_offset + s->header.backing_filename_size > s->header.cluster_size * s->header.header_size) { + error_setg(errp, "QED backing filename offset is invalid"); return -EINVAL; } @@ -453,6 +461,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, bs->auto_backing_file, sizeof(bs->auto_backing_file)); if (ret < 0) { + error_setg(errp, "Failed to read backing filename"); return ret; } pstrcpy(bs->backing_file, sizeof(bs->backing_file), @@ -475,6 +484,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_write_header_sync(s); if (ret) { + error_setg(errp, "Failed to update header"); return ret; } @@ -487,6 +497,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_read_l1_table_sync(s); if (ret) { + error_setg(errp, "Failed to read L1 table"); goto out; } @@ -503,6 +514,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_check(s, &result, true); if (ret) { + error_setg(errp, "Image corrupted"); goto out; } } @@ -1537,22 +1549,16 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, Error **errp) { BDRVQEDState *s = bs->opaque; - Error *local_err = NULL; int ret; bdrv_qed_close(bs); bdrv_qed_init_state(bs); qemu_co_mutex_lock(&s->table_lock); - ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); + ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, errp); qemu_co_mutex_unlock(&s->table_lock); - if (local_err) { - error_propagate_prepend(errp, local_err, - "Could not reopen qed layer: "); - return; - } else if (ret < 0) { - error_setg_errno(errp, -ret, "Could not reopen qed layer"); - return; + if (ret < 0) { + error_prepend(errp, "Could not reopen qed layer: "); } } diff --git a/block/quorum.c b/block/quorum.c index 0bd75450de..cfc1436abb 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -929,7 +929,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVQuorumState *s = bs->opaque; - Error *local_err = NULL; QemuOpts *opts = NULL; const char *pattern_str; bool *opened; @@ -1007,9 +1006,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, s->children[i] = bdrv_open_child(NULL, options, indexstr, bs, &child_of_bds, BDRV_CHILD_DATA, false, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + errp); + if (!s->children[i]) { ret = -EINVAL; goto close_exit; } diff --git a/blockdev.c b/blockdev.c index 68f022827c..5cc7c7effe 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1824,9 +1824,7 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) aio_context_acquire(aio_context); if (set_backing_hd) { - bdrv_set_backing_hd(target_bs, source, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (bdrv_set_backing_hd(target_bs, source, errp) < 0) { goto unref; } } diff --git a/blockjob.c b/blockjob.c index ef968017a2..207e8c7fd9 100644 --- a/blockjob.c +++ b/blockjob.c @@ -258,18 +258,18 @@ static bool job_timer_pending(Job *job) return timer_pending(&job->sleep_timer); } -void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) +bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { const BlockJobDriver *drv = block_job_driver(job); int64_t old_speed = job->speed; - if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) { - return; + if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp) < 0) { + return false; } if (speed < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "speed", "a non-negative value"); - return; + return false; } ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME); @@ -281,11 +281,13 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) } if (speed && speed <= old_speed) { - return; + return true; } /* kick only if a timer is pending */ job_enter_cond(&job->job, job_timer_pending); + + return true; } int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) @@ -462,12 +464,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, /* Only set speed when necessary to avoid NotSupported error */ if (speed != 0) { - Error *local_err = NULL; - - block_job_set_speed(job, speed, &local_err); - if (local_err) { + if (!block_job_set_speed(job, speed, errp)) { job_early_fail(&job->job); - error_propagate(errp, local_err); return NULL; } } diff --git a/default-configs/devices/lm32-softmmu.mak b/default-configs/devices/lm32-softmmu.mak index 115b3e34c9..1bce3f6e8b 100644 --- a/default-configs/devices/lm32-softmmu.mak +++ b/default-configs/devices/lm32-softmmu.mak @@ -8,5 +8,5 @@ CONFIG_SEMIHOSTING=y # Boards: # -CONFIG_LM32=y +CONFIG_LM32_EVR=y CONFIG_MILKYMIST=y diff --git a/disas/nanomips.cpp b/disas/nanomips.cpp index 90e63b8367..2b09655271 100644 --- a/disas/nanomips.cpp +++ b/disas/nanomips.cpp @@ -837,7 +837,7 @@ int NMD::Disassemble(const uint16 * data, std::string & dis, * an ASE attribute and the requested version * not having that attribute */ - dis = "ASE attribute missmatch"; + dis = "ASE attribute mismatch"; return -5; } disassembly_function dis_fn = table[i].disassembly; diff --git a/docs/generic-loader.txt b/docs/generic-loader.txt deleted file mode 100644 index a9603a2af7..0000000000 --- a/docs/generic-loader.txt +++ /dev/null @@ -1,92 +0,0 @@ -Copyright (c) 2016 Xilinx Inc. - -This work is licensed under the terms of the GNU GPL, version 2 or later. See -the COPYING file in the top-level directory. - - -The 'loader' device allows the user to load multiple images or values into -QEMU at startup. - -Loading Data into Memory Values -------------------------------- -The loader device allows memory values to be set from the command line. This -can be done by following the syntax below: - - -device loader,addr=<addr>,data=<data>,data-len=<data-len> - [,data-be=<data-be>][,cpu-num=<cpu-num>] - - <addr> - The address to store the data in. - <data> - The value to be written to the address. The maximum size of - the data is 8 bytes. - <data-len> - The length of the data in bytes. This argument must be - included if the data argument is. - <data-be> - Set to true if the data to be stored on the guest should be - written as big endian data. The default is to write little - endian data. - <cpu-num> - The number of the CPU's address space where the data should - be loaded. If not specified the address space of the first - CPU is used. - -All values are parsed using the standard QemuOps parsing. This allows the user -to specify any values in any format supported. By default the values -will be parsed as decimal. To use hex values the user should prefix the number -with a '0x'. - -An example of loading value 0x8000000e to address 0xfd1a0104 is: - -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4 - -Setting a CPU's Program Counter -------------------------------- -The loader device allows the CPU's PC to be set from the command line. This -can be done by following the syntax below: - - -device loader,addr=<addr>,cpu-num=<cpu-num> - - <addr> - The value to use as the CPU's PC. - <cpu-num> - The number of the CPU whose PC should be set to the - specified value. - -All values are parsed using the standard QemuOps parsing. This allows the user -to specify any values in any format supported. By default the values -will be parsed as decimal. To use hex values the user should prefix the number -with a '0x'. - -An example of setting CPU 0's PC to 0x8000 is: - -device loader,addr=0x8000,cpu-num=0 - -Loading Files -------------- -The loader device also allows files to be loaded into memory. It can load ELF, -U-Boot, and Intel HEX executable formats as well as raw images. The syntax is -shown below: - - -device loader,file=<file>[,addr=<addr>][,cpu-num=<cpu-num>][,force-raw=<raw>] - - <file> - A file to be loaded into memory - <addr> - The memory address where the file should be loaded. This is - required for raw images and ignored for non-raw files. - <cpu-num> - This specifies the CPU that should be used. This is an - optional argument and will cause the CPU's PC to be set to - the memory address where the raw file is loaded or the entry - point specified in the executable format header. This option - should only be used for the boot image. - This will also cause the image to be written to the specified - CPU's address space. If not specified, the default is CPU 0. - <force-raw> - Setting force-raw=on forces the file to be treated as a raw - image. This can be used to load supported executable formats - as if they were raw. - -All values are parsed using the standard QemuOps parsing. This allows the user -to specify any values in any format supported. By default the values -will be parsed as decimal. To use hex values the user should prefix the number -with a '0x'. - -An example of loading an ELF file which CPU0 will boot is shown below: - -device loader,file=./images/boot.elf,cpu-num=0 - -Restrictions and ToDos ----------------------- - - At the moment it is just assumed that if you specify a cpu-num then you - want to set the PC as well. This might not always be the case. In future - the internal state 'set_pc' (which exists in the generic loader now) should - be exposed to the user so that they can choose if the PC is set or not. diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 690bada784..d1fb8f25b3 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -48,6 +48,7 @@ Supported devices * UART * Ethernet controllers * Front LEDs (PCA9552 on I2C bus) + * LPC Peripheral Controller (a subset of subdevices are supported) Missing devices @@ -56,7 +57,6 @@ Missing devices * Coprocessor support * ADC (out of tree implementation) * PWM and Fan Controller - * LPC Bus Controller * Slave GPIO Controller * Super I/O Controller * Hash/Crypto Engine @@ -72,18 +72,22 @@ Missing devices Boot options ------------ -The Aspeed machines can be started using the -kernel option to load a -Linux kernel or from a firmware image which can be downloaded from the -OpenPOWER jenkins : +The Aspeed machines can be started using the ``-kernel`` option to +load a Linux kernel or from a firmware. Images can be downloaded from +the OpenBMC jenkins : - https://openpower.xyz/ + https://jenkins.openbmc.org/job/ci-openbmc/lastSuccessfulBuild/distro=ubuntu,label=docker-builder + +or directly from the OpenBMC GitHub release repository : + + https://github.com/openbmc/openbmc/releases The image should be attached as an MTD drive. Run : .. code-block:: bash $ qemu-system-arm -M romulus-bmc -nic user \ - -drive file=flash-romulus,format=raw,if=mtd -nographic + -drive file=obmc-phosphor-image-romulus.static.mtd,format=raw,if=mtd -nographic Options specific to Aspeed machines are : diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 241b28a521..5e3a31c123 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -166,6 +166,14 @@ Using ``-M kernel-irqchip=off`` with x86 machine types that include a local APIC is deprecated. The ``split`` setting is supported, as is using ``-M kernel-irqchip=off`` with the ISA PC machine type. +hexadecimal sizes with scaling multipliers (since 6.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Input parameters that take a size value should only use a size suffix +(such as 'k' or 'M') when the base is written in decimal, and not when +the value is hexadecimal. That is, '0x20M' is deprecated, and should +be written either as '32M' or as '0x2000000'. + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/system/generic-loader.rst b/docs/system/generic-loader.rst new file mode 100644 index 0000000000..6bf8a4eb48 --- /dev/null +++ b/docs/system/generic-loader.rst @@ -0,0 +1,117 @@ +.. + Copyright (c) 2016, Xilinx Inc. + +This work is licensed under the terms of the GNU GPL, version 2 or later. See +the COPYING file in the top-level directory. + +Generic Loader +-------------- + +The 'loader' device allows the user to load multiple images or values into +QEMU at startup. + +Loading Data into Memory Values +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The loader device allows memory values to be set from the command line. This +can be done by following the syntax below:: + + -device loader,addr=<addr>,data=<data>,data-len=<data-len> \ + [,data-be=<data-be>][,cpu-num=<cpu-num>] + +``<addr>`` + The address to store the data in. + +``<data>`` + The value to be written to the address. The maximum size of the data + is 8 bytes. + +``<data-len>`` + The length of the data in bytes. This argument must be included if + the data argument is. + +``<data-be>`` + Set to true if the data to be stored on the guest should be written + as big endian data. The default is to write little endian data. + +``<cpu-num>`` + The number of the CPU's address space where the data should be + loaded. If not specified the address space of the first CPU is used. + +All values are parsed using the standard QemuOps parsing. This allows the user +to specify any values in any format supported. By default the values +will be parsed as decimal. To use hex values the user should prefix the number +with a '0x'. + +An example of loading value 0x8000000e to address 0xfd1a0104 is:: + + -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4 + +Setting a CPU's Program Counter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The loader device allows the CPU's PC to be set from the command line. This +can be done by following the syntax below:: + + -device loader,addr=<addr>,cpu-num=<cpu-num> + +``<addr>`` + The value to use as the CPU's PC. + +``<cpu-num>`` + The number of the CPU whose PC should be set to the specified value. + +All values are parsed using the standard QemuOpts parsing. This allows the user +to specify any values in any format supported. By default the values +will be parsed as decimal. To use hex values the user should prefix the number +with a '0x'. + +An example of setting CPU 0's PC to 0x8000 is:: + + -device loader,addr=0x8000,cpu-num=0 + +Loading Files +^^^^^^^^^^^^^ + +The loader device also allows files to be loaded into memory. It can load ELF, +U-Boot, and Intel HEX executable formats as well as raw images. The syntax is +shown below: + + -device loader,file=<file>[,addr=<addr>][,cpu-num=<cpu-num>][,force-raw=<raw>] + +``<file>`` + A file to be loaded into memory + +``<addr>`` + The memory address where the file should be loaded. This is required + for raw images and ignored for non-raw files. + +``<cpu-num>`` + This specifies the CPU that should be used. This is an + optional argument and will cause the CPU's PC to be set to the + memory address where the raw file is loaded or the entry point + specified in the executable format header. This option should only + be used for the boot image. This will also cause the image to be + written to the specified CPU's address space. If not specified, the + default is CPU 0. <force-raw> - Setting force-raw=on forces the file + to be treated as a raw image. This can be used to load supported + executable formats as if they were raw. + +All values are parsed using the standard QemuOpts parsing. This allows the user +to specify any values in any format supported. By default the values +will be parsed as decimal. To use hex values the user should prefix the number +with a '0x'. + +An example of loading an ELF file which CPU0 will boot is shown below:: + + -device loader,file=./images/boot.elf,cpu-num=0 + +Restrictions and ToDos +^^^^^^^^^^^^^^^^^^^^^^ + +At the moment it is just assumed that if you specify a cpu-num then +you want to set the PC as well. This might not always be the case. In +future the internal state 'set_pc' (which exists in the generic loader +now) should be exposed to the user so that they can choose if the PC +is set or not. + + diff --git a/docs/system/guest-loader.rst b/docs/system/guest-loader.rst new file mode 100644 index 0000000000..37d03cbd89 --- /dev/null +++ b/docs/system/guest-loader.rst @@ -0,0 +1,54 @@ +.. + Copyright (c) 2020, Linaro + +Guest Loader +------------ + +The guest loader is similar to the `generic-loader` although it is +aimed at a particular use case of loading hypervisor guests. This is +useful for debugging hypervisors without having to jump through the +hoops of firmware and boot-loaders. + +The guest loader does two things: + + - load blobs (kernels and initial ram disks) into memory + - sets platform FDT data so hypervisors can find and boot them + +This is what is typically done by a boot-loader like grub using it's +multi-boot capability. A typical example would look like: + +.. parsed-literal:: + + |qemu_system| -kernel ~/xen.git/xen/xen \ + -append "dom0_mem=1G,max:1G loglvl=all guest_loglvl=all" \ + -device guest-loader,addr=0x42000000,kernel=Image,bootargs="root=/dev/sda2 ro console=hvc0 earlyprintk=xen" \ + -device guest-loader,addr=0x47000000,initrd=rootfs.cpio + +In the above example the Xen hypervisor is loaded by the -kernel +parameter and passed it's boot arguments via -append. The Dom0 guest +is loaded into the areas of memory. Each blob will get +`/chosen/module@<addr>` entry in the FDT to indicate it's location and +size. Additional information can be passed with by using additional +arguments. + +Currently the only supported machines which use FDT data to boot are +the ARM and RiscV `virt` machines. + +Arguments +^^^^^^^^^ + +The full syntax of the guest-loader is:: + + -device guest-loader,addr=<addr>[,kernel=<file>,[bootargs=<args>]][,initrd=<file>] + +``addr=<addr>`` + This is mandatory and indicates the start address of the blob. + +``kernel|initrd=<file>`` + Indicates the filename of the kernel or initrd blob. Both blobs will + have the "multiboot,module" compatibility string as well as + "multiboot,kernel" or "multiboot,ramdisk" as appropriate. + +``bootargs=<args>`` + This is an optional field for kernel blobs which will pass command + like via the `/chosen/module@<addr>/bootargs` node. diff --git a/docs/system/index.rst b/docs/system/index.rst index 625b494372..6ad9c93806 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -25,6 +25,8 @@ Contents: usb ivshmem linuxboot + generic-loader + guest-loader vnc-security tls gdb diff --git a/docs/system/ppc/embedded.rst b/docs/system/ppc/embedded.rst new file mode 100644 index 0000000000..cfffbda24d --- /dev/null +++ b/docs/system/ppc/embedded.rst @@ -0,0 +1,10 @@ +Embedded family boards +====================== + +- ``bamboo`` bamboo +- ``mpc8544ds`` mpc8544ds +- ``ppce500`` generic paravirt e500 platform +- ``ref405ep`` ref405ep +- ``sam460ex`` aCube Sam460ex +- ``taihu`` taihu +- ``virtex-ml507`` Xilinx Virtex ML507 reference design diff --git a/docs/system/ppc/powermac.rst b/docs/system/ppc/powermac.rst new file mode 100644 index 0000000000..04334ba210 --- /dev/null +++ b/docs/system/ppc/powermac.rst @@ -0,0 +1,34 @@ +PowerMac family boards (``g3beige``, ``mac99``) +================================================================== + +Use the executable ``qemu-system-ppc`` to simulate a complete PowerMac +PowerPC system. + +- ``g3beige`` Heathrow based PowerMAC +- ``mac99`` Mac99 based PowerMAC + +Supported devices +----------------- + +QEMU emulates the following PowerMac peripherals: + + * UniNorth or Grackle PCI Bridge + * PCI VGA compatible card with VESA Bochs Extensions + * 2 PMAC IDE interfaces with hard disk and CD-ROM support + * NE2000 PCI adapters + * Non Volatile RAM + * VIA-CUDA with ADB keyboard and mouse. + + +Missing devices +--------------- + + * To be identified + +Firmware +-------- + +Since version 0.9.1, QEMU uses OpenBIOS https://www.openbios.org/ for +the g3beige and mac99 PowerMac and the 40p machines. OpenBIOS is a free +(GPL v2) portable firmware implementation. The goal is to implement a +100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. diff --git a/docs/system/ppc/powernv.rst b/docs/system/ppc/powernv.rst new file mode 100644 index 0000000000..43c58bc32e --- /dev/null +++ b/docs/system/ppc/powernv.rst @@ -0,0 +1,193 @@ +PowerNV family boards (``powernv8``, ``powernv9``) +================================================================== + +PowerNV (as Non-Virtualized) is the "baremetal" platform using the +OPAL firmware. It runs Linux on IBM and OpenPOWER systems and it can +be used as an hypervisor OS, running KVM guests, or simply as a host +OS. + +The PowerNV QEMU machine tries to emulate a PowerNV system at the +level of the skiboot firmware, which loads the OS and provides some +runtime services. Power Systems have a lower firmware (HostBoot) that +does low level system initialization, like DRAM training. This is +beyond the scope of what QEMU addresses today. + +Supported devices +----------------- + + * Multi processor support for POWER8, POWER8NVL and POWER9. + * XSCOM, serial communication sideband bus to configure chiplets + * Simple LPC Controller + * Processor Service Interface (PSI) Controller + * Interrupt Controller, XICS (POWER8) and XIVE (POWER9) + * POWER8 PHB3 PCIe Host bridge and POWER9 PHB4 PCIe Host bridge + * Simple OCC is an on-chip microcontroller used for power management + tasks + * iBT device to handle BMC communication, with the internal BMC + simulator provided by QEMU or an external BMC such as an Aspeed + QEMU machine. + * PNOR containing the different firmware partitions. + +Missing devices +--------------- + +A lot is missing, among which : + + * POWER10 processor + * XIVE2 (POWER10) interrupt controller + * I2C controllers (yet to be merged) + * NPU/NPU2/NPU3 controllers + * EEH support for PCIe Host bridge controllers + * NX controller + * VAS controller + * chipTOD (Time Of Day) + * Self Boot Engine (SBE). + * FSI bus + +Firmware +-------- + +The OPAL firmware (OpenPower Abstraction Layer) for OpenPower systems +includes the runtime services `skiboot` and the bootloader kernel and +initramfs `skiroot`. Source code can be found on GitHub: + + https://github.com/open-power. + +Prebuilt images of `skiboot` and `skiboot` are made available on the `OpenPOWER <https://openpower.xyz/job/openpower/job/openpower-op-build/>`__ site. To boot a POWER9 machine, use the `witherspoon <https://openpower.xyz/job/openpower/job/openpower-op-build/label=slave,target=witherspoon/lastSuccessfulBuild/>`__ images. For POWER8, use +the `palmetto <https://openpower.xyz/job/openpower/job/openpower-op-build/label=slave,target=palmetto/lastSuccessfulBuild/>`__ images. + +QEMU includes a prebuilt image of `skiboot` which is updated when a +more recent version is required by the models. + +Boot options +------------ + +Here is a simple setup with one e1000e NIC : + +.. code-block:: bash + + $ qemu-system-ppc64 -m 2G -machine powernv9 -smp 2,cores=2,threads=1 \ + -accel tcg,thread=single \ + -device e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0 \ + -netdev user,id=net0,hostfwd=::20022-:22,hostname=pnv \ + -kernel ./zImage.epapr \ + -initrd ./rootfs.cpio.xz \ + -nographic + +and a SATA disk : + +.. code-block:: bash + + -device ich9-ahci,id=sata0,bus=pcie.1,addr=0x0 \ + -drive file=./ubuntu-ppc64le.qcow2,if=none,id=drive0,format=qcow2,cache=none \ + -device ide-hd,bus=sata0.0,unit=0,drive=drive0,id=ide,bootindex=1 \ + +Complex PCIe configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Six PHBs are defined per chip (POWER9) but no default PCI layout is +provided (to be compatible with libvirt). One PCI device can be added +on any of the available PCIe slots using command line options such as: + +.. code-block:: bash + + -device e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0 + -netdev bridge,id=net0,helper=/usr/libexec/qemu-bridge-helper,br=virbr0,id=hostnet0 + + -device megasas,id=scsi0,bus=pcie.0,addr=0x0 + -drive file=./ubuntu-ppc64le.qcow2,if=none,id=drive-scsi0-0-0-0,format=qcow2,cache=none + -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,bootindex=2 + +Here is a full example with two different storage controllers on +different PHBs, each with a disk, the second PHB is empty : + +.. code-block:: bash + + $ qemu-system-ppc64 -m 2G -machine powernv9 -smp 2,cores=2,threads=1 -accel tcg,thread=single \ + -kernel ./zImage.epapr -initrd ./rootfs.cpio.xz -bios ./skiboot.lid \ + \ + -device megasas,id=scsi0,bus=pcie.0,addr=0x0 \ + -drive file=./rhel7-ppc64le.qcow2,if=none,id=drive-scsi0-0-0-0,format=qcow2,cache=none \ + -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,bootindex=2 \ + \ + -device pcie-pci-bridge,id=bridge1,bus=pcie.1,addr=0x0 \ + \ + -device ich9-ahci,id=sata0,bus=bridge1,addr=0x1 \ + -drive file=./ubuntu-ppc64le.qcow2,if=none,id=drive0,format=qcow2,cache=none \ + -device ide-hd,bus=sata0.0,unit=0,drive=drive0,id=ide,bootindex=1 \ + -device e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=bridge1,addr=0x2 \ + -netdev bridge,helper=/usr/libexec/qemu-bridge-helper,br=virbr0,id=net0 \ + -device nec-usb-xhci,bus=bridge1,addr=0x7 \ + \ + -serial mon:stdio -nographic + +You can also use VIRTIO devices : + +.. code-block:: bash + + -drive file=./fedora-ppc64le.qcow2,if=none,snapshot=on,id=drive0 \ + -device virtio-blk-pci,drive=drive0,id=blk0,bus=pcie.0 \ + \ + -netdev tap,helper=/usr/lib/qemu/qemu-bridge-helper,br=virbr0,id=netdev0 \ + -device virtio-net-pci,netdev=netdev0,id=net0,bus=pcie.1 \ + \ + -fsdev local,id=fsdev0,path=$HOME,security_model=passthrough \ + -device virtio-9p-pci,fsdev=fsdev0,mount_tag=host,bus=pcie.2 + +Multi sockets +~~~~~~~~~~~~~ + +The number of sockets is deduced from the number of CPUs and the +number of cores. ``-smp 2,cores=1`` will define a machine with 2 +sockets of 1 core, whereas ``-smp 2,cores=2`` will define a machine +with 1 socket of 2 cores. ``-smp 8,cores=2``, 4 sockets of 2 cores. + +BMC configuration +~~~~~~~~~~~~~~~~~ + +OpenPOWER systems negotiate the shutdown and reboot with their +BMC. The QEMU PowerNV machine embeds an IPMI BMC simulator using the +iBT interface and should offer the same power features. + +If you want to define your own BMC, use ``-nodefaults`` and specify +one on the command line : + +.. code-block:: bash + + -device ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10 + +The files `palmetto-SDR.bin <http://www.kaod.org/qemu/powernv/palmetto-SDR.bin>`__ +and `palmetto-FRU.bin <http://www.kaod.org/qemu/powernv/palmetto-FRU.bin>`__ +define a Sensor Data Record repository and a Field Replaceable Unit +inventory for a palmetto BMC. They can be used to extend the QEMU BMC +simulator. + +.. code-block:: bash + + -device ipmi-bmc-sim,sdrfile=./palmetto-SDR.bin,fruareasize=256,frudatafile=./palmetto-FRU.bin,id=bmc0 \ + -device isa-ipmi-bt,bmc=bmc0,irq=10 + +The PowerNV machine can also be run with an external IPMI BMC device +connected to a remote QEMU machine acting as BMC, using these options +: + +.. code-block:: bash + + -chardev socket,id=ipmi0,host=localhost,port=9002,reconnect=10 \ + -device ipmi-bmc-extern,id=bmc0,chardev=ipmi0 \ + -device isa-ipmi-bt,bmc=bmc0,irq=10 \ + -nodefaults + +NVRAM +~~~~~ + +Use a MTD drive to add a PNOR to the machine, and get a NVRAM : + +.. code-block:: bash + + -drive file=./witherspoon.pnor,format=raw,if=mtd + +CAVEATS +------- + + * No support for multiple HW threads (SMT=1). Same as pseries. + * CPU can hang when doing intensive I/Os. Use ``-append powersave=off`` in that case. diff --git a/docs/system/ppc/prep.rst b/docs/system/ppc/prep.rst new file mode 100644 index 0000000000..bd9eb8eabd --- /dev/null +++ b/docs/system/ppc/prep.rst @@ -0,0 +1,18 @@ +Prep machine (``40p``) +================================================================== + +Use the executable ``qemu-system-ppc`` to simulate a complete 40P (PREP) + +Supported devices +----------------- + +QEMU emulates the following 40P (PREP) peripherals: + + * PCI Bridge + * PCI VGA compatible card with VESA Bochs Extensions + * 2 IDE interfaces with hard disk and CD-ROM support + * Floppy disk + * PCnet network adapters + * Serial port + * PREP Non Volatile RAM + * PC compatible keyboard and mouse. diff --git a/docs/system/ppc/pseries.rst b/docs/system/ppc/pseries.rst new file mode 100644 index 0000000000..932d4dd17d --- /dev/null +++ b/docs/system/ppc/pseries.rst @@ -0,0 +1,12 @@ +pSeries family boards (``pseries``) +=================================== + +Supported devices +----------------- + +Missing devices +--------------- + + +Firmware +-------- diff --git a/docs/system/quickstart.rst b/docs/system/quickstart.rst index 3a3acab5e7..681678c86e 100644 --- a/docs/system/quickstart.rst +++ b/docs/system/quickstart.rst @@ -11,3 +11,11 @@ Download and uncompress a PC hard disk image with Linux installed (e.g. |qemu_system| linux.img Linux should boot and give you a prompt. + +Users should be aware the above example elides a lot of the complexity +of setting up a VM with x86_64 specific defaults and assumes the +first non switch argument is a PC compatible disk image with a boot +sector. For a non-x86 system where we emulate a broad range of machine +types, the command lines are generally more explicit in defining the +machine and boot behaviour. You will find more example command lines +in the :ref:`system-targets-ref` section of the manual. diff --git a/docs/system/target-ppc.rst b/docs/system/target-ppc.rst index a2f04c533c..67905b8f2a 100644 --- a/docs/system/target-ppc.rst +++ b/docs/system/target-ppc.rst @@ -3,45 +3,22 @@ PowerPC System emulator ----------------------- -Use the executable ``qemu-system-ppc`` to simulate a complete 40P (PREP) -or PowerMac PowerPC system. +Board-specific documentation +============================ -QEMU emulates the following PowerMac peripherals: +You can get a complete list by running ``qemu-system-ppc64 --machine +help``. -- UniNorth or Grackle PCI Bridge +.. + This table of contents should be kept sorted alphabetically + by the title text of each file, which isn't the same ordering + as an alphabetical sort by filename. -- PCI VGA compatible card with VESA Bochs Extensions +.. toctree:: + :maxdepth: 1 -- 2 PMAC IDE interfaces with hard disk and CD-ROM support - -- NE2000 PCI adapters - -- Non Volatile RAM - -- VIA-CUDA with ADB keyboard and mouse. - -QEMU emulates the following 40P (PREP) peripherals: - -- PCI Bridge - -- PCI VGA compatible card with VESA Bochs Extensions - -- 2 IDE interfaces with hard disk and CD-ROM support - -- Floppy disk - -- PCnet network adapters - -- Serial port - -- PREP Non Volatile RAM - -- PC compatible keyboard and mouse. - -Since version 0.9.1, QEMU uses OpenBIOS https://www.openbios.org/ for -the g3beige and mac99 PowerMac and the 40p machines. OpenBIOS is a free -(GPL v2) portable firmware implementation. The goal is to implement a -100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. - -More information is available at -http://perso.magic.fr/l_indien/qemu-ppc/. + ppc/embedded + ppc/powermac + ppc/powernv + ppc/prep + ppc/pseries diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 75ed1087fd..9dcd95dd84 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst @@ -1,3 +1,5 @@ +.. _system-targets-ref: + QEMU System Emulator Targets ============================ diff --git a/gdbstub.c b/gdbstub.c index 16d7c8f534..054665e93e 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -49,7 +49,7 @@ #include "sysemu/hw_accel.h" #include "sysemu/kvm.h" #include "sysemu/runstate.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "exec/exec-all.h" #include "sysemu/replay.h" @@ -2754,7 +2754,7 @@ void gdb_set_stop_cpu(CPUState *cpu) } #ifndef CONFIG_USER_ONLY -static void gdb_vm_state_change(void *opaque, int running, RunState state) +static void gdb_vm_state_change(void *opaque, bool running, RunState state) { CPUState *cpu = gdbserver_state.c_cpu; g_autoptr(GString) buf = g_string_new(NULL); diff --git a/hw/Kconfig b/hw/Kconfig index 8ea26479c4..ff40bd3f7b 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -31,7 +31,6 @@ source remote/Kconfig source rtc/Kconfig source scsi/Kconfig source sd/Kconfig -source semihosting/Kconfig source smbios/Kconfig source ssi/Kconfig source timer/Kconfig diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index bf31ca351f..bc87e754a3 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -65,7 +65,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { #define ASPEED_A7MPCORE_ADDR 0x40460000 -#define ASPEED_SOC_AST2600_MAX_IRQ 128 +#define AST2600_MAX_IRQ 197 /* Shared Peripheral Interrupt values below are offset by -32 from datasheet */ static const int aspeed_soc_ast2600_irqmap[] = { @@ -98,13 +98,13 @@ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_DEV_WDT] = 24, [ASPEED_DEV_PWM] = 44, [ASPEED_DEV_LPC] = 35, - [ASPEED_DEV_IBT] = 35, /* LPC */ + [ASPEED_DEV_IBT] = 143, [ASPEED_DEV_I2C] = 110, /* 110 -> 125 */ [ASPEED_DEV_ETH1] = 2, [ASPEED_DEV_ETH2] = 3, [ASPEED_DEV_ETH3] = 32, [ASPEED_DEV_ETH4] = 33, - + [ASPEED_DEV_KCS] = 138, /* 138 -> 142 */ }; static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl) @@ -211,6 +211,8 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "emmc-controller.sdhci", &s->emmc.slots[0], TYPE_SYSBUS_SDHCI); + + object_initialize_child(obj, "lpc", &s->lpc, TYPE_ASPEED_LPC); } /* @@ -241,8 +243,6 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) /* CPU */ for (i = 0; i < sc->num_cpus; i++) { - object_property_set_int(OBJECT(&s->cpu[i]), "psci-conduit", - QEMU_PSCI_CONDUIT_SMC, &error_abort); if (sc->num_cpus > 1) { object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", ASPEED_A7MPCORE_ADDR, &error_abort); @@ -253,11 +253,6 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 1125000000, &error_abort); - /* - * TODO: the secondary CPUs are started and a boot helper - * is needed when using -kernel - */ - if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { return; } @@ -267,7 +262,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(&s->a7mpcore), "num-cpu", sc->num_cpus, &error_abort); object_property_set_int(OBJECT(&s->a7mpcore), "num-irq", - ASPEED_SOC_AST2600_MAX_IRQ + GIC_INTERNAL, + ROUND_UP(AST2600_MAX_IRQ + GIC_INTERNAL, 32), &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->a7mpcore), &error_abort); @@ -469,6 +464,40 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_DEV_EMMC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); + + /* LPC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + + /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_LPC)); + + /* + * On the AST2600 LPC subdevice IRQs are connected straight to the GIC. + * + * LPC subdevice IRQ sources are offset from 1 because the LPC model caters + * to the AST2400 and AST2500. SoCs before the AST2600 have one LPC IRQ + * shared across the subdevices, and the shared IRQ output to the VIC is at + * offset 0. + */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_1)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_2)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_3)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); } static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 7eefd54ac0..057d053c84 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -112,7 +112,6 @@ static const int aspeed_soc_ast2400_irqmap[] = { [ASPEED_DEV_WDT] = 27, [ASPEED_DEV_PWM] = 28, [ASPEED_DEV_LPC] = 8, - [ASPEED_DEV_IBT] = 8, /* LPC */ [ASPEED_DEV_I2C] = 12, [ASPEED_DEV_ETH1] = 2, [ASPEED_DEV_ETH2] = 3, @@ -211,6 +210,8 @@ static void aspeed_soc_init(Object *obj) object_initialize_child(obj, "sdhci[*]", &s->sdhci.slots[i], TYPE_SYSBUS_SDHCI); } + + object_initialize_child(obj, "lpc", &s->lpc, TYPE_ASPEED_LPC); } static void aspeed_soc_realize(DeviceState *dev, Error **errp) @@ -393,6 +394,37 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); + + /* LPC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + + /* Connect the LPC IRQ to the VIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_LPC)); + + /* + * On the AST2400 and AST2500 the one LPC IRQ is shared between all of the + * subdevices. Connect the LPC subdevice IRQs to the LPC controller IRQ (by + * contrast, on the AST2600, the subdevice IRQs are connected straight to + * the GIC). + * + * LPC subdevice IRQ sources are offset from 1 because the shared IRQ output + * to the VIC is at offset 0. + */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, + qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_1)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, + qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_2)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, + qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_3)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, + qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_4)); } static Property aspeed_soc_properties[] = { DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 371147f3ae..c08bf11297 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -218,14 +218,14 @@ static bool cpu_type_valid(const char *cpu) return false; } -static void create_kaslr_seed(VirtMachineState *vms, const char *node) +static void create_kaslr_seed(MachineState *ms, const char *node) { uint64_t seed; if (qemu_guest_getrandom(&seed, sizeof(seed), NULL)) { return; } - qemu_fdt_setprop_u64(vms->fdt, node, "kaslr-seed", seed); + qemu_fdt_setprop_u64(ms->fdt, node, "kaslr-seed", seed); } static void create_fdt(VirtMachineState *vms) @@ -239,7 +239,7 @@ static void create_fdt(VirtMachineState *vms) exit(1); } - vms->fdt = fdt; + ms->fdt = fdt; /* Header */ qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); @@ -248,11 +248,11 @@ static void create_fdt(VirtMachineState *vms) /* /chosen must exist for load_dtb to fill in necessary properties later */ qemu_fdt_add_subnode(fdt, "/chosen"); - create_kaslr_seed(vms, "/chosen"); + create_kaslr_seed(ms, "/chosen"); if (vms->secure) { qemu_fdt_add_subnode(fdt, "/secure-chosen"); - create_kaslr_seed(vms, "/secure-chosen"); + create_kaslr_seed(ms, "/secure-chosen"); } /* Clock node, for the benefit of the UART. The kernel device tree @@ -316,6 +316,7 @@ static void fdt_add_timer_nodes(const VirtMachineState *vms) ARMCPU *armcpu; VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI; + MachineState *ms = MACHINE(vms); if (vmc->claim_edge_triggered_timers) { irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; @@ -327,19 +328,19 @@ static void fdt_add_timer_nodes(const VirtMachineState *vms) (1 << MACHINE(vms)->smp.cpus) - 1); } - qemu_fdt_add_subnode(vms->fdt, "/timer"); + qemu_fdt_add_subnode(ms->fdt, "/timer"); armcpu = ARM_CPU(qemu_get_cpu(0)); if (arm_feature(&armcpu->env, ARM_FEATURE_V8)) { const char compat[] = "arm,armv8-timer\0arm,armv7-timer"; - qemu_fdt_setprop(vms->fdt, "/timer", "compatible", + qemu_fdt_setprop(ms->fdt, "/timer", "compatible", compat, sizeof(compat)); } else { - qemu_fdt_setprop_string(vms->fdt, "/timer", "compatible", + qemu_fdt_setprop_string(ms->fdt, "/timer", "compatible", "arm,armv7-timer"); } - qemu_fdt_setprop(vms->fdt, "/timer", "always-on", NULL, 0); - qemu_fdt_setprop_cells(vms->fdt, "/timer", "interrupts", + qemu_fdt_setprop(ms->fdt, "/timer", "always-on", NULL, 0); + qemu_fdt_setprop_cells(ms->fdt, "/timer", "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_S_EL1_IRQ, irqflags, GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL1_IRQ, irqflags, GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_VIRT_IRQ, irqflags, @@ -375,35 +376,35 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) } } - qemu_fdt_add_subnode(vms->fdt, "/cpus"); - qemu_fdt_setprop_cell(vms->fdt, "/cpus", "#address-cells", addr_cells); - qemu_fdt_setprop_cell(vms->fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_add_subnode(ms->fdt, "/cpus"); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", addr_cells); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); for (cpu = smp_cpus - 1; cpu >= 0; cpu--) { char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu); ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); CPUState *cs = CPU(armcpu); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "cpu"); - qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", armcpu->dtb_compatible); if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED && smp_cpus > 1) { - qemu_fdt_setprop_string(vms->fdt, nodename, + qemu_fdt_setprop_string(ms->fdt, nodename, "enable-method", "psci"); } if (addr_cells == 2) { - qemu_fdt_setprop_u64(vms->fdt, nodename, "reg", + qemu_fdt_setprop_u64(ms->fdt, nodename, "reg", armcpu->mp_affinity); } else { - qemu_fdt_setprop_cell(vms->fdt, nodename, "reg", + qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", armcpu->mp_affinity); } if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) { - qemu_fdt_setprop_cell(vms->fdt, nodename, "numa-node-id", + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", ms->possible_cpus->cpus[cs->cpu_index].props.node_id); } @@ -414,71 +415,74 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) static void fdt_add_its_gic_node(VirtMachineState *vms) { char *nodename; + MachineState *ms = MACHINE(vms); - vms->msi_phandle = qemu_fdt_alloc_phandle(vms->fdt); + vms->msi_phandle = qemu_fdt_alloc_phandle(ms->fdt); nodename = g_strdup_printf("/intc/its@%" PRIx64, vms->memmap[VIRT_GIC_ITS].base); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "arm,gic-v3-its"); - qemu_fdt_setprop(vms->fdt, nodename, "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop(ms->fdt, nodename, "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_ITS].base, 2, vms->memmap[VIRT_GIC_ITS].size); - qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->msi_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", vms->msi_phandle); g_free(nodename); } static void fdt_add_v2m_gic_node(VirtMachineState *vms) { + MachineState *ms = MACHINE(vms); char *nodename; nodename = g_strdup_printf("/intc/v2m@%" PRIx64, vms->memmap[VIRT_GIC_V2M].base); - vms->msi_phandle = qemu_fdt_alloc_phandle(vms->fdt); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", + vms->msi_phandle = qemu_fdt_alloc_phandle(ms->fdt); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "arm,gic-v2m-frame"); - qemu_fdt_setprop(vms->fdt, nodename, "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop(ms->fdt, nodename, "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_V2M].base, 2, vms->memmap[VIRT_GIC_V2M].size); - qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->msi_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", vms->msi_phandle); g_free(nodename); } static void fdt_add_gic_node(VirtMachineState *vms) { + MachineState *ms = MACHINE(vms); char *nodename; - vms->gic_phandle = qemu_fdt_alloc_phandle(vms->fdt); - qemu_fdt_setprop_cell(vms->fdt, "/", "interrupt-parent", vms->gic_phandle); + vms->gic_phandle = qemu_fdt_alloc_phandle(ms->fdt); + qemu_fdt_setprop_cell(ms->fdt, "/", "interrupt-parent", vms->gic_phandle); nodename = g_strdup_printf("/intc@%" PRIx64, vms->memmap[VIRT_GIC_DIST].base); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 3); - qemu_fdt_setprop(vms->fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 0x2); - qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 0x2); - qemu_fdt_setprop(vms->fdt, nodename, "ranges", NULL, 0); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 3); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2); + qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0); if (vms->gic_version == VIRT_GIC_VERSION_3) { int nb_redist_regions = virt_gicv3_redist_region_count(vms); - qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "arm,gic-v3"); - qemu_fdt_setprop_cell(vms->fdt, nodename, + qemu_fdt_setprop_cell(ms->fdt, nodename, "#redistributor-regions", nb_redist_regions); if (nb_redist_regions == 1) { - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_DIST].base, 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_REDIST].base, 2, vms->memmap[VIRT_GIC_REDIST].size); } else { - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_DIST].base, 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_REDIST].base, @@ -488,22 +492,22 @@ static void fdt_add_gic_node(VirtMachineState *vms) } if (vms->virt) { - qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } else { /* 'cortex-a15-gic' means 'GIC v2' */ - qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "arm,cortex-a15-gic"); if (!vms->virt) { - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_DIST].base, 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_CPU].base, 2, vms->memmap[VIRT_GIC_CPU].size); } else { - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_DIST].base, 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_CPU].base, @@ -512,13 +516,13 @@ static void fdt_add_gic_node(VirtMachineState *vms) 2, vms->memmap[VIRT_GIC_HYP].size, 2, vms->memmap[VIRT_GIC_VCPU].base, 2, vms->memmap[VIRT_GIC_VCPU].size); - qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } - qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->gic_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", vms->gic_phandle); g_free(nodename); } @@ -526,6 +530,7 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) { ARMCPU *armcpu = ARM_CPU(first_cpu); uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI; + MachineState *ms = MACHINE(vms); if (!arm_feature(&armcpu->env, ARM_FEATURE_PMU)) { assert(!object_property_get_bool(OBJECT(armcpu), "pmu", NULL)); @@ -538,12 +543,12 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) (1 << MACHINE(vms)->smp.cpus) - 1); } - qemu_fdt_add_subnode(vms->fdt, "/pmu"); + qemu_fdt_add_subnode(ms->fdt, "/pmu"); if (arm_feature(&armcpu->env, ARM_FEATURE_V8)) { const char compat[] = "arm,armv8-pmuv3"; - qemu_fdt_setprop(vms->fdt, "/pmu", "compatible", + qemu_fdt_setprop(ms->fdt, "/pmu", "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_cells(vms->fdt, "/pmu", "interrupts", + qemu_fdt_setprop_cells(ms->fdt, "/pmu", "interrupts", GIC_FDT_IRQ_TYPE_PPI, VIRTUAL_PMU_IRQ, irqflags); } } @@ -749,6 +754,7 @@ static void create_uart(const VirtMachineState *vms, int uart, const char clocknames[] = "uartclk\0apb_pclk"; DeviceState *dev = qdev_new(TYPE_PL011); SysBusDevice *s = SYS_BUS_DEVICE(dev); + MachineState *ms = MACHINE(vms); qdev_prop_set_chr(dev, "chardev", chr); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -757,28 +763,28 @@ static void create_uart(const VirtMachineState *vms, int uart, sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); nodename = g_strdup_printf("/pl011@%" PRIx64, base); - qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_add_subnode(ms->fdt, nodename); /* Note that we can't use setprop_string because of the embedded NUL */ - qemu_fdt_setprop(vms->fdt, nodename, "compatible", + qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_cells(vms->fdt, nodename, "clocks", + qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", vms->clock_phandle, vms->clock_phandle); - qemu_fdt_setprop(vms->fdt, nodename, "clock-names", + qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames, sizeof(clocknames)); if (uart == VIRT_UART) { - qemu_fdt_setprop_string(vms->fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); } else { /* Mark as not usable by the normal world */ - qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled"); - qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay"); + qemu_fdt_setprop_string(ms->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(ms->fdt, nodename, "secure-status", "okay"); - qemu_fdt_setprop_string(vms->fdt, "/secure-chosen", "stdout-path", + qemu_fdt_setprop_string(ms->fdt, "/secure-chosen", "stdout-path", nodename); } @@ -792,19 +798,20 @@ static void create_rtc(const VirtMachineState *vms) hwaddr size = vms->memmap[VIRT_RTC].size; int irq = vms->irqmap[VIRT_RTC]; const char compat[] = "arm,pl031\0arm,primecell"; + MachineState *ms = MACHINE(vms); sysbus_create_simple("pl031", base, qdev_get_gpio_in(vms->gic, irq)); nodename = g_strdup_printf("/pl031@%" PRIx64, base); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop(vms->fdt, nodename, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_cell(vms->fdt, nodename, "clocks", vms->clock_phandle); - qemu_fdt_setprop_string(vms->fdt, nodename, "clock-names", "apb_pclk"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "clocks", vms->clock_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "clock-names", "apb_pclk"); g_free(nodename); } @@ -821,32 +828,30 @@ static void virt_powerdown_req(Notifier *n, void *opaque) } } -static void create_gpio_keys(const VirtMachineState *vms, - DeviceState *pl061_dev, +static void create_gpio_keys(char *fdt, DeviceState *pl061_dev, uint32_t phandle) { gpio_key_dev = sysbus_create_simple("gpio-key", -1, qdev_get_gpio_in(pl061_dev, 3)); - qemu_fdt_add_subnode(vms->fdt, "/gpio-keys"); - qemu_fdt_setprop_string(vms->fdt, "/gpio-keys", "compatible", "gpio-keys"); - qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys", "#size-cells", 0); - qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys", "#address-cells", 1); + qemu_fdt_add_subnode(fdt, "/gpio-keys"); + qemu_fdt_setprop_string(fdt, "/gpio-keys", "compatible", "gpio-keys"); + qemu_fdt_setprop_cell(fdt, "/gpio-keys", "#size-cells", 0); + qemu_fdt_setprop_cell(fdt, "/gpio-keys", "#address-cells", 1); - qemu_fdt_add_subnode(vms->fdt, "/gpio-keys/poweroff"); - qemu_fdt_setprop_string(vms->fdt, "/gpio-keys/poweroff", + qemu_fdt_add_subnode(fdt, "/gpio-keys/poweroff"); + qemu_fdt_setprop_string(fdt, "/gpio-keys/poweroff", "label", "GPIO Key Poweroff"); - qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys/poweroff", "linux,code", + qemu_fdt_setprop_cell(fdt, "/gpio-keys/poweroff", "linux,code", KEY_POWER); - qemu_fdt_setprop_cells(vms->fdt, "/gpio-keys/poweroff", + qemu_fdt_setprop_cells(fdt, "/gpio-keys/poweroff", "gpios", phandle, 3, 0); } #define SECURE_GPIO_POWEROFF 0 #define SECURE_GPIO_RESET 1 -static void create_secure_gpio_pwr(const VirtMachineState *vms, - DeviceState *pl061_dev, +static void create_secure_gpio_pwr(char *fdt, DeviceState *pl061_dev, uint32_t phandle) { DeviceState *gpio_pwr_dev; @@ -860,22 +865,22 @@ static void create_secure_gpio_pwr(const VirtMachineState *vms, qdev_connect_gpio_out(pl061_dev, SECURE_GPIO_POWEROFF, qdev_get_gpio_in_named(gpio_pwr_dev, "shutdown", 0)); - qemu_fdt_add_subnode(vms->fdt, "/gpio-poweroff"); - qemu_fdt_setprop_string(vms->fdt, "/gpio-poweroff", "compatible", + qemu_fdt_add_subnode(fdt, "/gpio-poweroff"); + qemu_fdt_setprop_string(fdt, "/gpio-poweroff", "compatible", "gpio-poweroff"); - qemu_fdt_setprop_cells(vms->fdt, "/gpio-poweroff", + qemu_fdt_setprop_cells(fdt, "/gpio-poweroff", "gpios", phandle, SECURE_GPIO_POWEROFF, 0); - qemu_fdt_setprop_string(vms->fdt, "/gpio-poweroff", "status", "disabled"); - qemu_fdt_setprop_string(vms->fdt, "/gpio-poweroff", "secure-status", + qemu_fdt_setprop_string(fdt, "/gpio-poweroff", "status", "disabled"); + qemu_fdt_setprop_string(fdt, "/gpio-poweroff", "secure-status", "okay"); - qemu_fdt_add_subnode(vms->fdt, "/gpio-restart"); - qemu_fdt_setprop_string(vms->fdt, "/gpio-restart", "compatible", + qemu_fdt_add_subnode(fdt, "/gpio-restart"); + qemu_fdt_setprop_string(fdt, "/gpio-restart", "compatible", "gpio-restart"); - qemu_fdt_setprop_cells(vms->fdt, "/gpio-restart", + qemu_fdt_setprop_cells(fdt, "/gpio-restart", "gpios", phandle, SECURE_GPIO_RESET, 0); - qemu_fdt_setprop_string(vms->fdt, "/gpio-restart", "status", "disabled"); - qemu_fdt_setprop_string(vms->fdt, "/gpio-restart", "secure-status", + qemu_fdt_setprop_string(fdt, "/gpio-restart", "status", "disabled"); + qemu_fdt_setprop_string(fdt, "/gpio-restart", "secure-status", "okay"); } @@ -889,6 +894,7 @@ static void create_gpio_devices(const VirtMachineState *vms, int gpio, int irq = vms->irqmap[gpio]; const char compat[] = "arm,pl061\0arm,primecell"; SysBusDevice *s; + MachineState *ms = MACHINE(vms); pl061_dev = qdev_new("pl061"); s = SYS_BUS_DEVICE(pl061_dev); @@ -896,33 +902,33 @@ static void create_gpio_devices(const VirtMachineState *vms, int gpio, memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); - uint32_t phandle = qemu_fdt_alloc_phandle(vms->fdt); + uint32_t phandle = qemu_fdt_alloc_phandle(ms->fdt); nodename = g_strdup_printf("/pl061@%" PRIx64, base); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop(vms->fdt, nodename, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_cell(vms->fdt, nodename, "#gpio-cells", 2); - qemu_fdt_setprop(vms->fdt, nodename, "gpio-controller", NULL, 0); - qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", + qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#gpio-cells", 2); + qemu_fdt_setprop(ms->fdt, nodename, "gpio-controller", NULL, 0); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_cell(vms->fdt, nodename, "clocks", vms->clock_phandle); - qemu_fdt_setprop_string(vms->fdt, nodename, "clock-names", "apb_pclk"); - qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "clocks", vms->clock_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "clock-names", "apb_pclk"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", phandle); if (gpio != VIRT_GPIO) { /* Mark as not usable by the normal world */ - qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled"); - qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay"); + qemu_fdt_setprop_string(ms->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(ms->fdt, nodename, "secure-status", "okay"); } g_free(nodename); /* Child gpio devices */ if (gpio == VIRT_GPIO) { - create_gpio_keys(vms, pl061_dev, phandle); + create_gpio_keys(ms->fdt, pl061_dev, phandle); } else { - create_secure_gpio_pwr(vms, pl061_dev, phandle); + create_secure_gpio_pwr(ms->fdt, pl061_dev, phandle); } } @@ -930,6 +936,7 @@ static void create_virtio_devices(const VirtMachineState *vms) { int i; hwaddr size = vms->memmap[VIRT_MMIO].size; + MachineState *ms = MACHINE(vms); /* We create the transports in forwards order. Since qbus_realize() * prepends (not appends) new child buses, the incrementing loop below will @@ -979,15 +986,15 @@ static void create_virtio_devices(const VirtMachineState *vms) hwaddr base = vms->memmap[VIRT_MMIO].base + i * size; nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "virtio,mmio"); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - qemu_fdt_setprop(vms->fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); g_free(nodename); } } @@ -1068,17 +1075,18 @@ static void virt_flash_fdt(VirtMachineState *vms, { hwaddr flashsize = vms->memmap[VIRT_FLASH].size / 2; hwaddr flashbase = vms->memmap[VIRT_FLASH].base; + MachineState *ms = MACHINE(vms); char *nodename; if (sysmem == secure_sysmem) { /* Report both flash devices as a single node in the DT */ nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, flashbase, 2, flashsize, 2, flashbase + flashsize, 2, flashsize); - qemu_fdt_setprop_cell(vms->fdt, nodename, "bank-width", 4); + qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4); g_free(nodename); } else { /* @@ -1086,21 +1094,21 @@ static void virt_flash_fdt(VirtMachineState *vms, * only visible to the secure world. */ nodename = g_strdup_printf("/secflash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, flashbase, 2, flashsize); - qemu_fdt_setprop_cell(vms->fdt, nodename, "bank-width", 4); - qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled"); - qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4); + qemu_fdt_setprop_string(ms->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(ms->fdt, nodename, "secure-status", "okay"); g_free(nodename); nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, flashbase + flashsize, 2, flashsize); - qemu_fdt_setprop_cell(vms->fdt, nodename, "bank-width", 4); + qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4); g_free(nodename); } } @@ -1167,17 +1175,17 @@ static FWCfgState *create_fw_cfg(const VirtMachineState *vms, AddressSpace *as) fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)ms->smp.cpus); nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "qemu,fw-cfg-mmio"); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop(vms->fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); g_free(nodename); return fw_cfg; } -static void create_pcie_irq_map(const VirtMachineState *vms, +static void create_pcie_irq_map(const MachineState *ms, uint32_t gic_phandle, int first_irq, const char *nodename) { @@ -1205,10 +1213,10 @@ static void create_pcie_irq_map(const VirtMachineState *vms, } } - qemu_fdt_setprop(vms->fdt, nodename, "interrupt-map", + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map, sizeof(full_irq_map)); - qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupt-map-mask", + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask", cpu_to_be16(PCI_DEVFN(3, 0)), /* Slot 3 */ 0, 0, 0x7 /* PCI irq */); @@ -1225,6 +1233,7 @@ static void create_smmu(const VirtMachineState *vms, hwaddr size = vms->memmap[VIRT_SMMU].size; const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror"; DeviceState *dev; + MachineState *ms = MACHINE(vms); if (vms->iommu != VIRT_IOMMU_SMMUV3 || !vms->iommu_phandle) { return; @@ -1242,26 +1251,26 @@ static void create_smmu(const VirtMachineState *vms, } node = g_strdup_printf("/smmuv3@%" PRIx64, base); - qemu_fdt_add_subnode(vms->fdt, node); - qemu_fdt_setprop(vms->fdt, node, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(vms->fdt, node, "reg", 2, base, 2, size); + qemu_fdt_add_subnode(ms->fdt, node); + qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", 2, base, 2, size); - qemu_fdt_setprop_cells(vms->fdt, node, "interrupts", + qemu_fdt_setprop_cells(ms->fdt, node, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - qemu_fdt_setprop(vms->fdt, node, "interrupt-names", irq_names, + qemu_fdt_setprop(ms->fdt, node, "interrupt-names", irq_names, sizeof(irq_names)); - qemu_fdt_setprop_cell(vms->fdt, node, "clocks", vms->clock_phandle); - qemu_fdt_setprop_string(vms->fdt, node, "clock-names", "apb_pclk"); - qemu_fdt_setprop(vms->fdt, node, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, node, "clocks", vms->clock_phandle); + qemu_fdt_setprop_string(ms->fdt, node, "clock-names", "apb_pclk"); + qemu_fdt_setprop(ms->fdt, node, "dma-coherent", NULL, 0); - qemu_fdt_setprop_cell(vms->fdt, node, "#iommu-cells", 1); + qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1); - qemu_fdt_setprop_cell(vms->fdt, node, "phandle", vms->iommu_phandle); + qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle); g_free(node); } @@ -1269,22 +1278,23 @@ static void create_virtio_iommu_dt_bindings(VirtMachineState *vms) { const char compat[] = "virtio,pci-iommu"; uint16_t bdf = vms->virtio_iommu_bdf; + MachineState *ms = MACHINE(vms); char *node; - vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt); + vms->iommu_phandle = qemu_fdt_alloc_phandle(ms->fdt); node = g_strdup_printf("%s/virtio_iommu@%d", vms->pciehb_nodename, bdf); - qemu_fdt_add_subnode(vms->fdt, node); - qemu_fdt_setprop(vms->fdt, node, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(vms->fdt, node, "reg", + qemu_fdt_add_subnode(ms->fdt, node); + qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", 1, bdf << 8, 1, 0, 1, 0, 1, 0, 1, 0); - qemu_fdt_setprop_cell(vms->fdt, node, "#iommu-cells", 1); - qemu_fdt_setprop_cell(vms->fdt, node, "phandle", vms->iommu_phandle); + qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1); + qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle); g_free(node); - qemu_fdt_setprop_cells(vms->fdt, vms->pciehb_nodename, "iommu-map", + qemu_fdt_setprop_cells(ms->fdt, vms->pciehb_nodename, "iommu-map", 0x0, vms->iommu_phandle, 0x0, bdf, bdf + 1, vms->iommu_phandle, bdf + 1, 0xffff - bdf); } @@ -1309,6 +1319,7 @@ static void create_pcie(VirtMachineState *vms) char *nodename; int i, ecam_id; PCIHostState *pci; + MachineState *ms = MACHINE(vms); dev = qdev_new(TYPE_GPEX_HOST); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -1369,27 +1380,27 @@ static void create_pcie(VirtMachineState *vms) } nodename = vms->pciehb_nodename = g_strdup_printf("/pcie@%" PRIx64, base); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "pci-host-ecam-generic"); - qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "pci"); - qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 3); - qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 2); - qemu_fdt_setprop_cell(vms->fdt, nodename, "linux,pci-domain", 0); - qemu_fdt_setprop_cells(vms->fdt, nodename, "bus-range", 0, + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0); + qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); - qemu_fdt_setprop(vms->fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); if (vms->msi_phandle) { - qemu_fdt_setprop_cells(vms->fdt, nodename, "msi-parent", + qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-parent", vms->msi_phandle); } - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base_ecam, 2, size_ecam); if (vms->highmem) { - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "ranges", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges", 1, FDT_PCI_RANGE_IOPORT, 2, 0, 2, base_pio, 2, size_pio, 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, @@ -1398,23 +1409,23 @@ static void create_pcie(VirtMachineState *vms) 2, base_mmio_high, 2, base_mmio_high, 2, size_mmio_high); } else { - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "ranges", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges", 1, FDT_PCI_RANGE_IOPORT, 2, 0, 2, base_pio, 2, size_pio, 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, 2, base_mmio, 2, size_mmio); } - qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 1); - create_pcie_irq_map(vms, vms->gic_phandle, irq, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); + create_pcie_irq_map(ms, vms->gic_phandle, irq, nodename); if (vms->iommu) { - vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt); + vms->iommu_phandle = qemu_fdt_alloc_phandle(ms->fdt); switch (vms->iommu) { case VIRT_IOMMU_SMMUV3: create_smmu(vms, vms->bus); - qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map", + qemu_fdt_setprop_cells(ms->fdt, nodename, "iommu-map", 0x0, vms->iommu_phandle, 0x0, 0x10000); break; default: @@ -1466,17 +1477,18 @@ static void create_secure_ram(VirtMachineState *vms, char *nodename; hwaddr base = vms->memmap[VIRT_SECURE_MEM].base; hwaddr size = vms->memmap[VIRT_SECURE_MEM].size; + MachineState *ms = MACHINE(vms); memory_region_init_ram(secram, NULL, "virt.secure-ram", size, &error_fatal); memory_region_add_subregion(secure_sysmem, base, secram); nodename = g_strdup_printf("/secram@%" PRIx64, base); - qemu_fdt_add_subnode(vms->fdt, nodename); - qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "memory"); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled"); - qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay"); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_string(ms->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(ms->fdt, nodename, "secure-status", "okay"); if (secure_tag_sysmem) { create_tag_ram(secure_tag_sysmem, base, size, "mach-virt.secure-tag"); @@ -1489,9 +1501,11 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) { const VirtMachineState *board = container_of(binfo, VirtMachineState, bootinfo); + MachineState *ms = MACHINE(board); + *fdt_size = board->fdt_size; - return board->fdt; + return ms->fdt; } static void virt_build_smbios(VirtMachineState *vms) @@ -1539,7 +1553,7 @@ void virt_machine_done(Notifier *notifier, void *data) * while qemu takes charge of the qom stuff. */ if (info->dtb_filename == NULL) { - platform_bus_add_all_fdt_nodes(vms->fdt, "/intc", + platform_bus_add_all_fdt_nodes(ms->fdt, "/intc", vms->memmap[VIRT_PLATFORM_BUS].base, vms->memmap[VIRT_PLATFORM_BUS].size, vms->irqmap[VIRT_PLATFORM_BUS]); diff --git a/hw/block/meson.build b/hw/block/meson.build index 4bf994c64f..5492829155 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -13,7 +13,7 @@ softmmu_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) softmmu_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xen-block.c')) softmmu_ss.add(when: 'CONFIG_TC58128', if_true: files('tc58128.c')) -softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('nvme.c', 'nvme-ns.c')) +softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('nvme.c', 'nvme-ns.c', 'nvme-subsys.c')) specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c')) diff --git a/hw/block/nvme-ns.c b/hw/block/nvme-ns.c index 93ac6e107a..eda6a0c003 100644 --- a/hw/block/nvme-ns.c +++ b/hw/block/nvme-ns.c @@ -63,6 +63,15 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) id_ns->npda = id_ns->npdg = npdg - 1; + if (nvme_ns_shared(ns)) { + id_ns->nmic |= NVME_NMIC_NS_SHARED; + } + + /* simple copy */ + id_ns->mssrl = cpu_to_le16(ns->params.mssrl); + id_ns->mcl = cpu_to_le32(ns->params.mcl); + id_ns->msrc = ns->params.msrc; + return 0; } @@ -154,6 +163,18 @@ static int nvme_ns_zoned_check_calc_geometry(NvmeNamespace *ns, Error **errp) return -1; } + if (ns->params.max_active_zones) { + if (ns->params.max_open_zones > ns->params.max_active_zones) { + error_setg(errp, "max_open_zones (%u) exceeds max_active_zones (%u)", + ns->params.max_open_zones, ns->params.max_active_zones); + return -1; + } + + if (!ns->params.max_open_zones) { + ns->params.max_open_zones = ns->params.max_active_zones; + } + } + if (ns->params.zd_extension_size) { if (ns->params.zd_extension_size & 0x3f) { error_setg(errp, @@ -363,16 +384,27 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) return; } - if (nvme_register_namespace(n, ns, errp)) { - return; + if (ns->subsys) { + if (nvme_subsys_register_ns(ns, errp)) { + return; + } + } else { + if (nvme_register_namespace(n, ns, errp)) { + return; + } } - } static Property nvme_ns_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeNamespace, blkconf), + DEFINE_PROP_LINK("subsys", NvmeNamespace, subsys, TYPE_NVME_SUBSYS, + NvmeSubsystem *), + DEFINE_PROP_BOOL("detached", NvmeNamespace, params.detached, false), DEFINE_PROP_UINT32("nsid", NvmeNamespace, params.nsid, 0), DEFINE_PROP_UUID("uuid", NvmeNamespace, params.uuid), + DEFINE_PROP_UINT16("mssrl", NvmeNamespace, params.mssrl, 128), + DEFINE_PROP_UINT32("mcl", NvmeNamespace, params.mcl, 128), + DEFINE_PROP_UINT8("msrc", NvmeNamespace, params.msrc, 127), DEFINE_PROP_BOOL("zoned", NvmeNamespace, params.zoned, false), DEFINE_PROP_SIZE("zoned.zone_size", NvmeNamespace, params.zone_size_bs, NVME_DEFAULT_ZONE_SIZE), diff --git a/hw/block/nvme-ns.h b/hw/block/nvme-ns.h index 293ac990e3..318d3aebe1 100644 --- a/hw/block/nvme-ns.h +++ b/hw/block/nvme-ns.h @@ -26,9 +26,14 @@ typedef struct NvmeZone { } NvmeZone; typedef struct NvmeNamespaceParams { + bool detached; uint32_t nsid; QemuUUID uuid; + uint16_t mssrl; + uint32_t mcl; + uint8_t msrc; + bool zoned; bool cross_zone_read; uint64_t zone_size_bs; @@ -47,6 +52,9 @@ typedef struct NvmeNamespace { const uint32_t *iocs; uint8_t csi; + NvmeSubsystem *subsys; + QTAILQ_ENTRY(NvmeNamespace) entry; + NvmeIdNsZoned *id_ns_zoned; NvmeZone *zone_array; QTAILQ_HEAD(, NvmeZone) exp_open_zones; @@ -77,6 +85,11 @@ static inline uint32_t nvme_nsid(NvmeNamespace *ns) return -1; } +static inline bool nvme_ns_shared(NvmeNamespace *ns) +{ + return !!ns->subsys; +} + static inline NvmeLBAF *nvme_ns_lbaf(NvmeNamespace *ns) { NvmeIdNs *id_ns = &ns->id_ns; diff --git a/hw/block/nvme-subsys.c b/hw/block/nvme-subsys.c new file mode 100644 index 0000000000..af4804a819 --- /dev/null +++ b/hw/block/nvme-subsys.c @@ -0,0 +1,116 @@ +/* + * QEMU NVM Express Subsystem: nvme-subsys + * + * Copyright (c) 2021 Minwoo Im <minwoo.im.dev@gmail.com> + * + * This code is licensed under the GNU GPL v2. Refer COPYING. + */ + +#include "qemu/units.h" +#include "qemu/osdep.h" +#include "qemu/uuid.h" +#include "qemu/iov.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-core.h" +#include "hw/block/block.h" +#include "block/aio.h" +#include "block/accounting.h" +#include "sysemu/sysemu.h" +#include "hw/pci/pci.h" +#include "nvme.h" +#include "nvme-subsys.h" + +int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) +{ + NvmeSubsystem *subsys = n->subsys; + int cntlid; + + for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) { + if (!subsys->ctrls[cntlid]) { + break; + } + } + + if (cntlid == ARRAY_SIZE(subsys->ctrls)) { + error_setg(errp, "no more free controller id"); + return -1; + } + + subsys->ctrls[cntlid] = n; + + return cntlid; +} + +int nvme_subsys_register_ns(NvmeNamespace *ns, Error **errp) +{ + NvmeSubsystem *subsys = ns->subsys; + NvmeCtrl *n; + int i; + + if (subsys->namespaces[nvme_nsid(ns)]) { + error_setg(errp, "namespace %d already registerd to subsy %s", + nvme_nsid(ns), subsys->parent_obj.id); + return -1; + } + + subsys->namespaces[nvme_nsid(ns)] = ns; + + for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { + n = subsys->ctrls[i]; + + if (n && nvme_register_namespace(n, ns, errp)) { + return -1; + } + } + + return 0; +} + +static void nvme_subsys_setup(NvmeSubsystem *subsys) +{ + const char *nqn = subsys->params.nqn ? + subsys->params.nqn : subsys->parent_obj.id; + + snprintf((char *)subsys->subnqn, sizeof(subsys->subnqn), + "nqn.2019-08.org.qemu:%s", nqn); +} + +static void nvme_subsys_realize(DeviceState *dev, Error **errp) +{ + NvmeSubsystem *subsys = NVME_SUBSYS(dev); + + nvme_subsys_setup(subsys); +} + +static Property nvme_subsystem_props[] = { + DEFINE_PROP_STRING("nqn", NvmeSubsystem, params.nqn), + DEFINE_PROP_END_OF_LIST(), +}; + +static void nvme_subsys_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + + dc->realize = nvme_subsys_realize; + dc->desc = "Virtual NVMe subsystem"; + + device_class_set_props(dc, nvme_subsystem_props); +} + +static const TypeInfo nvme_subsys_info = { + .name = TYPE_NVME_SUBSYS, + .parent = TYPE_DEVICE, + .class_init = nvme_subsys_class_init, + .instance_size = sizeof(NvmeSubsystem), +}; + +static void nvme_subsys_register_types(void) +{ + type_register_static(&nvme_subsys_info); +} + +type_init(nvme_subsys_register_types) diff --git a/hw/block/nvme-subsys.h b/hw/block/nvme-subsys.h new file mode 100644 index 0000000000..fb66ae752a --- /dev/null +++ b/hw/block/nvme-subsys.h @@ -0,0 +1,60 @@ +/* + * QEMU NVM Express Subsystem: nvme-subsys + * + * Copyright (c) 2021 Minwoo Im <minwoo.im.dev@gmail.com> + * + * This code is licensed under the GNU GPL v2. Refer COPYING. + */ + +#ifndef NVME_SUBSYS_H +#define NVME_SUBSYS_H + +#define TYPE_NVME_SUBSYS "nvme-subsys" +#define NVME_SUBSYS(obj) \ + OBJECT_CHECK(NvmeSubsystem, (obj), TYPE_NVME_SUBSYS) + +#define NVME_SUBSYS_MAX_CTRLS 32 +#define NVME_SUBSYS_MAX_NAMESPACES 256 + +typedef struct NvmeCtrl NvmeCtrl; +typedef struct NvmeNamespace NvmeNamespace; +typedef struct NvmeSubsystem { + DeviceState parent_obj; + uint8_t subnqn[256]; + + NvmeCtrl *ctrls[NVME_SUBSYS_MAX_CTRLS]; + /* Allocated namespaces for this subsystem */ + NvmeNamespace *namespaces[NVME_SUBSYS_MAX_NAMESPACES + 1]; + + struct { + char *nqn; + } params; +} NvmeSubsystem; + +int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp); +int nvme_subsys_register_ns(NvmeNamespace *ns, Error **errp); + +static inline NvmeCtrl *nvme_subsys_ctrl(NvmeSubsystem *subsys, + uint32_t cntlid) +{ + if (!subsys) { + return NULL; + } + + return subsys->ctrls[cntlid]; +} + +/* + * Return allocated namespace of the specified nsid in the subsystem. + */ +static inline NvmeNamespace *nvme_subsys_ns(NvmeSubsystem *subsys, + uint32_t nsid) +{ + if (!subsys) { + return NULL; + } + + return subsys->namespaces[nsid]; +} + +#endif /* NVME_SUBSYS_H */ diff --git a/hw/block/nvme.c b/hw/block/nvme.c index fb83636abd..d439e44db8 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -17,14 +17,17 @@ /** * Usage: add options: * -drive file=<file>,if=none,id=<drive_id> + * -device nvme-subsys,id=<subsys_id>,nqn=<nqn_id> * -device nvme,serial=<serial>,id=<bus_name>, \ * cmb_size_mb=<cmb_size_mb[optional]>, \ * [pmrdev=<mem_backend_file_id>,] \ * max_ioqpairs=<N[optional]>, \ - * aerl=<N[optional]>, aer_max_queued=<N[optional]>, \ - * mdts=<N[optional]>,zoned.append_size_limit=<N[optional]> \ + * aerl=<N[optional]>,aer_max_queued=<N[optional]>, \ + * mdts=<N[optional]>,zoned.zasl=<N[optional]>, \ + * subsys=<subsys_id> * -device nvme-ns,drive=<drive_id>,bus=<bus_name>,nsid=<nsid>,\ - * zoned=<true|false[optional]> + * zoned=<true|false[optional]>, \ + * subsys=<subsys_id>,detached=<true|false[optional]> * * Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at * offset 0 in BAR2 and supports only WDS, RDS and SQS for now. By default, the @@ -38,9 +41,27 @@ * * The PMR will use BAR 4/5 exclusively. * + * To place controller(s) and namespace(s) to a subsystem, then provide + * nvme-subsys device as above. + * + * nvme subsystem device parameters + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * - `nqn` + * This parameter provides the `<nqn_id>` part of the string + * `nqn.2019-08.org.qemu:<nqn_id>` which will be reported in the SUBNQN field + * of subsystem controllers. Note that `<nqn_id>` should be unique per + * subsystem, but this is not enforced by QEMU. If not specified, it will + * default to the value of the `id` parameter (`<subsys_id>`). * * nvme device parameters * ~~~~~~~~~~~~~~~~~~~~~~ + * - `subsys` + * Specifying this parameter attaches the controller to the subsystem and + * the SUBNQN field in the controller will report the NQN of the subsystem + * device. This also enables multi controller capability represented in + * Identify Controller data structure in CMIC (Controller Multi-path I/O and + * Namesapce Sharing Capabilities). + * * - `aerl` * The Asynchronous Event Request Limit (AERL). Indicates the maximum number * of concurrently outstanding Asynchronous Event Request commands support @@ -51,13 +72,31 @@ * completion when there are no outstanding AERs. When the maximum number of * enqueued events are reached, subsequent events will be dropped. * - * - `zoned.append_size_limit` - * The maximum I/O size in bytes that is allowed in Zone Append command. - * The default is 128KiB. Since internally this this value is maintained as - * ZASL = log2(<maximum append size> / <page size>), some values assigned - * to this property may be rounded down and result in a lower maximum ZA - * data size being in effect. By setting this property to 0, users can make - * ZASL to be equal to MDTS. This property only affects zoned namespaces. + * - `mdts` + * Indicates the maximum data transfer size for a command that transfers data + * between host-accessible memory and the controller. The value is specified + * as a power of two (2^n) and is in units of the minimum memory page size + * (CAP.MPSMIN). The default value is 7 (i.e. 512 KiB). + * + * - `zoned.zasl` + * Indicates the maximum data transfer size for the Zone Append command. Like + * `mdts`, the value is specified as a power of two (2^n) and is in units of + * the minimum memory page size (CAP.MPSMIN). The default value is 0 (i.e. + * defaulting to the value of `mdts`). + * + * nvme namespace device parameters + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * - `subsys` + * If given, the namespace will be attached to all controllers in the + * subsystem. Otherwise, `bus` must be given to attach this namespace to a + * specific controller as a non-shared namespace. + * + * - `detached` + * This parameter is only valid together with the `subsys` parameter. If left + * at the default value (`false/off`), the namespace will be attached to all + * controllers in the NVMe subsystem at boot-up. If set to `true/on`, the + * namespace will be be available in the subsystem not not attached to any + * controllers. * * Setting `zoned` to true selects Zoned Command Set at the namespace. * In this case, the following namespace properties are available to configure @@ -157,6 +196,7 @@ static const uint32_t nvme_cse_acs[256] = { [NVME_ADM_CMD_SET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP, + [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC, }; static const uint32_t nvme_cse_iocs_none[256]; @@ -167,6 +207,7 @@ static const uint32_t nvme_cse_iocs_nvm[256] = { [NVME_CMD_WRITE] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_READ] = NVME_CMD_EFF_CSUPP, [NVME_CMD_DSM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, + [NVME_CMD_COPY] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_COMPARE] = NVME_CMD_EFF_CSUPP, }; @@ -176,6 +217,7 @@ static const uint32_t nvme_cse_iocs_zoned[256] = { [NVME_CMD_WRITE] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_READ] = NVME_CMD_EFF_CSUPP, [NVME_CMD_DSM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, + [NVME_CMD_COPY] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_COMPARE] = NVME_CMD_EFF_CSUPP, [NVME_CMD_ZONE_APPEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_ZONE_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, @@ -407,15 +449,31 @@ static void nvme_req_clear(NvmeRequest *req) req->status = NVME_SUCCESS; } -static void nvme_req_exit(NvmeRequest *req) +static inline void nvme_sg_init(NvmeCtrl *n, NvmeSg *sg, bool dma) { - if (req->qsg.sg) { - qemu_sglist_destroy(&req->qsg); + if (dma) { + pci_dma_sglist_init(&sg->qsg, &n->parent_obj, 0); + sg->flags = NVME_SG_DMA; + } else { + qemu_iovec_init(&sg->iov, 0); } - if (req->iov.iov) { - qemu_iovec_destroy(&req->iov); + sg->flags |= NVME_SG_ALLOC; +} + +static inline void nvme_sg_unmap(NvmeSg *sg) +{ + if (!(sg->flags & NVME_SG_ALLOC)) { + return; } + + if (sg->flags & NVME_SG_DMA) { + qemu_sglist_destroy(&sg->qsg); + } else { + qemu_iovec_destroy(&sg->iov); + } + + memset(sg, 0x0, sizeof(*sg)); } static uint16_t nvme_map_addr_cmb(NvmeCtrl *n, QEMUIOVector *iov, hwaddr addr, @@ -452,8 +510,7 @@ static uint16_t nvme_map_addr_pmr(NvmeCtrl *n, QEMUIOVector *iov, hwaddr addr, return NVME_SUCCESS; } -static uint16_t nvme_map_addr(NvmeCtrl *n, QEMUSGList *qsg, QEMUIOVector *iov, - hwaddr addr, size_t len) +static uint16_t nvme_map_addr(NvmeCtrl *n, NvmeSg *sg, hwaddr addr, size_t len) { bool cmb = false, pmr = false; @@ -470,40 +527,33 @@ static uint16_t nvme_map_addr(NvmeCtrl *n, QEMUSGList *qsg, QEMUIOVector *iov, } if (cmb || pmr) { - if (qsg && qsg->sg) { + if (sg->flags & NVME_SG_DMA) { return NVME_INVALID_USE_OF_CMB | NVME_DNR; } - assert(iov); - - if (!iov->iov) { - qemu_iovec_init(iov, 1); - } - if (cmb) { - return nvme_map_addr_cmb(n, iov, addr, len); + return nvme_map_addr_cmb(n, &sg->iov, addr, len); } else { - return nvme_map_addr_pmr(n, iov, addr, len); + return nvme_map_addr_pmr(n, &sg->iov, addr, len); } } - if (iov && iov->iov) { + if (!(sg->flags & NVME_SG_DMA)) { return NVME_INVALID_USE_OF_CMB | NVME_DNR; } - assert(qsg); - - if (!qsg->sg) { - pci_dma_sglist_init(qsg, &n->parent_obj, 1); - } - - qemu_sglist_add(qsg, addr, len); + qemu_sglist_add(&sg->qsg, addr, len); return NVME_SUCCESS; } -static uint16_t nvme_map_prp(NvmeCtrl *n, uint64_t prp1, uint64_t prp2, - uint32_t len, NvmeRequest *req) +static inline bool nvme_addr_is_dma(NvmeCtrl *n, hwaddr addr) +{ + return !(nvme_addr_is_cmb(n, addr) || nvme_addr_is_pmr(n, addr)); +} + +static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1, + uint64_t prp2, uint32_t len) { hwaddr trans_len = n->page_size - (prp1 % n->page_size); trans_len = MIN(len, trans_len); @@ -511,20 +561,13 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, uint64_t prp1, uint64_t prp2, uint16_t status; int ret; - QEMUSGList *qsg = &req->qsg; - QEMUIOVector *iov = &req->iov; - trace_pci_nvme_map_prp(trans_len, len, prp1, prp2, num_prps); - if (nvme_addr_is_cmb(n, prp1) || (nvme_addr_is_pmr(n, prp1))) { - qemu_iovec_init(iov, num_prps); - } else { - pci_dma_sglist_init(qsg, &n->parent_obj, num_prps); - } + nvme_sg_init(n, sg, nvme_addr_is_dma(n, prp1)); - status = nvme_map_addr(n, qsg, iov, prp1, trans_len); + status = nvme_map_addr(n, sg, prp1, trans_len); if (status) { - return status; + goto unmap; } len -= trans_len; @@ -539,7 +582,8 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, uint64_t prp1, uint64_t prp2, ret = nvme_addr_read(n, prp2, (void *)prp_list, prp_trans); if (ret) { trace_pci_nvme_err_addr_read(prp2); - return NVME_DATA_TRAS_ERROR; + status = NVME_DATA_TRAS_ERROR; + goto unmap; } while (len != 0) { uint64_t prp_ent = le64_to_cpu(prp_list[i]); @@ -547,7 +591,8 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, uint64_t prp1, uint64_t prp2, if (i == n->max_prp_ents - 1 && len > n->page_size) { if (unlikely(prp_ent & (n->page_size - 1))) { trace_pci_nvme_err_invalid_prplist_ent(prp_ent); - return NVME_INVALID_PRP_OFFSET | NVME_DNR; + status = NVME_INVALID_PRP_OFFSET | NVME_DNR; + goto unmap; } i = 0; @@ -557,20 +602,22 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, uint64_t prp1, uint64_t prp2, prp_trans); if (ret) { trace_pci_nvme_err_addr_read(prp_ent); - return NVME_DATA_TRAS_ERROR; + status = NVME_DATA_TRAS_ERROR; + goto unmap; } prp_ent = le64_to_cpu(prp_list[i]); } if (unlikely(prp_ent & (n->page_size - 1))) { trace_pci_nvme_err_invalid_prplist_ent(prp_ent); - return NVME_INVALID_PRP_OFFSET | NVME_DNR; + status = NVME_INVALID_PRP_OFFSET | NVME_DNR; + goto unmap; } trans_len = MIN(len, n->page_size); - status = nvme_map_addr(n, qsg, iov, prp_ent, trans_len); + status = nvme_map_addr(n, sg, prp_ent, trans_len); if (status) { - return status; + goto unmap; } len -= trans_len; @@ -579,26 +626,30 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, uint64_t prp1, uint64_t prp2, } else { if (unlikely(prp2 & (n->page_size - 1))) { trace_pci_nvme_err_invalid_prp2_align(prp2); - return NVME_INVALID_PRP_OFFSET | NVME_DNR; + status = NVME_INVALID_PRP_OFFSET | NVME_DNR; + goto unmap; } - status = nvme_map_addr(n, qsg, iov, prp2, len); + status = nvme_map_addr(n, sg, prp2, len); if (status) { - return status; + goto unmap; } } } return NVME_SUCCESS; + +unmap: + nvme_sg_unmap(sg); + return status; } /* * Map 'nsgld' data descriptors from 'segment'. The function will subtract the * number of bytes mapped in len. */ -static uint16_t nvme_map_sgl_data(NvmeCtrl *n, QEMUSGList *qsg, - QEMUIOVector *iov, +static uint16_t nvme_map_sgl_data(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor *segment, uint64_t nsgld, - size_t *len, NvmeRequest *req) + size_t *len, NvmeCmd *cmd) { dma_addr_t addr, trans_len; uint32_t dlen; @@ -609,7 +660,7 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, QEMUSGList *qsg, switch (type) { case NVME_SGL_DESCR_TYPE_BIT_BUCKET: - if (req->cmd.opcode == NVME_CMD_WRITE) { + if (cmd->opcode == NVME_CMD_WRITE) { continue; } case NVME_SGL_DESCR_TYPE_DATA_BLOCK: @@ -638,7 +689,7 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, QEMUSGList *qsg, break; } - trace_pci_nvme_err_invalid_sgl_excess_length(nvme_cid(req)); + trace_pci_nvme_err_invalid_sgl_excess_length(dlen); return NVME_DATA_SGL_LEN_INVALID | NVME_DNR; } @@ -654,7 +705,7 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, QEMUSGList *qsg, return NVME_DATA_SGL_LEN_INVALID | NVME_DNR; } - status = nvme_map_addr(n, qsg, iov, addr, trans_len); + status = nvme_map_addr(n, sg, addr, trans_len); if (status) { return status; } @@ -666,9 +717,8 @@ next: return NVME_SUCCESS; } -static uint16_t nvme_map_sgl(NvmeCtrl *n, QEMUSGList *qsg, QEMUIOVector *iov, - NvmeSglDescriptor sgl, size_t len, - NvmeRequest *req) +static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl, + size_t len, NvmeCmd *cmd) { /* * Read the segment in chunks of 256 descriptors (one 4k page) to avoid @@ -689,14 +739,16 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, QEMUSGList *qsg, QEMUIOVector *iov, sgld = &sgl; addr = le64_to_cpu(sgl.addr); - trace_pci_nvme_map_sgl(nvme_cid(req), NVME_SGL_TYPE(sgl.type), len); + trace_pci_nvme_map_sgl(NVME_SGL_TYPE(sgl.type), len); + + nvme_sg_init(n, sg, nvme_addr_is_dma(n, addr)); /* * If the entire transfer can be described with a single data block it can * be mapped directly. */ if (NVME_SGL_TYPE(sgl.type) == NVME_SGL_DESCR_TYPE_DATA_BLOCK) { - status = nvme_map_sgl_data(n, qsg, iov, sgld, 1, &len, req); + status = nvme_map_sgl_data(n, sg, sgld, 1, &len, cmd); if (status) { goto unmap; } @@ -734,8 +786,8 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, QEMUSGList *qsg, QEMUIOVector *iov, goto unmap; } - status = nvme_map_sgl_data(n, qsg, iov, segment, SEG_CHUNK_SIZE, - &len, req); + status = nvme_map_sgl_data(n, sg, segment, SEG_CHUNK_SIZE, + &len, cmd); if (status) { goto unmap; } @@ -761,7 +813,7 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, QEMUSGList *qsg, QEMUIOVector *iov, switch (NVME_SGL_TYPE(last_sgld->type)) { case NVME_SGL_DESCR_TYPE_DATA_BLOCK: case NVME_SGL_DESCR_TYPE_BIT_BUCKET: - status = nvme_map_sgl_data(n, qsg, iov, segment, nsgld, &len, req); + status = nvme_map_sgl_data(n, sg, segment, nsgld, &len, cmd); if (status) { goto unmap; } @@ -788,7 +840,7 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, QEMUSGList *qsg, QEMUIOVector *iov, * Do not map the last descriptor; it will be a Segment or Last Segment * descriptor and is handled by the next iteration. */ - status = nvme_map_sgl_data(n, qsg, iov, segment, nsgld - 1, &len, req); + status = nvme_map_sgl_data(n, sg, segment, nsgld - 1, &len, cmd); if (status) { goto unmap; } @@ -804,83 +856,120 @@ out: return NVME_SUCCESS; unmap: - if (iov->iov) { - qemu_iovec_destroy(iov); - } - - if (qsg->sg) { - qemu_sglist_destroy(qsg); - } - + nvme_sg_unmap(sg); return status; } -static uint16_t nvme_map_dptr(NvmeCtrl *n, size_t len, NvmeRequest *req) +static uint16_t nvme_map_dptr(NvmeCtrl *n, NvmeSg *sg, size_t len, + NvmeCmd *cmd) { uint64_t prp1, prp2; - switch (NVME_CMD_FLAGS_PSDT(req->cmd.flags)) { + switch (NVME_CMD_FLAGS_PSDT(cmd->flags)) { case NVME_PSDT_PRP: - prp1 = le64_to_cpu(req->cmd.dptr.prp1); - prp2 = le64_to_cpu(req->cmd.dptr.prp2); + prp1 = le64_to_cpu(cmd->dptr.prp1); + prp2 = le64_to_cpu(cmd->dptr.prp2); - return nvme_map_prp(n, prp1, prp2, len, req); + return nvme_map_prp(n, sg, prp1, prp2, len); case NVME_PSDT_SGL_MPTR_CONTIGUOUS: case NVME_PSDT_SGL_MPTR_SGL: - /* SGLs shall not be used for Admin commands in NVMe over PCIe */ - if (!req->sq->sqid) { - return NVME_INVALID_FIELD | NVME_DNR; - } - - return nvme_map_sgl(n, &req->qsg, &req->iov, req->cmd.dptr.sgl, len, - req); + return nvme_map_sgl(n, sg, cmd->dptr.sgl, len, cmd); default: return NVME_INVALID_FIELD; } } -static uint16_t nvme_dma(NvmeCtrl *n, uint8_t *ptr, uint32_t len, - DMADirection dir, NvmeRequest *req) -{ - uint16_t status = NVME_SUCCESS; +typedef enum NvmeTxDirection { + NVME_TX_DIRECTION_TO_DEVICE = 0, + NVME_TX_DIRECTION_FROM_DEVICE = 1, +} NvmeTxDirection; - status = nvme_map_dptr(n, len, req); - if (status) { - return status; - } - - /* assert that only one of qsg and iov carries data */ - assert((req->qsg.nsg > 0) != (req->iov.niov > 0)); +static uint16_t nvme_tx(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr, uint32_t len, + NvmeTxDirection dir) +{ + assert(sg->flags & NVME_SG_ALLOC); - if (req->qsg.nsg > 0) { + if (sg->flags & NVME_SG_DMA) { uint64_t residual; - if (dir == DMA_DIRECTION_TO_DEVICE) { - residual = dma_buf_write(ptr, len, &req->qsg); + if (dir == NVME_TX_DIRECTION_TO_DEVICE) { + residual = dma_buf_write(ptr, len, &sg->qsg); } else { - residual = dma_buf_read(ptr, len, &req->qsg); + residual = dma_buf_read(ptr, len, &sg->qsg); } if (unlikely(residual)) { trace_pci_nvme_err_invalid_dma(); - status = NVME_INVALID_FIELD | NVME_DNR; + return NVME_INVALID_FIELD | NVME_DNR; } } else { size_t bytes; - if (dir == DMA_DIRECTION_TO_DEVICE) { - bytes = qemu_iovec_to_buf(&req->iov, 0, ptr, len); + if (dir == NVME_TX_DIRECTION_TO_DEVICE) { + bytes = qemu_iovec_to_buf(&sg->iov, 0, ptr, len); } else { - bytes = qemu_iovec_from_buf(&req->iov, 0, ptr, len); + bytes = qemu_iovec_from_buf(&sg->iov, 0, ptr, len); } if (unlikely(bytes != len)) { trace_pci_nvme_err_invalid_dma(); - status = NVME_INVALID_FIELD | NVME_DNR; + return NVME_INVALID_FIELD | NVME_DNR; } } - return status; + return NVME_SUCCESS; +} + +static inline uint16_t nvme_c2h(NvmeCtrl *n, uint8_t *ptr, uint32_t len, + NvmeRequest *req) +{ + uint16_t status; + + status = nvme_map_dptr(n, &req->sg, len, &req->cmd); + if (status) { + return status; + } + + return nvme_tx(n, &req->sg, ptr, len, NVME_TX_DIRECTION_FROM_DEVICE); +} + +static inline uint16_t nvme_h2c(NvmeCtrl *n, uint8_t *ptr, uint32_t len, + NvmeRequest *req) +{ + uint16_t status; + + status = nvme_map_dptr(n, &req->sg, len, &req->cmd); + if (status) { + return status; + } + + return nvme_tx(n, &req->sg, ptr, len, NVME_TX_DIRECTION_TO_DEVICE); +} + +static inline void nvme_blk_read(BlockBackend *blk, int64_t offset, + BlockCompletionFunc *cb, NvmeRequest *req) +{ + assert(req->sg.flags & NVME_SG_ALLOC); + + if (req->sg.flags & NVME_SG_DMA) { + req->aiocb = dma_blk_read(blk, &req->sg.qsg, offset, BDRV_SECTOR_SIZE, + cb, req); + } else { + req->aiocb = blk_aio_preadv(blk, offset, &req->sg.iov, 0, cb, req); + } +} + +static inline void nvme_blk_write(BlockBackend *blk, int64_t offset, + BlockCompletionFunc *cb, NvmeRequest *req) +{ + assert(req->sg.flags & NVME_SG_ALLOC); + + if (req->sg.flags & NVME_SG_DMA) { + req->aiocb = dma_blk_write(blk, &req->sg.qsg, offset, BDRV_SECTOR_SIZE, + cb, req); + } else { + req->aiocb = blk_aio_pwritev(blk, offset, &req->sg.iov, 0, cb, req); + } } static void nvme_post_cqes(void *opaque) @@ -913,7 +1002,7 @@ static void nvme_post_cqes(void *opaque) } QTAILQ_REMOVE(&cq->req_list, req, entry); nvme_inc_cq_tail(cq); - nvme_req_exit(req); + nvme_sg_unmap(&req->sg); QTAILQ_INSERT_TAIL(&sq->req_list, req, entry); } if (cq->tail != cq->head) { @@ -1048,6 +1137,7 @@ static inline uint16_t nvme_check_mdts(NvmeCtrl *n, size_t len) uint8_t mdts = n->params.mdts; if (mdts && len > n->page_size << mdts) { + trace_pci_nvme_err_mdts(len); return NVME_INVALID_FIELD | NVME_DNR; } @@ -1129,7 +1219,7 @@ static void nvme_aio_err(NvmeRequest *req, int ret) break; } - trace_pci_nvme_err_aio(nvme_cid(req), strerror(ret), status); + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), status); error_setg_errno(&local_err, -ret, "aio failed"); error_report_err(local_err); @@ -1185,9 +1275,8 @@ static uint16_t nvme_check_zone_state_for_write(NvmeZone *zone) return NVME_INTERNAL_DEV_ERROR; } -static uint16_t nvme_check_zone_write(NvmeCtrl *n, NvmeNamespace *ns, - NvmeZone *zone, uint64_t slba, - uint32_t nlb) +static uint16_t nvme_check_zone_write(NvmeNamespace *ns, NvmeZone *zone, + uint64_t slba, uint32_t nlb) { uint64_t zcap = nvme_zone_wr_boundary(zone); uint16_t status; @@ -1212,8 +1301,6 @@ static uint16_t nvme_check_zone_write(NvmeCtrl *n, NvmeNamespace *ns, static uint16_t nvme_check_zone_state_for_read(NvmeZone *zone) { - uint16_t status; - switch (nvme_get_zone_state(zone)) { case NVME_ZONE_STATE_EMPTY: case NVME_ZONE_STATE_IMPLICITLY_OPEN: @@ -1221,16 +1308,15 @@ static uint16_t nvme_check_zone_state_for_read(NvmeZone *zone) case NVME_ZONE_STATE_FULL: case NVME_ZONE_STATE_CLOSED: case NVME_ZONE_STATE_READ_ONLY: - status = NVME_SUCCESS; - break; + return NVME_SUCCESS; case NVME_ZONE_STATE_OFFLINE: - status = NVME_ZONE_OFFLINE; - break; + trace_pci_nvme_err_zone_is_offline(zone->d.zslba); + return NVME_ZONE_OFFLINE; default: assert(false); } - return status; + return NVME_INTERNAL_DEV_ERROR; } static uint16_t nvme_check_zone_read(NvmeNamespace *ns, uint64_t slba, @@ -1265,7 +1351,45 @@ static uint16_t nvme_check_zone_read(NvmeNamespace *ns, uint64_t slba, return status; } -static void nvme_auto_transition_zone(NvmeNamespace *ns) +static uint16_t nvme_zrm_finish(NvmeNamespace *ns, NvmeZone *zone) +{ + switch (nvme_get_zone_state(zone)) { + case NVME_ZONE_STATE_FULL: + return NVME_SUCCESS; + + case NVME_ZONE_STATE_IMPLICITLY_OPEN: + case NVME_ZONE_STATE_EXPLICITLY_OPEN: + nvme_aor_dec_open(ns); + /* fallthrough */ + case NVME_ZONE_STATE_CLOSED: + nvme_aor_dec_active(ns); + /* fallthrough */ + case NVME_ZONE_STATE_EMPTY: + nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_FULL); + return NVME_SUCCESS; + + default: + return NVME_ZONE_INVAL_TRANSITION; + } +} + +static uint16_t nvme_zrm_close(NvmeNamespace *ns, NvmeZone *zone) +{ + switch (nvme_get_zone_state(zone)) { + case NVME_ZONE_STATE_EXPLICITLY_OPEN: + case NVME_ZONE_STATE_IMPLICITLY_OPEN: + nvme_aor_dec_open(ns); + nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_CLOSED); + /* fall through */ + case NVME_ZONE_STATE_CLOSED: + return NVME_SUCCESS; + + default: + return NVME_ZONE_INVAL_TRANSITION; + } +} + +static void nvme_zrm_auto_transition_zone(NvmeNamespace *ns) { NvmeZone *zone; @@ -1277,85 +1401,92 @@ static void nvme_auto_transition_zone(NvmeNamespace *ns) * Automatically close this implicitly open zone. */ QTAILQ_REMOVE(&ns->imp_open_zones, zone, entry); - nvme_aor_dec_open(ns); - nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_CLOSED); + nvme_zrm_close(ns, zone); } } } -static uint16_t nvme_auto_open_zone(NvmeNamespace *ns, NvmeZone *zone) +static uint16_t __nvme_zrm_open(NvmeNamespace *ns, NvmeZone *zone, + bool implicit) { - uint16_t status = NVME_SUCCESS; - uint8_t zs = nvme_get_zone_state(zone); + int act = 0; + uint16_t status; - if (zs == NVME_ZONE_STATE_EMPTY) { - nvme_auto_transition_zone(ns); - status = nvme_aor_check(ns, 1, 1); - } else if (zs == NVME_ZONE_STATE_CLOSED) { - nvme_auto_transition_zone(ns); - status = nvme_aor_check(ns, 0, 1); - } + switch (nvme_get_zone_state(zone)) { + case NVME_ZONE_STATE_EMPTY: + act = 1; - return status; + /* fallthrough */ + + case NVME_ZONE_STATE_CLOSED: + nvme_zrm_auto_transition_zone(ns); + status = nvme_aor_check(ns, act, 1); + if (status) { + return status; + } + + if (act) { + nvme_aor_inc_active(ns); + } + + nvme_aor_inc_open(ns); + + if (implicit) { + nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_IMPLICITLY_OPEN); + return NVME_SUCCESS; + } + + /* fallthrough */ + + case NVME_ZONE_STATE_IMPLICITLY_OPEN: + if (implicit) { + return NVME_SUCCESS; + } + + nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_EXPLICITLY_OPEN); + + /* fallthrough */ + + case NVME_ZONE_STATE_EXPLICITLY_OPEN: + return NVME_SUCCESS; + + default: + return NVME_ZONE_INVAL_TRANSITION; + } } -static void nvme_finalize_zoned_write(NvmeNamespace *ns, NvmeRequest *req, - bool failed) +static inline uint16_t nvme_zrm_auto(NvmeNamespace *ns, NvmeZone *zone) { - NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd; - NvmeZone *zone; - NvmeZonedResult *res = (NvmeZonedResult *)&req->cqe; - uint64_t slba; - uint32_t nlb; + return __nvme_zrm_open(ns, zone, true); +} - slba = le64_to_cpu(rw->slba); - nlb = le16_to_cpu(rw->nlb) + 1; - zone = nvme_get_zone_by_slba(ns, slba); +static inline uint16_t nvme_zrm_open(NvmeNamespace *ns, NvmeZone *zone) +{ + return __nvme_zrm_open(ns, zone, false); +} +static void __nvme_advance_zone_wp(NvmeNamespace *ns, NvmeZone *zone, + uint32_t nlb) +{ zone->d.wp += nlb; - if (failed) { - res->slba = 0; - } - if (zone->d.wp == nvme_zone_wr_boundary(zone)) { - switch (nvme_get_zone_state(zone)) { - case NVME_ZONE_STATE_IMPLICITLY_OPEN: - case NVME_ZONE_STATE_EXPLICITLY_OPEN: - nvme_aor_dec_open(ns); - /* fall through */ - case NVME_ZONE_STATE_CLOSED: - nvme_aor_dec_active(ns); - /* fall through */ - case NVME_ZONE_STATE_EMPTY: - nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_FULL); - /* fall through */ - case NVME_ZONE_STATE_FULL: - break; - default: - assert(false); - } + nvme_zrm_finish(ns, zone); } } -static void nvme_advance_zone_wp(NvmeNamespace *ns, NvmeZone *zone, - uint32_t nlb) +static void nvme_finalize_zoned_write(NvmeNamespace *ns, NvmeRequest *req) { - uint8_t zs; + NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd; + NvmeZone *zone; + uint64_t slba; + uint32_t nlb; - zone->w_ptr += nlb; + slba = le64_to_cpu(rw->slba); + nlb = le16_to_cpu(rw->nlb) + 1; + zone = nvme_get_zone_by_slba(ns, slba); - if (zone->w_ptr < nvme_zone_wr_boundary(zone)) { - zs = nvme_get_zone_state(zone); - switch (zs) { - case NVME_ZONE_STATE_EMPTY: - nvme_aor_inc_active(ns); - /* fall through */ - case NVME_ZONE_STATE_CLOSED: - nvme_aor_inc_open(ns); - nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_IMPLICITLY_OPEN); - } - } + __nvme_advance_zone_wp(ns, zone, nlb); } static inline bool nvme_is_write(NvmeRequest *req) @@ -1379,9 +1510,37 @@ static void nvme_rw_cb(void *opaque, int ret) trace_pci_nvme_rw_cb(nvme_cid(req), blk_name(blk)); if (ns->params.zoned && nvme_is_write(req)) { - nvme_finalize_zoned_write(ns, req, ret != 0); + nvme_finalize_zoned_write(ns, req); + } + + if (!ret) { + block_acct_done(stats, acct); + } else { + block_acct_failed(stats, acct); + nvme_aio_err(req, ret); } + nvme_enqueue_req_completion(nvme_cq(req), req); +} + +struct nvme_aio_flush_ctx { + NvmeRequest *req; + NvmeNamespace *ns; + BlockAcctCookie acct; +}; + +static void nvme_aio_flush_cb(void *opaque, int ret) +{ + struct nvme_aio_flush_ctx *ctx = opaque; + NvmeRequest *req = ctx->req; + uintptr_t *num_flushes = (uintptr_t *)&req->opaque; + + BlockBackend *blk = ctx->ns->blkconf.blk; + BlockAcctCookie *acct = &ctx->acct; + BlockAcctStats *stats = blk_get_stats(blk); + + trace_pci_nvme_aio_flush_cb(nvme_cid(req), blk_name(blk)); + if (!ret) { block_acct_done(stats, acct); } else { @@ -1389,6 +1548,13 @@ static void nvme_rw_cb(void *opaque, int ret) nvme_aio_err(req, ret); } + (*num_flushes)--; + g_free(ctx); + + if (*num_flushes) { + return; + } + nvme_enqueue_req_completion(nvme_cq(req), req); } @@ -1459,10 +1625,139 @@ static void nvme_aio_zone_reset_cb(void *opaque, int ret) nvme_enqueue_req_completion(nvme_cq(req), req); } +struct nvme_copy_ctx { + int copies; + uint8_t *bounce; + uint32_t nlb; +}; + +struct nvme_copy_in_ctx { + NvmeRequest *req; + QEMUIOVector iov; +}; + +static void nvme_copy_cb(void *opaque, int ret) +{ + NvmeRequest *req = opaque; + NvmeNamespace *ns = req->ns; + struct nvme_copy_ctx *ctx = req->opaque; + + trace_pci_nvme_copy_cb(nvme_cid(req)); + + if (ns->params.zoned) { + NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd; + uint64_t sdlba = le64_to_cpu(copy->sdlba); + NvmeZone *zone = nvme_get_zone_by_slba(ns, sdlba); + + __nvme_advance_zone_wp(ns, zone, ctx->nlb); + } + + if (!ret) { + block_acct_done(blk_get_stats(ns->blkconf.blk), &req->acct); + } else { + block_acct_failed(blk_get_stats(ns->blkconf.blk), &req->acct); + nvme_aio_err(req, ret); + } + + g_free(ctx->bounce); + g_free(ctx); + + nvme_enqueue_req_completion(nvme_cq(req), req); +} + +static void nvme_copy_in_complete(NvmeRequest *req) +{ + NvmeNamespace *ns = req->ns; + NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd; + struct nvme_copy_ctx *ctx = req->opaque; + uint64_t sdlba = le64_to_cpu(copy->sdlba); + uint16_t status; + + trace_pci_nvme_copy_in_complete(nvme_cid(req)); + + block_acct_done(blk_get_stats(ns->blkconf.blk), &req->acct); + + status = nvme_check_bounds(ns, sdlba, ctx->nlb); + if (status) { + trace_pci_nvme_err_invalid_lba_range(sdlba, ctx->nlb, ns->id_ns.nsze); + goto invalid; + } + + if (ns->params.zoned) { + NvmeZone *zone = nvme_get_zone_by_slba(ns, sdlba); + + status = nvme_check_zone_write(ns, zone, sdlba, ctx->nlb); + if (status) { + goto invalid; + } + + status = nvme_zrm_auto(ns, zone); + if (status) { + goto invalid; + } + + zone->w_ptr += ctx->nlb; + } + + qemu_iovec_init(&req->sg.iov, 1); + qemu_iovec_add(&req->sg.iov, ctx->bounce, nvme_l2b(ns, ctx->nlb)); + + block_acct_start(blk_get_stats(ns->blkconf.blk), &req->acct, 0, + BLOCK_ACCT_WRITE); + + req->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_l2b(ns, sdlba), + &req->sg.iov, 0, nvme_copy_cb, req); + + return; + +invalid: + req->status = status; + + g_free(ctx->bounce); + g_free(ctx); + + nvme_enqueue_req_completion(nvme_cq(req), req); +} + +static void nvme_aio_copy_in_cb(void *opaque, int ret) +{ + struct nvme_copy_in_ctx *in_ctx = opaque; + NvmeRequest *req = in_ctx->req; + NvmeNamespace *ns = req->ns; + struct nvme_copy_ctx *ctx = req->opaque; + + qemu_iovec_destroy(&in_ctx->iov); + g_free(in_ctx); + + trace_pci_nvme_aio_copy_in_cb(nvme_cid(req)); + + if (ret) { + nvme_aio_err(req, ret); + } + + ctx->copies--; + + if (ctx->copies) { + return; + } + + if (req->status) { + block_acct_failed(blk_get_stats(ns->blkconf.blk), &req->acct); + + g_free(ctx->bounce); + g_free(ctx); + + nvme_enqueue_req_completion(nvme_cq(req), req); + + return; + } + + nvme_copy_in_complete(req); +} + struct nvme_compare_ctx { QEMUIOVector iov; uint8_t *bounce; - size_t len; }; static void nvme_compare_cb(void *opaque, int ret) @@ -1483,16 +1778,15 @@ static void nvme_compare_cb(void *opaque, int ret) goto out; } - buf = g_malloc(ctx->len); + buf = g_malloc(ctx->iov.size); - status = nvme_dma(nvme_ctrl(req), buf, ctx->len, DMA_DIRECTION_TO_DEVICE, - req); + status = nvme_h2c(nvme_ctrl(req), buf, ctx->iov.size, req); if (status) { req->status = status; goto out; } - if (memcmp(buf, ctx->bounce, ctx->len)) { + if (memcmp(buf, ctx->bounce, ctx->iov.size)) { req->status = NVME_CMP_FAILURE; } @@ -1522,8 +1816,7 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req) NvmeDsmRange range[nr]; uintptr_t *discards = (uintptr_t *)&req->opaque; - status = nvme_dma(n, (uint8_t *)range, sizeof(range), - DMA_DIRECTION_TO_DEVICE, req); + status = nvme_h2c(n, (uint8_t *)range, sizeof(range), req); if (status) { return status; } @@ -1548,6 +1841,10 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_dsm_deallocate(nvme_cid(req), nvme_nsid(ns), slba, nlb); + if (nlb > n->dmrsl) { + trace_pci_nvme_dsm_single_range_limit_exceeded(nlb, n->dmrsl); + } + offset = nvme_l2b(ns, slba); len = nvme_l2b(ns, nlb); @@ -1577,6 +1874,121 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req) return status; } +static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeNamespace *ns = req->ns; + NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd; + g_autofree NvmeCopySourceRange *range = NULL; + + uint16_t nr = copy->nr + 1; + uint8_t format = copy->control[0] & 0xf; + uint32_t nlb = 0; + + uint8_t *bounce = NULL, *bouncep = NULL; + struct nvme_copy_ctx *ctx; + uint16_t status; + int i; + + trace_pci_nvme_copy(nvme_cid(req), nvme_nsid(ns), nr, format); + + if (!(n->id_ctrl.ocfs & (1 << format))) { + trace_pci_nvme_err_copy_invalid_format(format); + return NVME_INVALID_FIELD | NVME_DNR; + } + + if (nr > ns->id_ns.msrc + 1) { + return NVME_CMD_SIZE_LIMIT | NVME_DNR; + } + + range = g_new(NvmeCopySourceRange, nr); + + status = nvme_h2c(n, (uint8_t *)range, nr * sizeof(NvmeCopySourceRange), + req); + if (status) { + return status; + } + + for (i = 0; i < nr; i++) { + uint64_t slba = le64_to_cpu(range[i].slba); + uint32_t _nlb = le16_to_cpu(range[i].nlb) + 1; + + if (_nlb > le16_to_cpu(ns->id_ns.mssrl)) { + return NVME_CMD_SIZE_LIMIT | NVME_DNR; + } + + status = nvme_check_bounds(ns, slba, _nlb); + if (status) { + trace_pci_nvme_err_invalid_lba_range(slba, _nlb, ns->id_ns.nsze); + return status; + } + + if (NVME_ERR_REC_DULBE(ns->features.err_rec)) { + status = nvme_check_dulbe(ns, slba, _nlb); + if (status) { + return status; + } + } + + if (ns->params.zoned) { + status = nvme_check_zone_read(ns, slba, _nlb); + if (status) { + return status; + } + } + + nlb += _nlb; + } + + if (nlb > le32_to_cpu(ns->id_ns.mcl)) { + return NVME_CMD_SIZE_LIMIT | NVME_DNR; + } + + bounce = bouncep = g_malloc(nvme_l2b(ns, nlb)); + + block_acct_start(blk_get_stats(ns->blkconf.blk), &req->acct, 0, + BLOCK_ACCT_READ); + + ctx = g_new(struct nvme_copy_ctx, 1); + + ctx->bounce = bounce; + ctx->nlb = nlb; + ctx->copies = 1; + + req->opaque = ctx; + + for (i = 0; i < nr; i++) { + uint64_t slba = le64_to_cpu(range[i].slba); + uint32_t nlb = le16_to_cpu(range[i].nlb) + 1; + + size_t len = nvme_l2b(ns, nlb); + int64_t offset = nvme_l2b(ns, slba); + + trace_pci_nvme_copy_source_range(slba, nlb); + + struct nvme_copy_in_ctx *in_ctx = g_new(struct nvme_copy_in_ctx, 1); + in_ctx->req = req; + + qemu_iovec_init(&in_ctx->iov, 1); + qemu_iovec_add(&in_ctx->iov, bouncep, len); + + ctx->copies++; + + blk_aio_preadv(ns->blkconf.blk, offset, &in_ctx->iov, 0, + nvme_aio_copy_in_cb, in_ctx); + + bouncep += len; + } + + /* account for the 1-initialization */ + ctx->copies--; + + if (!ctx->copies) { + nvme_copy_in_complete(req); + } + + return NVME_NO_COMPLETE; +} + static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req) { NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd; @@ -1594,7 +2006,6 @@ static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req) status = nvme_check_mdts(n, len); if (status) { - trace_pci_nvme_err_mdts(nvme_cid(req), len); return status; } @@ -1615,7 +2026,6 @@ static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req) ctx = g_new(struct nvme_compare_ctx, 1); ctx->bounce = bounce; - ctx->len = len; req->opaque = ctx; @@ -1630,10 +2040,56 @@ static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req) static uint16_t nvme_flush(NvmeCtrl *n, NvmeRequest *req) { - block_acct_start(blk_get_stats(req->ns->blkconf.blk), &req->acct, 0, - BLOCK_ACCT_FLUSH); - req->aiocb = blk_aio_flush(req->ns->blkconf.blk, nvme_rw_cb, req); - return NVME_NO_COMPLETE; + uint32_t nsid = le32_to_cpu(req->cmd.nsid); + uintptr_t *num_flushes = (uintptr_t *)&req->opaque; + uint16_t status; + struct nvme_aio_flush_ctx *ctx; + NvmeNamespace *ns; + + trace_pci_nvme_flush(nvme_cid(req), nsid); + + if (nsid != NVME_NSID_BROADCAST) { + req->ns = nvme_ns(n, nsid); + if (unlikely(!req->ns)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + block_acct_start(blk_get_stats(req->ns->blkconf.blk), &req->acct, 0, + BLOCK_ACCT_FLUSH); + req->aiocb = blk_aio_flush(req->ns->blkconf.blk, nvme_rw_cb, req); + return NVME_NO_COMPLETE; + } + + /* 1-initialize; see comment in nvme_dsm */ + *num_flushes = 1; + + for (int i = 1; i <= n->num_namespaces; i++) { + ns = nvme_ns(n, i); + if (!ns) { + continue; + } + + ctx = g_new(struct nvme_aio_flush_ctx, 1); + ctx->req = req; + ctx->ns = ns; + + (*num_flushes)++; + + block_acct_start(blk_get_stats(ns->blkconf.blk), &ctx->acct, 0, + BLOCK_ACCT_FLUSH); + blk_aio_flush(ns->blkconf.blk, nvme_aio_flush_cb, ctx); + } + + /* account for the 1-initialization */ + (*num_flushes)--; + + if (*num_flushes) { + status = NVME_NO_COMPLETE; + } else { + status = req->status; + } + + return status; } static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req) @@ -1651,7 +2107,6 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req) status = nvme_check_mdts(n, data_size); if (status) { - trace_pci_nvme_err_mdts(nvme_cid(req), data_size); goto invalid; } @@ -1669,7 +2124,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req) } } - status = nvme_map_dptr(n, data_size, req); + status = nvme_map_dptr(n, &req->sg, data_size, &req->cmd); if (status) { goto invalid; } @@ -1685,13 +2140,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req) block_acct_start(blk_get_stats(blk), &req->acct, data_size, BLOCK_ACCT_READ); - if (req->qsg.sg) { - req->aiocb = dma_blk_read(blk, &req->qsg, data_offset, - BDRV_SECTOR_SIZE, nvme_rw_cb, req); - } else { - req->aiocb = blk_aio_preadv(blk, data_offset, &req->iov, 0, - nvme_rw_cb, req); - } + nvme_blk_read(blk, data_offset, nvme_rw_cb, req); return NVME_NO_COMPLETE; invalid: @@ -1719,7 +2168,6 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, if (!wrz) { status = nvme_check_mdts(n, data_size); if (status) { - trace_pci_nvme_err_mdts(nvme_cid(req), data_size); goto invalid; } } @@ -1740,48 +2188,40 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, goto invalid; } - if (nvme_l2b(ns, nlb) > (n->page_size << n->zasl)) { - trace_pci_nvme_err_append_too_large(slba, nlb, n->zasl); - status = NVME_INVALID_FIELD; - goto invalid; + if (n->params.zasl && data_size > n->page_size << n->params.zasl) { + trace_pci_nvme_err_zasl(data_size); + return NVME_INVALID_FIELD | NVME_DNR; } slba = zone->w_ptr; res->slba = cpu_to_le64(slba); } - status = nvme_check_zone_write(n, ns, zone, slba, nlb); + status = nvme_check_zone_write(ns, zone, slba, nlb); if (status) { goto invalid; } - status = nvme_auto_open_zone(ns, zone); + status = nvme_zrm_auto(ns, zone); if (status) { goto invalid; } - nvme_advance_zone_wp(ns, zone, nlb); + zone->w_ptr += nlb; } data_offset = nvme_l2b(ns, slba); if (!wrz) { - status = nvme_map_dptr(n, data_size, req); + status = nvme_map_dptr(n, &req->sg, data_size, &req->cmd); if (status) { goto invalid; } block_acct_start(blk_get_stats(blk), &req->acct, data_size, BLOCK_ACCT_WRITE); - if (req->qsg.sg) { - req->aiocb = dma_blk_write(blk, &req->qsg, data_offset, - BDRV_SECTOR_SIZE, nvme_rw_cb, req); - } else { - req->aiocb = blk_aio_pwritev(blk, data_offset, &req->iov, 0, - nvme_rw_cb, req); - } + nvme_blk_write(blk, data_offset, nvme_rw_cb, req); } else { - block_acct_start(blk_get_stats(blk), &req->acct, 0, BLOCK_ACCT_WRITE); req->aiocb = blk_aio_pwrite_zeroes(blk, data_offset, data_size, BDRV_REQ_MAY_UNMAP, nvme_rw_cb, req); @@ -1846,73 +2286,19 @@ enum NvmeZoneProcessingMask { static uint16_t nvme_open_zone(NvmeNamespace *ns, NvmeZone *zone, NvmeZoneState state, NvmeRequest *req) { - uint16_t status; - - switch (state) { - case NVME_ZONE_STATE_EMPTY: - status = nvme_aor_check(ns, 1, 0); - if (status) { - return status; - } - nvme_aor_inc_active(ns); - /* fall through */ - case NVME_ZONE_STATE_CLOSED: - status = nvme_aor_check(ns, 0, 1); - if (status) { - if (state == NVME_ZONE_STATE_EMPTY) { - nvme_aor_dec_active(ns); - } - return status; - } - nvme_aor_inc_open(ns); - /* fall through */ - case NVME_ZONE_STATE_IMPLICITLY_OPEN: - nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_EXPLICITLY_OPEN); - /* fall through */ - case NVME_ZONE_STATE_EXPLICITLY_OPEN: - return NVME_SUCCESS; - default: - return NVME_ZONE_INVAL_TRANSITION; - } + return nvme_zrm_open(ns, zone); } static uint16_t nvme_close_zone(NvmeNamespace *ns, NvmeZone *zone, NvmeZoneState state, NvmeRequest *req) { - switch (state) { - case NVME_ZONE_STATE_EXPLICITLY_OPEN: - case NVME_ZONE_STATE_IMPLICITLY_OPEN: - nvme_aor_dec_open(ns); - nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_CLOSED); - /* fall through */ - case NVME_ZONE_STATE_CLOSED: - return NVME_SUCCESS; - default: - return NVME_ZONE_INVAL_TRANSITION; - } + return nvme_zrm_close(ns, zone); } static uint16_t nvme_finish_zone(NvmeNamespace *ns, NvmeZone *zone, NvmeZoneState state, NvmeRequest *req) { - switch (state) { - case NVME_ZONE_STATE_EXPLICITLY_OPEN: - case NVME_ZONE_STATE_IMPLICITLY_OPEN: - nvme_aor_dec_open(ns); - /* fall through */ - case NVME_ZONE_STATE_CLOSED: - nvme_aor_dec_active(ns); - /* fall through */ - case NVME_ZONE_STATE_EMPTY: - zone->w_ptr = nvme_zone_wr_boundary(zone); - zone->d.wp = zone->w_ptr; - nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_FULL); - /* fall through */ - case NVME_ZONE_STATE_FULL: - return NVME_SUCCESS; - default: - return NVME_ZONE_INVAL_TRANSITION; - } + return nvme_zrm_finish(ns, zone); } static uint16_t nvme_reset_zone(NvmeNamespace *ns, NvmeZone *zone, @@ -2168,8 +2554,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_FIELD | NVME_DNR; } zd_ext = nvme_get_zd_extension(ns, zone_idx); - status = nvme_dma(n, zd_ext, ns->params.zd_extension_size, - DMA_DIRECTION_TO_DEVICE, req); + status = nvme_h2c(n, zd_ext, ns->params.zd_extension_size, req); if (status) { trace_pci_nvme_err_zd_extension_map_error(zone_idx); return status; @@ -2267,7 +2652,6 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req) status = nvme_check_mdts(n, data_size); if (status) { - trace_pci_nvme_err_mdts(nvme_cid(req), data_size); return status; } @@ -2324,8 +2708,7 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req) } } - status = nvme_dma(n, (uint8_t *)buf, data_size, - DMA_DIRECTION_FROM_DEVICE, req); + status = nvme_c2h(n, (uint8_t *)buf, data_size, req); g_free(buf); @@ -2343,6 +2726,29 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_NSID | NVME_DNR; } + /* + * In the base NVM command set, Flush may apply to all namespaces + * (indicated by NSID being set to 0xFFFFFFFF). But if that feature is used + * along with TP 4056 (Namespace Types), it may be pretty screwed up. + * + * If NSID is indeed set to 0xFFFFFFFF, we simply cannot associate the + * opcode with a specific command since we cannot determine a unique I/O + * command set. Opcode 0x0 could have any other meaning than something + * equivalent to flushing and say it DOES have completely different + * semantics in some other command set - does an NSID of 0xFFFFFFFF then + * mean "for all namespaces, apply whatever command set specific command + * that uses the 0x0 opcode?" Or does it mean "for all namespaces, apply + * whatever command that uses the 0x0 opcode if, and only if, it allows + * NSID to be 0xFFFFFFFF"? + * + * Anyway (and luckily), for now, we do not care about this since the + * device only supports namespace types that includes the NVM Flush command + * (NVM and Zoned), so always do an NVM Flush. + */ + if (req->cmd.opcode == NVME_CMD_FLUSH) { + return nvme_flush(n, req); + } + req->ns = nvme_ns(n, nsid); if (unlikely(!req->ns)) { return NVME_INVALID_FIELD | NVME_DNR; @@ -2354,8 +2760,6 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) } switch (req->cmd.opcode) { - case NVME_CMD_FLUSH: - return nvme_flush(n, req); case NVME_CMD_WRITE_ZEROES: return nvme_write_zeroes(n, req); case NVME_CMD_ZONE_APPEND: @@ -2368,6 +2772,8 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) return nvme_compare(n, req); case NVME_CMD_DSM: return nvme_dsm(n, req); + case NVME_CMD_COPY: + return nvme_copy(n, req); case NVME_CMD_ZONE_MGMT_SEND: return nvme_zone_mgmt_send(n, req); case NVME_CMD_ZONE_MGMT_RECV: @@ -2568,8 +2974,7 @@ static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, nvme_clear_events(n, NVME_AER_TYPE_SMART); } - return nvme_dma(n, (uint8_t *) &smart + off, trans_len, - DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, (uint8_t *) &smart + off, trans_len, req); } static uint16_t nvme_fw_log_info(NvmeCtrl *n, uint32_t buf_len, uint64_t off, @@ -2587,8 +2992,7 @@ static uint16_t nvme_fw_log_info(NvmeCtrl *n, uint32_t buf_len, uint64_t off, strpadcpy((char *)&fw_log.frs1, sizeof(fw_log.frs1), "1.0", ' '); trans_len = MIN(sizeof(fw_log) - off, buf_len); - return nvme_dma(n, (uint8_t *) &fw_log + off, trans_len, - DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, (uint8_t *) &fw_log + off, trans_len, req); } static uint16_t nvme_error_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, @@ -2608,8 +3012,49 @@ static uint16_t nvme_error_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, memset(&errlog, 0x0, sizeof(errlog)); trans_len = MIN(sizeof(errlog) - off, buf_len); - return nvme_dma(n, (uint8_t *)&errlog, trans_len, - DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, (uint8_t *)&errlog, trans_len, req); +} + +static uint16_t nvme_changed_nslist(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, + uint64_t off, NvmeRequest *req) +{ + uint32_t nslist[1024]; + uint32_t trans_len; + int i = 0; + uint32_t nsid; + + memset(nslist, 0x0, sizeof(nslist)); + trans_len = MIN(sizeof(nslist) - off, buf_len); + + while ((nsid = find_first_bit(n->changed_nsids, NVME_CHANGED_NSID_SIZE)) != + NVME_CHANGED_NSID_SIZE) { + /* + * If more than 1024 namespaces, the first entry in the log page should + * be set to 0xffffffff and the others to 0 as spec. + */ + if (i == ARRAY_SIZE(nslist)) { + memset(nslist, 0x0, sizeof(nslist)); + nslist[0] = 0xffffffff; + break; + } + + nslist[i++] = nsid; + clear_bit(nsid, n->changed_nsids); + } + + /* + * Remove all the remaining list entries in case returns directly due to + * more than 1024 namespaces. + */ + if (nslist[0] == 0xffffffff) { + bitmap_zero(n->changed_nsids, NVME_CHANGED_NSID_SIZE); + } + + if (!rae) { + nvme_clear_events(n, NVME_AER_TYPE_NOTICE); + } + + return nvme_c2h(n, ((uint8_t *)nslist) + off, trans_len, req); } static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, @@ -2649,8 +3094,7 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, trans_len = MIN(sizeof(log) - off, buf_len); - return nvme_dma(n, ((uint8_t *)&log) + off, trans_len, - DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, ((uint8_t *)&log) + off, trans_len, req); } static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) @@ -2686,7 +3130,6 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) status = nvme_check_mdts(n, len); if (status) { - trace_pci_nvme_err_mdts(nvme_cid(req), len); return status; } @@ -2697,6 +3140,8 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) return nvme_smart_info(n, rae, len, off, req); case NVME_LOG_FW_SLOT_INFO: return nvme_fw_log_info(n, len, off, req); + case NVME_LOG_CHANGED_NSLIST: + return nvme_changed_nslist(n, rae, len, off, req); case NVME_LOG_CMD_EFFECTS: return nvme_cmd_effects(n, csi, len, off, req); default: @@ -2819,7 +3264,7 @@ static uint16_t nvme_rpt_empty_id_struct(NvmeCtrl *n, NvmeRequest *req) { uint8_t id[NVME_IDENTIFY_DATA_SIZE] = {}; - return nvme_dma(n, id, sizeof(id), DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, id, sizeof(id), req); } static inline bool nvme_csi_has_nvm_support(NvmeNamespace *ns) @@ -2836,31 +3281,33 @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeRequest *req) { trace_pci_nvme_identify_ctrl(); - return nvme_dma(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), - DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), req); } static uint16_t nvme_identify_ctrl_csi(NvmeCtrl *n, NvmeRequest *req) { NvmeIdentify *c = (NvmeIdentify *)&req->cmd; - NvmeIdCtrlZoned id = {}; + uint8_t id[NVME_IDENTIFY_DATA_SIZE] = {}; trace_pci_nvme_identify_ctrl_csi(c->csi); - if (c->csi == NVME_CSI_NVM) { - return nvme_rpt_empty_id_struct(n, req); - } else if (c->csi == NVME_CSI_ZONED) { - if (n->params.zasl_bs) { - id.zasl = n->zasl; - } - return nvme_dma(n, (uint8_t *)&id, sizeof(id), - DMA_DIRECTION_FROM_DEVICE, req); + switch (c->csi) { + case NVME_CSI_NVM: + ((NvmeIdCtrlNvm *)&id)->dmrsl = cpu_to_le32(n->dmrsl); + break; + + case NVME_CSI_ZONED: + ((NvmeIdCtrlZoned *)&id)->zasl = n->params.zasl; + break; + + default: + return NVME_INVALID_FIELD | NVME_DNR; } - return NVME_INVALID_FIELD | NVME_DNR; + return nvme_c2h(n, id, sizeof(id), req); } -static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req) +static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req, bool active) { NvmeNamespace *ns; NvmeIdentify *c = (NvmeIdentify *)&req->cmd; @@ -2874,18 +3321,64 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req) ns = nvme_ns(n, nsid); if (unlikely(!ns)) { - return nvme_rpt_empty_id_struct(n, req); + if (!active) { + ns = nvme_subsys_ns(n->subsys, nsid); + if (!ns) { + return nvme_rpt_empty_id_struct(n, req); + } + } else { + return nvme_rpt_empty_id_struct(n, req); + } } if (c->csi == NVME_CSI_NVM && nvme_csi_has_nvm_support(ns)) { - return nvme_dma(n, (uint8_t *)&ns->id_ns, sizeof(NvmeIdNs), - DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, (uint8_t *)&ns->id_ns, sizeof(NvmeIdNs), req); } return NVME_INVALID_CMD_SET | NVME_DNR; } -static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req) +static uint16_t nvme_identify_ns_attached_list(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeIdentify *c = (NvmeIdentify *)&req->cmd; + uint16_t min_id = le16_to_cpu(c->ctrlid); + uint16_t list[NVME_CONTROLLER_LIST_SIZE] = {}; + uint16_t *ids = &list[1]; + NvmeNamespace *ns; + NvmeCtrl *ctrl; + int cntlid, nr_ids = 0; + + trace_pci_nvme_identify_ns_attached_list(min_id); + + if (c->nsid == NVME_NSID_BROADCAST) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + ns = nvme_subsys_ns(n->subsys, c->nsid); + if (!ns) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + for (cntlid = min_id; cntlid < ARRAY_SIZE(n->subsys->ctrls); cntlid++) { + ctrl = nvme_subsys_ctrl(n->subsys, cntlid); + if (!ctrl) { + continue; + } + + if (!nvme_ns_is_attached(ctrl, ns)) { + continue; + } + + ids[nr_ids++] = cntlid; + } + + list[0] = nr_ids; + + return nvme_c2h(n, (uint8_t *)list, sizeof(list), req); +} + +static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req, + bool active) { NvmeNamespace *ns; NvmeIdentify *c = (NvmeIdentify *)&req->cmd; @@ -2899,20 +3392,28 @@ static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req) ns = nvme_ns(n, nsid); if (unlikely(!ns)) { - return nvme_rpt_empty_id_struct(n, req); + if (!active) { + ns = nvme_subsys_ns(n->subsys, nsid); + if (!ns) { + return nvme_rpt_empty_id_struct(n, req); + } + } else { + return nvme_rpt_empty_id_struct(n, req); + } } if (c->csi == NVME_CSI_NVM && nvme_csi_has_nvm_support(ns)) { return nvme_rpt_empty_id_struct(n, req); } else if (c->csi == NVME_CSI_ZONED && ns->csi == NVME_CSI_ZONED) { - return nvme_dma(n, (uint8_t *)ns->id_ns_zoned, sizeof(NvmeIdNsZoned), - DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, (uint8_t *)ns->id_ns_zoned, sizeof(NvmeIdNsZoned), + req); } return NVME_INVALID_FIELD | NVME_DNR; } -static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeRequest *req) +static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeRequest *req, + bool active) { NvmeNamespace *ns; NvmeIdentify *c = (NvmeIdentify *)&req->cmd; @@ -2937,7 +3438,14 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeRequest *req) for (i = 1; i <= n->num_namespaces; i++) { ns = nvme_ns(n, i); if (!ns) { - continue; + if (!active) { + ns = nvme_subsys_ns(n->subsys, i); + if (!ns) { + continue; + } + } else { + continue; + } } if (ns->params.nsid <= min_nsid) { continue; @@ -2948,10 +3456,11 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeRequest *req) } } - return nvme_dma(n, list, data_len, DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, list, data_len, req); } -static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req) +static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req, + bool active) { NvmeNamespace *ns; NvmeIdentify *c = (NvmeIdentify *)&req->cmd; @@ -2977,7 +3486,14 @@ static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req) for (i = 1; i <= n->num_namespaces; i++) { ns = nvme_ns(n, i); if (!ns) { - continue; + if (!active) { + ns = nvme_subsys_ns(n->subsys, i); + if (!ns) { + continue; + } + } else { + continue; + } } if (ns->params.nsid <= min_nsid || c->csi != ns->csi) { continue; @@ -2988,7 +3504,7 @@ static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req) } } - return nvme_dma(n, list, data_len, DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, list, data_len, req); } static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req) @@ -3035,7 +3551,7 @@ static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req) ns_descrs->csi.hdr.nidl = NVME_NIDL_CSI; ns_descrs->csi.v = ns->csi; - return nvme_dma(n, list, sizeof(list), DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, list, sizeof(list), req); } static uint16_t nvme_identify_cmd_set(NvmeCtrl *n, NvmeRequest *req) @@ -3048,34 +3564,39 @@ static uint16_t nvme_identify_cmd_set(NvmeCtrl *n, NvmeRequest *req) NVME_SET_CSI(*list, NVME_CSI_NVM); NVME_SET_CSI(*list, NVME_CSI_ZONED); - return nvme_dma(n, list, data_len, DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, list, data_len, req); } static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) { NvmeIdentify *c = (NvmeIdentify *)&req->cmd; - switch (le32_to_cpu(c->cns)) { + trace_pci_nvme_identify(nvme_cid(req), c->cns, le16_to_cpu(c->ctrlid), + c->csi); + + switch (c->cns) { case NVME_ID_CNS_NS: - /* fall through */ + return nvme_identify_ns(n, req, true); case NVME_ID_CNS_NS_PRESENT: - return nvme_identify_ns(n, req); + return nvme_identify_ns(n, req, false); + case NVME_ID_CNS_NS_ATTACHED_CTRL_LIST: + return nvme_identify_ns_attached_list(n, req); case NVME_ID_CNS_CS_NS: - /* fall through */ + return nvme_identify_ns_csi(n, req, true); case NVME_ID_CNS_CS_NS_PRESENT: - return nvme_identify_ns_csi(n, req); + return nvme_identify_ns_csi(n, req, false); case NVME_ID_CNS_CTRL: return nvme_identify_ctrl(n, req); case NVME_ID_CNS_CS_CTRL: return nvme_identify_ctrl_csi(n, req); case NVME_ID_CNS_NS_ACTIVE_LIST: - /* fall through */ + return nvme_identify_nslist(n, req, true); case NVME_ID_CNS_NS_PRESENT_LIST: - return nvme_identify_nslist(n, req); + return nvme_identify_nslist(n, req, false); case NVME_ID_CNS_CS_NS_ACTIVE_LIST: - /* fall through */ + return nvme_identify_nslist_csi(n, req, true); case NVME_ID_CNS_CS_NS_PRESENT_LIST: - return nvme_identify_nslist_csi(n, req); + return nvme_identify_nslist_csi(n, req, false); case NVME_ID_CNS_NS_DESCR_LIST: return nvme_identify_ns_descr_list(n, req); case NVME_ID_CNS_IO_COMMAND_SET: @@ -3137,8 +3658,7 @@ static uint16_t nvme_get_feature_timestamp(NvmeCtrl *n, NvmeRequest *req) { uint64_t timestamp = nvme_get_timestamp(n); - return nvme_dma(n, (uint8_t *)×tamp, sizeof(timestamp), - DMA_DIRECTION_FROM_DEVICE, req); + return nvme_c2h(n, (uint8_t *)×tamp, sizeof(timestamp), req); } static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req) @@ -3299,8 +3819,7 @@ static uint16_t nvme_set_feature_timestamp(NvmeCtrl *n, NvmeRequest *req) uint16_t ret; uint64_t timestamp; - ret = nvme_dma(n, (uint8_t *)×tamp, sizeof(timestamp), - DMA_DIRECTION_TO_DEVICE, req); + ret = nvme_h2c(n, (uint8_t *)×tamp, sizeof(timestamp), req); if (ret) { return ret; } @@ -3472,6 +3991,71 @@ static uint16_t nvme_aer(NvmeCtrl *n, NvmeRequest *req) return NVME_NO_COMPLETE; } +static void __nvme_select_ns_iocs(NvmeCtrl *n, NvmeNamespace *ns); +static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeNamespace *ns; + NvmeCtrl *ctrl; + uint16_t list[NVME_CONTROLLER_LIST_SIZE] = {}; + uint32_t nsid = le32_to_cpu(req->cmd.nsid); + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10); + bool attach = !(dw10 & 0xf); + uint16_t *nr_ids = &list[0]; + uint16_t *ids = &list[1]; + uint16_t ret; + int i; + + trace_pci_nvme_ns_attachment(nvme_cid(req), dw10 & 0xf); + + ns = nvme_subsys_ns(n->subsys, nsid); + if (!ns) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + ret = nvme_h2c(n, (uint8_t *)list, 4096, req); + if (ret) { + return ret; + } + + if (!*nr_ids) { + return NVME_NS_CTRL_LIST_INVALID | NVME_DNR; + } + + for (i = 0; i < *nr_ids; i++) { + ctrl = nvme_subsys_ctrl(n->subsys, ids[i]); + if (!ctrl) { + return NVME_NS_CTRL_LIST_INVALID | NVME_DNR; + } + + if (attach) { + if (nvme_ns_is_attached(ctrl, ns)) { + return NVME_NS_ALREADY_ATTACHED | NVME_DNR; + } + + nvme_ns_attach(ctrl, ns); + __nvme_select_ns_iocs(ctrl, ns); + } else { + if (!nvme_ns_is_attached(ctrl, ns)) { + return NVME_NS_NOT_ATTACHED | NVME_DNR; + } + + nvme_ns_detach(ctrl, ns); + } + + /* + * Add namespace id to the changed namespace id list for event clearing + * via Get Log Page command. + */ + if (!test_and_set_bit(nsid, ctrl->changed_nsids)) { + nvme_enqueue_event(ctrl, NVME_AER_TYPE_NOTICE, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED, + NVME_LOG_CHANGED_NSLIST); + } + } + + return NVME_SUCCESS; +} + static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) { trace_pci_nvme_admin_cmd(nvme_cid(req), nvme_sqid(req), req->cmd.opcode, @@ -3482,6 +4066,11 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_OPCODE | NVME_DNR; } + /* SGLs shall not be used for Admin commands in NVMe over PCIe */ + if (NVME_CMD_FLAGS_PSDT(req->cmd.flags) != NVME_PSDT_PRP) { + return NVME_INVALID_FIELD | NVME_DNR; + } + switch (req->cmd.opcode) { case NVME_ADM_CMD_DELETE_SQ: return nvme_del_sq(n, req); @@ -3503,6 +4092,8 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) return nvme_get_feature(n, req); case NVME_ADM_CMD_ASYNC_EV_REQ: return nvme_aer(n, req); + case NVME_ADM_CMD_NS_ATTACHMENT: + return nvme_ns_attachment(n, req); default: assert(false); } @@ -3604,6 +4195,25 @@ static void nvme_ctrl_shutdown(NvmeCtrl *n) } } +static void __nvme_select_ns_iocs(NvmeCtrl *n, NvmeNamespace *ns) +{ + ns->iocs = nvme_cse_iocs_none; + switch (ns->csi) { + case NVME_CSI_NVM: + if (NVME_CC_CSS(n->bar.cc) != NVME_CC_CSS_ADMIN_ONLY) { + ns->iocs = nvme_cse_iocs_nvm; + } + break; + case NVME_CSI_ZONED: + if (NVME_CC_CSS(n->bar.cc) == NVME_CC_CSS_CSI) { + ns->iocs = nvme_cse_iocs_zoned; + } else if (NVME_CC_CSS(n->bar.cc) == NVME_CC_CSS_NVM) { + ns->iocs = nvme_cse_iocs_nvm; + } + break; + } +} + static void nvme_select_ns_iocs(NvmeCtrl *n) { NvmeNamespace *ns; @@ -3614,21 +4224,8 @@ static void nvme_select_ns_iocs(NvmeCtrl *n) if (!ns) { continue; } - ns->iocs = nvme_cse_iocs_none; - switch (ns->csi) { - case NVME_CSI_NVM: - if (NVME_CC_CSS(n->bar.cc) != NVME_CC_CSS_ADMIN_ONLY) { - ns->iocs = nvme_cse_iocs_nvm; - } - break; - case NVME_CSI_ZONED: - if (NVME_CC_CSS(n->bar.cc) == NVME_CC_CSS_CSI) { - ns->iocs = nvme_cse_iocs_zoned; - } else if (NVME_CC_CSS(n->bar.cc) == NVME_CC_CSS_NVM) { - ns->iocs = nvme_cse_iocs_nvm; - } - break; - } + + __nvme_select_ns_iocs(n, ns); } } @@ -3726,17 +4323,6 @@ static int nvme_start_ctrl(NvmeCtrl *n) nvme_init_sq(&n->admin_sq, n, n->bar.asq, 0, 0, NVME_AQA_ASQS(n->bar.aqa) + 1); - if (!n->params.zasl_bs) { - n->zasl = n->params.mdts; - } else { - if (n->params.zasl_bs < n->page_size) { - trace_pci_nvme_err_startfail_zasl_too_small(n->params.zasl_bs, - n->page_size); - return -1; - } - n->zasl = 31 - clz32(n->params.zasl_bs / n->page_size); - } - nvme_set_timestamp(n, 0ULL); QTAILQ_INIT(&n->aer_queue); @@ -4245,11 +4831,10 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp) host_memory_backend_set_mapped(n->pmr.dev, true); } - if (n->params.zasl_bs) { - if (!is_power_of_2(n->params.zasl_bs)) { - error_setg(errp, "zone append size limit has to be a power of 2"); - return; - } + if (n->params.zasl > n->params.mdts) { + error_setg(errp, "zoned.zasl (Zone Append Size Limit) must be less " + "than or equal to mdts (Maximum Data Transfer Size)"); + return; } } @@ -4267,6 +4852,20 @@ static void nvme_init_state(NvmeCtrl *n) n->aer_reqs = g_new0(NvmeRequest *, n->params.aerl + 1); } +static int nvme_attach_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) +{ + if (nvme_ns_is_attached(n, ns)) { + error_setg(errp, + "namespace %d is already attached to controller %d", + nvme_nsid(ns), n->cntlid); + return -1; + } + + nvme_ns_attach(n, ns); + + return 0; +} + int nvme_register_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) { uint32_t nsid = nvme_nsid(ns); @@ -4298,7 +4897,26 @@ int nvme_register_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) trace_pci_nvme_register_namespace(nsid); - n->namespaces[nsid - 1] = ns; + /* + * If subsys is not given, namespae is always attached to the controller + * because there's no subsystem to manage namespace allocation. + */ + if (!n->subsys) { + if (ns->params.detached) { + error_setg(errp, + "detached needs nvme-subsys specified nvme or nvme-ns"); + return -1; + } + + return nvme_attach_namespace(n, ns, errp); + } else { + if (!ns->params.detached) { + return nvme_attach_namespace(n, ns, errp); + } + } + + n->dmrsl = MIN_NON_ZERO(n->dmrsl, + BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); return 0; } @@ -4405,24 +5023,49 @@ static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) return 0; } +static void nvme_init_subnqn(NvmeCtrl *n) +{ + NvmeSubsystem *subsys = n->subsys; + NvmeIdCtrl *id = &n->id_ctrl; + + if (!subsys) { + snprintf((char *)id->subnqn, sizeof(id->subnqn), + "nqn.2019-08.org.qemu:%s", n->params.serial); + } else { + pstrcpy((char *)id->subnqn, sizeof(id->subnqn), (char*)subsys->subnqn); + } +} + static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) { NvmeIdCtrl *id = &n->id_ctrl; uint8_t *pci_conf = pci_dev->config; - char *subnqn; id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); strpadcpy((char *)id->mn, sizeof(id->mn), "QEMU NVMe Ctrl", ' '); strpadcpy((char *)id->fr, sizeof(id->fr), "1.0", ' '); strpadcpy((char *)id->sn, sizeof(id->sn), n->params.serial, ' '); + + id->cntlid = cpu_to_le16(n->cntlid); + + id->oaes = cpu_to_le32(NVME_OAES_NS_ATTR); + id->rab = 6; - id->ieee[0] = 0x00; - id->ieee[1] = 0x02; - id->ieee[2] = 0xb3; + + if (n->params.use_intel_id) { + id->ieee[0] = 0xb3; + id->ieee[1] = 0x02; + id->ieee[2] = 0x00; + } else { + id->ieee[0] = 0x00; + id->ieee[1] = 0x54; + id->ieee[2] = 0x52; + } + id->mdts = n->params.mdts; id->ver = cpu_to_le32(NVME_SPEC_VER); - id->oacs = cpu_to_le16(0); + id->oacs = cpu_to_le16(NVME_OACS_NS_MGMT); id->cntrltype = 0x1; /* @@ -4450,20 +5093,31 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->nn = cpu_to_le32(n->num_namespaces); id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROES | NVME_ONCS_TIMESTAMP | NVME_ONCS_FEATURES | NVME_ONCS_DSM | - NVME_ONCS_COMPARE); + NVME_ONCS_COMPARE | NVME_ONCS_COPY); - id->vwc = (0x2 << 1) | 0x1; + /* + * NOTE: If this device ever supports a command set that does NOT use 0x0 + * as a Flush-equivalent operation, support for the broadcast NSID in Flush + * should probably be removed. + * + * See comment in nvme_io_cmd. + */ + id->vwc = NVME_VWC_NSID_BROADCAST_SUPPORT | NVME_VWC_PRESENT; + + id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0); id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN | NVME_CTRL_SGLS_BITBUCKET); - subnqn = g_strdup_printf("nqn.2019-08.org.qemu:%s", n->params.serial); - strpadcpy((char *)id->subnqn, sizeof(id->subnqn), subnqn, '\0'); - g_free(subnqn); + nvme_init_subnqn(n); id->psd[0].mp = cpu_to_le16(0x9c4); id->psd[0].enlat = cpu_to_le32(0x10); id->psd[0].exlat = cpu_to_le32(0x4); + if (n->subsys) { + id->cmic |= NVME_CMIC_MULTI_CTRL; + } + NVME_CAP_SET_MQES(n->bar.cap, 0x7ff); NVME_CAP_SET_CQR(n->bar.cap, 1); NVME_CAP_SET_TO(n->bar.cap, 0xf); @@ -4478,6 +5132,24 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) n->bar.intmc = n->bar.intms = 0; } +static int nvme_init_subsys(NvmeCtrl *n, Error **errp) +{ + int cntlid; + + if (!n->subsys) { + return 0; + } + + cntlid = nvme_subsys_register_ctrl(n, errp); + if (cntlid < 0) { + return -1; + } + + n->cntlid = cntlid; + + return 0; +} + static void nvme_realize(PCIDevice *pci_dev, Error **errp) { NvmeCtrl *n = NVME(pci_dev); @@ -4498,6 +5170,10 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) return; } + if (nvme_init_subsys(n, errp)) { + error_propagate(errp, local_err); + return; + } nvme_init_ctrl(n, pci_dev); /* setup a namespace if the controller drive property was given */ @@ -4550,6 +5226,8 @@ static Property nvme_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeCtrl, namespace.blkconf), DEFINE_PROP_LINK("pmrdev", NvmeCtrl, pmr.dev, TYPE_MEMORY_BACKEND, HostMemoryBackend *), + DEFINE_PROP_LINK("subsys", NvmeCtrl, subsys, TYPE_NVME_SUBSYS, + NvmeSubsystem *), DEFINE_PROP_STRING("serial", NvmeCtrl, params.serial), DEFINE_PROP_UINT32("cmb_size_mb", NvmeCtrl, params.cmb_size_mb, 0), DEFINE_PROP_UINT32("num_queues", NvmeCtrl, params.num_queues, 0), @@ -4560,8 +5238,7 @@ static Property nvme_props[] = { DEFINE_PROP_UINT8("mdts", NvmeCtrl, params.mdts, 7), DEFINE_PROP_BOOL("use-intel-id", NvmeCtrl, params.use_intel_id, false), DEFINE_PROP_BOOL("legacy-cmb", NvmeCtrl, params.legacy_cmb, false), - DEFINE_PROP_SIZE32("zoned.append_size_limit", NvmeCtrl, params.zasl_bs, - NVME_DEFAULT_MAX_ZA_SIZE), + DEFINE_PROP_UINT8("zoned.zasl", NvmeCtrl, params.zasl, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/nvme.h b/hw/block/nvme.h index dee6092bd4..4955d649c7 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -2,6 +2,7 @@ #define HW_NVME_H #include "block/nvme.h" +#include "nvme-subsys.h" #include "nvme-ns.h" #define NVME_MAX_NAMESPACES 256 @@ -9,6 +10,12 @@ #define NVME_DEFAULT_ZONE_SIZE (128 * MiB) #define NVME_DEFAULT_MAX_ZA_SIZE (128 * KiB) +/* + * Subsystem namespace list for allocated namespaces should be larger than + * attached namespace list in a controller. + */ +QEMU_BUILD_BUG_ON(NVME_MAX_NAMESPACES > NVME_SUBSYS_MAX_NAMESPACES); + typedef struct NvmeParams { char *serial; uint32_t num_queues; /* deprecated since 5.1 */ @@ -19,7 +26,7 @@ typedef struct NvmeParams { uint32_t aer_max_queued; uint8_t mdts; bool use_intel_id; - uint32_t zasl_bs; + uint8_t zasl; bool legacy_cmb; } NvmeParams; @@ -28,6 +35,20 @@ typedef struct NvmeAsyncEvent { NvmeAerResult result; } NvmeAsyncEvent; +enum { + NVME_SG_ALLOC = 1 << 0, + NVME_SG_DMA = 1 << 1, +}; + +typedef struct NvmeSg { + int flags; + + union { + QEMUSGList qsg; + QEMUIOVector iov; + }; +} NvmeSg; + typedef struct NvmeRequest { struct NvmeSQueue *sq; struct NvmeNamespace *ns; @@ -37,8 +58,7 @@ typedef struct NvmeRequest { NvmeCqe cqe; NvmeCmd cmd; BlockAcctCookie acct; - QEMUSGList qsg; - QEMUIOVector iov; + NvmeSg sg; QTAILQ_ENTRY(NvmeRequest)entry; } NvmeRequest; @@ -68,6 +88,7 @@ static inline const char *nvme_io_opc_str(uint8_t opc) case NVME_CMD_COMPARE: return "NVME_NVM_CMD_COMPARE"; case NVME_CMD_WRITE_ZEROES: return "NVME_NVM_CMD_WRITE_ZEROES"; case NVME_CMD_DSM: return "NVME_NVM_CMD_DSM"; + case NVME_CMD_COPY: return "NVME_NVM_CMD_COPY"; case NVME_CMD_ZONE_MGMT_SEND: return "NVME_ZONED_CMD_MGMT_SEND"; case NVME_CMD_ZONE_MGMT_RECV: return "NVME_ZONED_CMD_MGMT_RECV"; case NVME_CMD_ZONE_APPEND: return "NVME_ZONED_CMD_ZONE_APPEND"; @@ -133,6 +154,7 @@ typedef struct NvmeCtrl { NvmeBus bus; BlockConf conf; + uint16_t cntlid; bool qs_created; uint32_t page_size; uint16_t page_bits; @@ -168,9 +190,19 @@ typedef struct NvmeCtrl { QTAILQ_HEAD(, NvmeAsyncEvent) aer_queue; int aer_queued; - uint8_t zasl; + uint32_t dmrsl; + + /* Namespace ID is started with 1 so bitmap should be 1-based */ +#define NVME_CHANGED_NSID_SIZE (NVME_MAX_NAMESPACES + 1) + DECLARE_BITMAP(changed_nsids, NVME_CHANGED_NSID_SIZE); + + NvmeSubsystem *subsys; NvmeNamespace namespace; + /* + * Attached namespaces to this controller. If subsys is not given, all + * namespaces in this list will always be attached. + */ NvmeNamespace *namespaces[NVME_MAX_NAMESPACES]; NvmeSQueue **sq; NvmeCQueue **cq; @@ -189,6 +221,29 @@ static inline NvmeNamespace *nvme_ns(NvmeCtrl *n, uint32_t nsid) return n->namespaces[nsid - 1]; } +static inline bool nvme_ns_is_attached(NvmeCtrl *n, NvmeNamespace *ns) +{ + int nsid; + + for (nsid = 1; nsid <= n->num_namespaces; nsid++) { + if (nvme_ns(n, nsid) == ns) { + return true; + } + } + + return false; +} + +static inline void nvme_ns_attach(NvmeCtrl *n, NvmeNamespace *ns) +{ + n->namespaces[nvme_nsid(ns) - 1] = ns; +} + +static inline void nvme_ns_detach(NvmeCtrl *n, NvmeNamespace *ns) +{ + n->namespaces[nvme_nsid(ns) - 1] = NULL; +} + static inline NvmeCQueue *nvme_cq(NvmeRequest *req) { NvmeSQueue *sq = req->sq; diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 22287a1522..526b70417d 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -1014,7 +1014,7 @@ void pflash_cfi01_legacy_drive(PFlashCFI01 *fl, DriveInfo *dinfo) loc_pop(&loc); } -static void postload_update_cb(void *opaque, int running, RunState state) +static void postload_update_cb(void *opaque, bool running, RunState state) { PFlashCFI01 *pfl = opaque; diff --git a/hw/block/trace-events b/hw/block/trace-events index d32475c398..ef06d2ea74 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -37,26 +37,36 @@ pci_nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2 pci_nvme_map_addr(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRIu64"" pci_nvme_map_addr_cmb(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRIu64"" pci_nvme_map_prp(uint64_t trans_len, uint32_t len, uint64_t prp1, uint64_t prp2, int num_prps) "trans_len %"PRIu64" len %"PRIu32" prp1 0x%"PRIx64" prp2 0x%"PRIx64" num_prps %d" -pci_nvme_map_sgl(uint16_t cid, uint8_t typ, uint64_t len) "cid %"PRIu16" type 0x%"PRIx8" len %"PRIu64"" +pci_nvme_map_sgl(uint8_t typ, uint64_t len) "type 0x%"PRIx8" len %"PRIu64"" pci_nvme_io_cmd(uint16_t cid, uint32_t nsid, uint16_t sqid, uint8_t opcode, const char *opname) "cid %"PRIu16" nsid %"PRIu32" sqid %"PRIu16" opc 0x%"PRIx8" opname '%s'" pci_nvme_admin_cmd(uint16_t cid, uint16_t sqid, uint8_t opcode, const char *opname) "cid %"PRIu16" sqid %"PRIu16" opc 0x%"PRIx8" opname '%s'" +pci_nvme_flush(uint16_t cid, uint32_t nsid) "cid %"PRIu16" nsid %"PRIu32"" pci_nvme_read(uint16_t cid, uint32_t nsid, uint32_t nlb, uint64_t count, uint64_t lba) "cid %"PRIu16" nsid %"PRIu32" nlb %"PRIu32" count %"PRIu64" lba 0x%"PRIx64"" pci_nvme_write(uint16_t cid, const char *verb, uint32_t nsid, uint32_t nlb, uint64_t count, uint64_t lba) "cid %"PRIu16" opname '%s' nsid %"PRIu32" nlb %"PRIu32" count %"PRIu64" lba 0x%"PRIx64"" pci_nvme_rw_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'" +pci_nvme_copy(uint16_t cid, uint32_t nsid, uint16_t nr, uint8_t format) "cid %"PRIu16" nsid %"PRIu32" nr %"PRIu16" format 0x%"PRIx8"" +pci_nvme_copy_source_range(uint64_t slba, uint32_t nlb) "slba 0x%"PRIx64" nlb %"PRIu32"" +pci_nvme_copy_in_complete(uint16_t cid) "cid %"PRIu16"" +pci_nvme_copy_cb(uint16_t cid) "cid %"PRIu16"" pci_nvme_block_status(int64_t offset, int64_t bytes, int64_t pnum, int ret, bool zeroed) "offset %"PRId64" bytes %"PRId64" pnum %"PRId64" ret 0x%x zeroed %d" pci_nvme_dsm(uint16_t cid, uint32_t nsid, uint32_t nr, uint32_t attr) "cid %"PRIu16" nsid %"PRIu32" nr %"PRIu32" attr 0x%"PRIx32"" pci_nvme_dsm_deallocate(uint16_t cid, uint32_t nsid, uint64_t slba, uint32_t nlb) "cid %"PRIu16" nsid %"PRIu32" slba %"PRIu64" nlb %"PRIu32"" +pci_nvme_dsm_single_range_limit_exceeded(uint32_t nlb, uint32_t dmrsl) "nlb %"PRIu32" dmrsl %"PRIu32"" pci_nvme_compare(uint16_t cid, uint32_t nsid, uint64_t slba, uint32_t nlb) "cid %"PRIu16" nsid %"PRIu32" slba 0x%"PRIx64" nlb %"PRIu32"" pci_nvme_compare_cb(uint16_t cid) "cid %"PRIu16"" pci_nvme_aio_discard_cb(uint16_t cid) "cid %"PRIu16"" +pci_nvme_aio_copy_in_cb(uint16_t cid) "cid %"PRIu16"" pci_nvme_aio_zone_reset_cb(uint16_t cid, uint64_t zslba) "cid %"PRIu16" zslba 0x%"PRIx64"" +pci_nvme_aio_flush_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'" pci_nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16"" pci_nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d" pci_nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16"" pci_nvme_del_cq(uint16_t cqid) "deleted completion queue, cqid=%"PRIu16"" +pci_nvme_identify(uint16_t cid, uint8_t cns, uint16_t ctrlid, uint8_t csi) "cid %"PRIu16" cns 0x%"PRIx8" ctrlid %"PRIu16" csi 0x%"PRIx8"" pci_nvme_identify_ctrl(void) "identify controller" pci_nvme_identify_ctrl_csi(uint8_t csi) "identify controller, csi=0x%"PRIx8"" pci_nvme_identify_ns(uint32_t ns) "nsid %"PRIu32"" +pci_nvme_identify_ns_attached_list(uint16_t cntid) "cntid=%"PRIu16"" pci_nvme_identify_ns_csi(uint32_t ns, uint8_t csi) "nsid=%"PRIu32", csi=0x%"PRIx8"" pci_nvme_identify_nslist(uint32_t ns) "nsid %"PRIu32"" pci_nvme_identify_nslist_csi(uint16_t ns, uint8_t csi) "nsid=%"PRIu16", csi=0x%"PRIx8"" @@ -75,6 +85,8 @@ pci_nvme_aer(uint16_t cid) "cid %"PRIu16"" pci_nvme_aer_aerl_exceeded(void) "aerl exceeded" pci_nvme_aer_masked(uint8_t type, uint8_t mask) "type 0x%"PRIx8" mask 0x%"PRIx8"" pci_nvme_aer_post_cqe(uint8_t typ, uint8_t info, uint8_t log_page) "type 0x%"PRIx8" info 0x%"PRIx8" lid 0x%"PRIx8"" +pci_nvme_ns_attachment(uint16_t cid, uint8_t sel) "cid %"PRIu16", sel=0x%"PRIx8"" +pci_nvme_ns_attachment_attach(uint16_t cntlid, uint32_t nsid) "cntlid=0x%"PRIx16", nsid=0x%"PRIx32"" pci_nvme_enqueue_event(uint8_t typ, uint8_t info, uint8_t log_page) "type 0x%"PRIx8" info 0x%"PRIx8" lid 0x%"PRIx8"" pci_nvme_enqueue_event_noqueue(int queued) "queued %d" pci_nvme_enqueue_event_masked(uint8_t typ) "type 0x%"PRIx8"" @@ -107,15 +119,17 @@ pci_nvme_clear_ns_close(uint32_t state, uint64_t slba) "zone state=%"PRIu32", sl pci_nvme_clear_ns_reset(uint32_t state, uint64_t slba) "zone state=%"PRIu32", slba=%"PRIu64" transitioned to Empty state" # nvme traces for error conditions -pci_nvme_err_mdts(uint16_t cid, size_t len) "cid %"PRIu16" len %zu" +pci_nvme_err_mdts(size_t len) "len %zu" +pci_nvme_err_zasl(size_t len) "len %zu" pci_nvme_err_req_status(uint16_t cid, uint32_t nsid, uint16_t status, uint8_t opc) "cid %"PRIu16" nsid %"PRIu32" status 0x%"PRIx16" opc 0x%"PRIx8"" pci_nvme_err_addr_read(uint64_t addr) "addr 0x%"PRIx64"" pci_nvme_err_addr_write(uint64_t addr) "addr 0x%"PRIx64"" pci_nvme_err_cfs(void) "controller fatal status" pci_nvme_err_aio(uint16_t cid, const char *errname, uint16_t status) "cid %"PRIu16" err '%s' status 0x%"PRIx16"" +pci_nvme_err_copy_invalid_format(uint8_t format) "format 0x%"PRIx8"" pci_nvme_err_invalid_sgld(uint16_t cid, uint8_t typ) "cid %"PRIu16" type 0x%"PRIx8"" pci_nvme_err_invalid_num_sgld(uint16_t cid, uint8_t typ) "cid %"PRIu16" type 0x%"PRIx8"" -pci_nvme_err_invalid_sgl_excess_length(uint16_t cid) "cid %"PRIu16"" +pci_nvme_err_invalid_sgl_excess_length(uint32_t residual) "residual %"PRIu32"" pci_nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size" pci_nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is not page aligned: 0x%"PRIx64"" pci_nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64"" @@ -136,7 +150,6 @@ pci_nvme_err_zone_boundary(uint64_t slba, uint32_t nlb, uint64_t zcap) "lba 0x%" pci_nvme_err_zone_invalid_write(uint64_t slba, uint64_t wp) "lba 0x%"PRIx64" wp 0x%"PRIx64"" pci_nvme_err_zone_write_not_ok(uint64_t slba, uint32_t nlb, uint16_t status) "slba=%"PRIu64", nlb=%"PRIu32", status=0x%"PRIx16"" pci_nvme_err_zone_read_not_ok(uint64_t slba, uint32_t nlb, uint16_t status) "slba=%"PRIu64", nlb=%"PRIu32", status=0x%"PRIx16"" -pci_nvme_err_append_too_large(uint64_t slba, uint32_t nlb, uint8_t zasl) "slba=%"PRIu64", nlb=%"PRIu32", zasl=%"PRIu8"" pci_nvme_err_insuff_active_res(uint32_t max_active) "max_active=%"PRIu32" zone limit exceeded" pci_nvme_err_insuff_open_res(uint32_t max_open) "max_open=%"PRIu32" zone limit exceeded" pci_nvme_err_zd_extension_map_error(uint32_t zone_idx) "can't map descriptor extension for zone_idx=%"PRIu32"" diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index e8600b069d..3d2072cf75 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -870,7 +870,7 @@ static void virtio_blk_dma_restart_bh(void *opaque) virtio_blk_process_queued_requests(s, true); } -static void virtio_blk_dma_restart_cb(void *opaque, int running, +static void virtio_blk_dma_restart_cb(void *opaque, bool running, RunState state) { VirtIOBlock *s = opaque; diff --git a/hw/char/meson.build b/hw/char/meson.build index afe9a0af88..7ba38dbd96 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -8,8 +8,8 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_serial.c')) softmmu_ss.add(when: 'CONFIG_IPACK', if_true: files('ipoctal232.c')) softmmu_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) softmmu_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugcon.c')) -softmmu_ss.add(when: 'CONFIG_LM32', if_true: files('lm32_juart.c')) -softmmu_ss.add(when: 'CONFIG_LM32', if_true: files('lm32_uart.c')) +softmmu_ss.add(when: 'CONFIG_LM32_DEVICES', if_true: files('lm32_juart.c')) +softmmu_ss.add(when: 'CONFIG_LM32_DEVICES', if_true: files('lm32_uart.c')) softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-uart.c')) softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_uart.c')) softmmu_ss.add(when: 'CONFIG_PARALLEL', if_true: files('parallel.c')) diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c new file mode 100644 index 0000000000..bde44e27b4 --- /dev/null +++ b/hw/core/guest-loader.c @@ -0,0 +1,145 @@ +/* + * Guest Loader + * + * Copyright (C) 2020 Linaro + * Written by Alex BennĂ©e <alex.bennee@linaro.org> + * (based on the generic-loader by Li Guang <lig.fnst@cn.fujitsu.com>) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Much like the generic-loader this is treated as a special device + * inside QEMU. However unlike the generic-loader this device is used + * to load guest images for hypervisors. As part of that process the + * hypervisor needs to have platform information passed to it by the + * lower levels of the stack (e.g. firmware/bootloader). If you boot + * the hypervisor directly you use the guest-loader to load the Dom0 + * or equivalent guest images in the right place in the same way a + * boot loader would. + * + * This is only relevant for full system emulation. + */ + +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/sysbus.h" +#include "sysemu/dma.h" +#include "hw/loader.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "guest-loader.h" +#include "sysemu/device_tree.h" +#include "hw/boards.h" + +/* + * Insert some FDT nodes for the loaded blob. + */ +static void loader_insert_platform_data(GuestLoaderState *s, int size, + Error **errp) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + void *fdt = machine->fdt; + g_autofree char *node = g_strdup_printf("/chosen/module@0x%08" PRIx64, + s->addr); + uint64_t reg_attr[2] = {cpu_to_be64(s->addr), cpu_to_be64(size)}; + + if (!fdt) { + error_setg(errp, "Cannot modify FDT fields if the machine has none"); + return; + } + + qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop(fdt, node, "reg", ®_attr, sizeof(reg_attr)); + + if (s->kernel) { + const char *compat[2] = { "multiboot,module", "multiboot,kernel" }; + if (qemu_fdt_setprop_string_array(fdt, node, "compatible", + (char **) &compat, + ARRAY_SIZE(compat)) < 0) { + error_setg(errp, "couldn't set %s/compatible", node); + return; + } + if (s->args) { + if (qemu_fdt_setprop_string(fdt, node, "bootargs", s->args) < 0) { + error_setg(errp, "couldn't set %s/bootargs", node); + } + } + } else if (s->initrd) { + const char *compat[2] = { "multiboot,module", "multiboot,ramdisk" }; + if (qemu_fdt_setprop_string_array(fdt, node, "compatible", + (char **) &compat, + ARRAY_SIZE(compat)) < 0) { + error_setg(errp, "couldn't set %s/compatible", node); + return; + } + } +} + +static void guest_loader_realize(DeviceState *dev, Error **errp) +{ + GuestLoaderState *s = GUEST_LOADER(dev); + char *file = s->kernel ? s->kernel : s->initrd; + int size = 0; + + /* Perform some error checking on the user's options */ + if (s->kernel && s->initrd) { + error_setg(errp, "Cannot specify a kernel and initrd in same stanza"); + return; + } else if (!s->kernel && !s->initrd) { + error_setg(errp, "Need to specify a kernel or initrd image"); + return; + } else if (!s->addr) { + error_setg(errp, "Need to specify the address of guest blob"); + return; + } else if (s->args && !s->kernel) { + error_setg(errp, "Boot args only relevant to kernel blobs"); + } + + /* Default to the maximum size being the machine's ram size */ + size = load_image_targphys_as(file, s->addr, current_machine->ram_size, + NULL); + if (size < 0) { + error_setg(errp, "Cannot load specified image %s", file); + return; + } + + /* Now the image is loaded we need to update the platform data */ + loader_insert_platform_data(s, size, errp); +} + +static Property guest_loader_props[] = { + DEFINE_PROP_UINT64("addr", GuestLoaderState, addr, 0), + DEFINE_PROP_STRING("kernel", GuestLoaderState, kernel), + DEFINE_PROP_STRING("bootargs", GuestLoaderState, args), + DEFINE_PROP_STRING("initrd", GuestLoaderState, initrd), + DEFINE_PROP_END_OF_LIST(), +}; + +static void guest_loader_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = guest_loader_realize; + device_class_set_props(dc, guest_loader_props); + dc->desc = "Guest Loader"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static TypeInfo guest_loader_info = { + .name = TYPE_GUEST_LOADER, + .parent = TYPE_DEVICE, + .instance_size = sizeof(GuestLoaderState), + .class_init = guest_loader_class_init, +}; + +static void guest_loader_register_type(void) +{ + type_register_static(&guest_loader_info); +} + +type_init(guest_loader_register_type) diff --git a/hw/core/guest-loader.h b/hw/core/guest-loader.h new file mode 100644 index 0000000000..07f4b4884b --- /dev/null +++ b/hw/core/guest-loader.h @@ -0,0 +1,34 @@ +/* + * Guest Loader + * + * Copyright (C) 2020 Linaro + * Written by Alex BennĂ©e <alex.bennee@linaro.org> + * (based on the generic-loader by Li Guang <lig.fnst@cn.fujitsu.com>) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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 GUEST_LOADER_H +#define GUEST_LOADER_H + +#include "hw/qdev-core.h" +#include "qom/object.h" + +struct GuestLoaderState { + /* <private> */ + DeviceState parent_obj; + + /* <public> */ + uint64_t addr; + char *kernel; + char *args; + char *initrd; +}; + +#define TYPE_GUEST_LOADER "guest-loader" +OBJECT_DECLARE_SIMPLE_TYPE(GuestLoaderState, GUEST_LOADER) + +#endif diff --git a/hw/core/meson.build b/hw/core/meson.build index 032576f571..9cd72edf51 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -37,6 +37,8 @@ softmmu_ss.add(files( 'clock-vmstate.c', )) +softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c')) + specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files( 'machine-qmp-cmds.c', 'numa.c', diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 6784d32920..93907e82a3 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -1992,7 +1992,7 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl) } } -static void qxl_vm_change_state_handler(void *opaque, int running, +static void qxl_vm_change_state_handler(void *opaque, bool running, RunState state) { PCIQXLDevice *qxl = opaque; diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 8966b69bc7..8789722ef2 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1558,86 +1558,85 @@ 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" - -#define DEPTH 15 -#include "sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 15 -#include "sm501_template.h" - -#define DEPTH 16 -#include "sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 16 -#include "sm501_template.h" - -#define DEPTH 32 -#include "sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 32 -#include "sm501_template.h" - -static draw_line_func *draw_line8_funcs[] = { - draw_line8_8, - draw_line8_15, - draw_line8_16, - draw_line8_32, - draw_line8_32bgr, - draw_line8_15bgr, - draw_line8_16bgr, -}; - -static draw_line_func *draw_line16_funcs[] = { - draw_line16_8, - draw_line16_15, - draw_line16_16, - draw_line16_32, - draw_line16_32bgr, - draw_line16_15bgr, - draw_line16_16bgr, -}; +static void draw_line8_32(uint8_t *d, const uint8_t *s, int width, + const uint32_t *pal) +{ + 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; + *(uint32_t *)d = rgb_to_pixel32(r, g, b); + s++; + d += 4; + } while (--width != 0); +} -static draw_line_func *draw_line32_funcs[] = { - draw_line32_8, - draw_line32_15, - draw_line32_16, - draw_line32_32, - draw_line32_32bgr, - draw_line32_15bgr, - draw_line32_16bgr, -}; +static void draw_line16_32(uint8_t *d, const uint8_t *s, int width, + const uint32_t *pal) +{ + uint16_t rgb565; + uint8_t r, g, b; + + do { + rgb565 = lduw_le_p(s); + r = (rgb565 >> 8) & 0xf8; + g = (rgb565 >> 3) & 0xfc; + b = (rgb565 << 3) & 0xf8; + *(uint32_t *)d = rgb_to_pixel32(r, g, b); + s += 2; + d += 4; + } while (--width != 0); +} -static draw_hwc_line_func *draw_hwc_line_funcs[] = { - draw_hwc_line_8, - draw_hwc_line_15, - draw_hwc_line_16, - draw_hwc_line_32, - draw_hwc_line_32bgr, - draw_hwc_line_15bgr, - draw_hwc_line_16bgr, -}; +static void draw_line32_32(uint8_t *d, const uint8_t *s, int width, + const uint32_t *pal) +{ + uint8_t r, g, b; + + do { + r = s[2]; + g = s[1]; + b = s[0]; + *(uint32_t *)d = rgb_to_pixel32(r, g, b); + s += 4; + d += 4; + } while (--width != 0); +} -static inline int get_depth_index(DisplaySurface *surface) +/** + * Draw hardware cursor image on the given line. + */ +static void draw_hwc_line_32(uint8_t *d, const uint8_t *s, int width, + const uint8_t *palette, int c_x, int c_y) { - switch (surface_bits_per_pixel(surface)) { - default: - case 8: - return 0; - case 15: - return 1; - case 16: - return 2; - case 32: - if (is_surface_bgr(surface)) { - return 4; - } else { - return 3; + int i; + uint8_t r, g, b, v, bitset = 0; + + /* get cursor position */ + assert(0 <= c_y && c_y < SM501_HWC_HEIGHT); + s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */ + d += c_x * 4; + + for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) { + /* get pixel value */ + if (i % 4 == 0) { + bitset = ldub_p(s); + s++; } + v = bitset & 3; + bitset >>= 2; + + /* write pixel */ + if (v) { + v--; + r = palette[v * 3 + 0]; + g = palette[v * 3 + 1]; + b = palette[v * 3 + 2]; + *(uint32_t *)d = rgb_to_pixel32(r, g, b); + } + d += 4; } } @@ -1652,7 +1651,6 @@ static void sm501_update_display(void *opaque) int height = get_height(s, crt); int src_bpp = get_bpp(s, crt); int dst_bpp = surface_bytes_per_pixel(surface); - 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; @@ -1662,6 +1660,8 @@ static void sm501_update_display(void *opaque) uint8_t hwc_palette[3 * 3]; uint8_t *hwc_src = NULL; + assert(dst_bpp == 4); /* Output is always 32-bit RGB */ + if (!((crt ? s->dc_crt_control : s->dc_panel_control) & SM501_DC_CRT_CONTROL_ENABLE)) { return; @@ -1674,13 +1674,13 @@ static void sm501_update_display(void *opaque) /* choose draw_line function */ switch (src_bpp) { case 1: - draw_line = draw_line8_funcs[dst_depth_index]; + draw_line = draw_line8_32; break; case 2: - draw_line = draw_line16_funcs[dst_depth_index]; + draw_line = draw_line16_32; break; case 4: - draw_line = draw_line32_funcs[dst_depth_index]; + draw_line = draw_line32_32; break; default: qemu_log_mask(LOG_GUEST_ERROR, "sm501: update display" @@ -1691,7 +1691,7 @@ static void sm501_update_display(void *opaque) /* set up to draw hardware cursor */ if (is_hwc_enabled(s, crt)) { /* choose cursor draw line function */ - draw_hwc_line = draw_hwc_line_funcs[dst_depth_index]; + draw_hwc_line = draw_hwc_line_32; hwc_src = get_hwc_address(s, crt); c_x = get_hwc_x(s, crt); c_y = get_hwc_y(s, crt); diff --git a/hw/display/sm501_template.h b/hw/display/sm501_template.h deleted file mode 100644 index a60abad019..0000000000 --- a/hw/display/sm501_template.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Pixel drawing function templates for QEMU SM501 Device - * - * Copyright (c) 2008 Shin-ichiro KAWASAKI - * - * 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. - */ - -#if DEPTH == 8 -#define BPP 1 -#define PIXEL_TYPE uint8_t -#elif DEPTH == 15 || DEPTH == 16 -#define BPP 2 -#define PIXEL_TYPE uint16_t -#elif DEPTH == 32 -#define BPP 4 -#define PIXEL_TYPE uint32_t -#else -#error unsupport depth -#endif - -#ifdef BGR_FORMAT -#define PIXEL_NAME glue(DEPTH, bgr) -#else -#define PIXEL_NAME DEPTH -#endif /* BGR_FORMAT */ - - -static void glue(draw_line8_, PIXEL_NAME)( - uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) -{ - 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 = 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) -{ - uint16_t rgb565; - uint8_t r, g, b; - - do { - 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 r, g, b; - - do { - r = s[2]; - 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)(uint8_t *d, const uint8_t *s, - int width, const uint8_t *palette, int c_x, int c_y) -{ - int i; - uint8_t r, g, b, v, bitset = 0; - - /* get cursor position */ - 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_p(s); - s++; - } - v = bitset & 3; - bitset >>= 2; - - /* write pixel */ - if (v) { - v--; - 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; - } -} - -#undef DEPTH -#undef BPP -#undef PIXEL_TYPE -#undef PIXEL_NAME -#undef BGR_FORMAT diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index a01f9315e1..6cdaa1c73b 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -325,7 +325,6 @@ vhost_user_gpu_chr_read(void *opaque) } msg = g_malloc(VHOST_USER_GPU_HDR_SIZE + size); - g_return_if_fail(msg != NULL); r = qemu_chr_fe_read_all(&g->vhost_chr, (uint8_t *)&msg->payload, size); diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index 9eb489077b..d98964858e 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -438,7 +438,7 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, break; case VIRTIO_GPU_CMD_RESOURCE_FLUSH: virgl_cmd_resource_flush(g, cmd); - break; + break; case VIRTIO_GPU_CMD_RESOURCE_UNREF: virgl_cmd_resource_unref(g, cmd); break; @@ -456,7 +456,6 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, case VIRTIO_GPU_CMD_GET_CAPSET: virgl_cmd_get_capset(g, cmd); break; - case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: virtio_gpu_get_display_info(g, cmd); break; diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 2d8a366369..51872dd84c 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -162,7 +162,7 @@ static void do_kvmclock_ctrl(CPUState *cpu, run_on_cpu_data data) } } -static void kvmclock_vm_state_change(void *opaque, int running, +static void kvmclock_vm_state_change(void *opaque, bool running, RunState state) { KVMClockState *s = opaque; diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index c73254e886..c558893961 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -239,7 +239,7 @@ static void kvm_pit_irq_control(void *opaque, int n, int enable) kvm_pit_put(pit); } -static void kvm_pit_vm_state_change(void *opaque, int running, +static void kvm_pit_vm_state_change(void *opaque, bool running, RunState state) { KVMPITState *s = opaque; diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index 2c1898032e..46315445d2 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -748,7 +748,7 @@ static void do_vapic_enable(CPUState *cs, run_on_cpu_data data) s->state = VAPIC_ACTIVE; } -static void kvmvapic_vm_state_change(void *opaque, int running, +static void kvmvapic_vm_state_change(void *opaque, bool running, RunState state) { MachineState *ms = MACHINE(qdev_get_machine()); diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 68821d90f5..7ce672e5a5 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1235,7 +1235,7 @@ static void xen_main_loop_prepare(XenIOState *state) } -static void xen_hvm_change_state_handler(void *opaque, int running, +static void xen_hvm_change_state_handler(void *opaque, bool running, RunState rstate) { XenIOState *state = opaque; diff --git a/hw/ide/core.c b/hw/ide/core.c index 81db2c95de..fd69ca3167 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2677,7 +2677,7 @@ static void ide_restart_bh(void *opaque) } } -static void ide_restart_cb(void *opaque, int running, RunState state) +static void ide_restart_cb(void *opaque, bool running, RunState state) { IDEBus *bus = opaque; diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 057cb53f13..b554d2ede0 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -71,7 +71,7 @@ static int kvm_its_send_msi(GICv3ITSState *s, uint32_t value, uint16_t devid) * * The tables get flushed to guest RAM whenever the VM gets stopped. */ -static void vm_change_state_handler(void *opaque, int running, +static void vm_change_state_handler(void *opaque, bool running, RunState state) { GICv3ITSState *s = (GICv3ITSState *)opaque; diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index d040a5d1e9..65a4c880a3 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -743,7 +743,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { * * The tables get flushed to guest RAM whenever the VM gets stopped. */ -static void vm_change_state_handler(void *opaque, int running, +static void vm_change_state_handler(void *opaque, bool running, RunState state) { GICv3State *s = (GICv3State *)opaque; diff --git a/hw/intc/meson.build b/hw/intc/meson.build index b3d9345a0d..8df3656419 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -14,7 +14,7 @@ softmmu_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c')) softmmu_ss.add(when: 'CONFIG_I8259', if_true: files('i8259_common.c', 'i8259.c')) softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c')) softmmu_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c')) -softmmu_ss.add(when: 'CONFIG_LM32', if_true: files('lm32_pic.c')) +softmmu_ss.add(when: 'CONFIG_LM32_DEVICES', if_true: files('lm32_pic.c')) softmmu_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c')) softmmu_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c')) softmmu_ss.add(when: 'CONFIG_PUV3', if_true: files('puv3_intc.c')) diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index acc8c3650c..c008331160 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -504,7 +504,7 @@ static int kvmppc_xive_get_queues(SpaprXive *xive, Error **errp) * runs again. If an interrupt was queued while the VM was stopped, * simply generate a trigger. */ -static void kvmppc_xive_change_state_handler(void *opaque, int running, +static void kvmppc_xive_change_state_handler(void *opaque, bool running, RunState state) { SpaprXive *xive = opaque; diff --git a/hw/lm32/Kconfig b/hw/lm32/Kconfig index ed2e3060b0..8ac94205d7 100644 --- a/hw/lm32/Kconfig +++ b/hw/lm32/Kconfig @@ -1,14 +1,18 @@ -config LM32 +config LM32_DEVICES bool select PTIMER - select PFLASH_CFI02 config MILKYMIST bool # FIXME: disabling it results in compile-time errors select MILKYMIST_TMU2 if OPENGL && X11 - select PTIMER select PFLASH_CFI01 select FRAMEBUFFER select SD select USB_OHCI + select LM32_DEVICES + +config LM32_EVR + bool + select LM32_DEVICES + select PFLASH_CFI02 diff --git a/hw/lm32/meson.build b/hw/lm32/meson.build index 8caf0a727f..42d6f8db3d 100644 --- a/hw/lm32/meson.build +++ b/hw/lm32/meson.build @@ -1,6 +1,6 @@ lm32_ss = ss.source_set() # LM32 boards -lm32_ss.add(when: 'CONFIG_LM32', if_true: files('lm32_boards.c')) +lm32_ss.add(when: 'CONFIG_LM32_EVR', if_true: files('lm32_boards.c')) lm32_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist.c')) hw_arch += {'lm32': lm32_ss} diff --git a/hw/meson.build b/hw/meson.build index e615d72d4d..8ba79b1a52 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -30,7 +30,6 @@ subdir('rdma') subdir('rtc') subdir('scsi') subdir('sd') -subdir('semihosting') subdir('smbios') subdir('ssi') subdir('timer') diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 9afc0b427b..26e7b1bd9f 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -58,7 +58,7 @@ #include "qemu/error-report.h" #include "hw/misc/empty_slot.h" #include "sysemu/kvm.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "hw/mips/cps.h" #include "hw/qdev-clock.h" diff --git a/hw/misc/aspeed_lpc.c b/hw/misc/aspeed_lpc.c new file mode 100644 index 0000000000..2dddb27c35 --- /dev/null +++ b/hw/misc/aspeed_lpc.c @@ -0,0 +1,486 @@ +/* + * ASPEED LPC Controller + * + * Copyright (C) 2017-2018 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 "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/misc/aspeed_lpc.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +#define TO_REG(offset) ((offset) >> 2) + +#define HICR0 TO_REG(0x00) +#define HICR0_LPC3E BIT(7) +#define HICR0_LPC2E BIT(6) +#define HICR0_LPC1E BIT(5) +#define HICR1 TO_REG(0x04) +#define HICR2 TO_REG(0x08) +#define HICR2_IBFIE3 BIT(3) +#define HICR2_IBFIE2 BIT(2) +#define HICR2_IBFIE1 BIT(1) +#define HICR3 TO_REG(0x0C) +#define HICR4 TO_REG(0x10) +#define HICR4_KCSENBL BIT(2) +#define IDR1 TO_REG(0x24) +#define IDR2 TO_REG(0x28) +#define IDR3 TO_REG(0x2C) +#define ODR1 TO_REG(0x30) +#define ODR2 TO_REG(0x34) +#define ODR3 TO_REG(0x38) +#define STR1 TO_REG(0x3C) +#define STR_OBF BIT(0) +#define STR_IBF BIT(1) +#define STR_CMD_DATA BIT(3) +#define STR2 TO_REG(0x40) +#define STR3 TO_REG(0x44) +#define HICR5 TO_REG(0x80) +#define HICR6 TO_REG(0x84) +#define HICR7 TO_REG(0x88) +#define HICR8 TO_REG(0x8C) +#define HICRB TO_REG(0x100) +#define HICRB_IBFIE4 BIT(1) +#define HICRB_LPC4E BIT(0) +#define IDR4 TO_REG(0x114) +#define ODR4 TO_REG(0x118) +#define STR4 TO_REG(0x11C) + +enum aspeed_kcs_channel_id { + kcs_channel_1 = 0, + kcs_channel_2, + kcs_channel_3, + kcs_channel_4, +}; + +static const enum aspeed_lpc_subdevice aspeed_kcs_subdevice_map[] = { + [kcs_channel_1] = aspeed_lpc_kcs_1, + [kcs_channel_2] = aspeed_lpc_kcs_2, + [kcs_channel_3] = aspeed_lpc_kcs_3, + [kcs_channel_4] = aspeed_lpc_kcs_4, +}; + +struct aspeed_kcs_channel { + enum aspeed_kcs_channel_id id; + + int idr; + int odr; + int str; +}; + +static const struct aspeed_kcs_channel aspeed_kcs_channel_map[] = { + [kcs_channel_1] = { + .id = kcs_channel_1, + .idr = IDR1, + .odr = ODR1, + .str = STR1 + }, + + [kcs_channel_2] = { + .id = kcs_channel_2, + .idr = IDR2, + .odr = ODR2, + .str = STR2 + }, + + [kcs_channel_3] = { + .id = kcs_channel_3, + .idr = IDR3, + .odr = ODR3, + .str = STR3 + }, + + [kcs_channel_4] = { + .id = kcs_channel_4, + .idr = IDR4, + .odr = ODR4, + .str = STR4 + }, +}; + +struct aspeed_kcs_register_data { + const char *name; + int reg; + const struct aspeed_kcs_channel *chan; +}; + +static const struct aspeed_kcs_register_data aspeed_kcs_registers[] = { + { + .name = "idr1", + .reg = IDR1, + .chan = &aspeed_kcs_channel_map[kcs_channel_1], + }, + { + .name = "odr1", + .reg = ODR1, + .chan = &aspeed_kcs_channel_map[kcs_channel_1], + }, + { + .name = "str1", + .reg = STR1, + .chan = &aspeed_kcs_channel_map[kcs_channel_1], + }, + { + .name = "idr2", + .reg = IDR2, + .chan = &aspeed_kcs_channel_map[kcs_channel_2], + }, + { + .name = "odr2", + .reg = ODR2, + .chan = &aspeed_kcs_channel_map[kcs_channel_2], + }, + { + .name = "str2", + .reg = STR2, + .chan = &aspeed_kcs_channel_map[kcs_channel_2], + }, + { + .name = "idr3", + .reg = IDR3, + .chan = &aspeed_kcs_channel_map[kcs_channel_3], + }, + { + .name = "odr3", + .reg = ODR3, + .chan = &aspeed_kcs_channel_map[kcs_channel_3], + }, + { + .name = "str3", + .reg = STR3, + .chan = &aspeed_kcs_channel_map[kcs_channel_3], + }, + { + .name = "idr4", + .reg = IDR4, + .chan = &aspeed_kcs_channel_map[kcs_channel_4], + }, + { + .name = "odr4", + .reg = ODR4, + .chan = &aspeed_kcs_channel_map[kcs_channel_4], + }, + { + .name = "str4", + .reg = STR4, + .chan = &aspeed_kcs_channel_map[kcs_channel_4], + }, + { }, +}; + +static const struct aspeed_kcs_register_data * +aspeed_kcs_get_register_data_by_name(const char *name) +{ + const struct aspeed_kcs_register_data *pos = aspeed_kcs_registers; + + while (pos->name) { + if (!strcmp(pos->name, name)) { + return pos; + } + pos++; + } + + return NULL; +} + +static const struct aspeed_kcs_channel * +aspeed_kcs_get_channel_by_register(int reg) +{ + const struct aspeed_kcs_register_data *pos = aspeed_kcs_registers; + + while (pos->name) { + if (pos->reg == reg) { + return pos->chan; + } + pos++; + } + + return NULL; +} + +static void aspeed_kcs_get_register_property(Object *obj, + Visitor *v, + const char *name, + void *opaque, + Error **errp) +{ + const struct aspeed_kcs_register_data *data; + AspeedLPCState *s = ASPEED_LPC(obj); + uint32_t val; + + data = aspeed_kcs_get_register_data_by_name(name); + if (!data) { + return; + } + + if (!strncmp("odr", name, 3)) { + s->regs[data->chan->str] &= ~STR_OBF; + } + + val = s->regs[data->reg]; + + visit_type_uint32(v, name, &val, errp); +} + +static bool aspeed_kcs_channel_enabled(AspeedLPCState *s, + const struct aspeed_kcs_channel *channel) +{ + switch (channel->id) { + case kcs_channel_1: return s->regs[HICR0] & HICR0_LPC1E; + case kcs_channel_2: return s->regs[HICR0] & HICR0_LPC2E; + case kcs_channel_3: + return (s->regs[HICR0] & HICR0_LPC3E) && + (s->regs[HICR4] & HICR4_KCSENBL); + case kcs_channel_4: return s->regs[HICRB] & HICRB_LPC4E; + default: return false; + } +} + +static bool +aspeed_kcs_channel_ibf_irq_enabled(AspeedLPCState *s, + const struct aspeed_kcs_channel *channel) +{ + if (!aspeed_kcs_channel_enabled(s, channel)) { + return false; + } + + switch (channel->id) { + case kcs_channel_1: return s->regs[HICR2] & HICR2_IBFIE1; + case kcs_channel_2: return s->regs[HICR2] & HICR2_IBFIE2; + case kcs_channel_3: return s->regs[HICR2] & HICR2_IBFIE3; + case kcs_channel_4: return s->regs[HICRB] & HICRB_IBFIE4; + default: return false; + } +} + +static void aspeed_kcs_set_register_property(Object *obj, + Visitor *v, + const char *name, + void *opaque, + Error **errp) +{ + const struct aspeed_kcs_register_data *data; + AspeedLPCState *s = ASPEED_LPC(obj); + uint32_t val; + + data = aspeed_kcs_get_register_data_by_name(name); + if (!data) { + return; + } + + if (!visit_type_uint32(v, name, &val, errp)) { + return; + } + + if (strncmp("str", name, 3)) { + s->regs[data->reg] = val; + } + + if (!strncmp("idr", name, 3)) { + s->regs[data->chan->str] |= STR_IBF; + if (aspeed_kcs_channel_ibf_irq_enabled(s, data->chan)) { + enum aspeed_lpc_subdevice subdev; + + subdev = aspeed_kcs_subdevice_map[data->chan->id]; + qemu_irq_raise(s->subdevice_irqs[subdev]); + } + } +} + +static void aspeed_lpc_set_irq(void *opaque, int irq, int level) +{ + AspeedLPCState *s = (AspeedLPCState *)opaque; + + if (level) { + s->subdevice_irqs_pending |= BIT(irq); + } else { + s->subdevice_irqs_pending &= ~BIT(irq); + } + + qemu_set_irq(s->irq, !!s->subdevice_irqs_pending); +} + +static uint64_t aspeed_lpc_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedLPCState *s = ASPEED_LPC(opaque); + int reg = TO_REG(offset); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + switch (reg) { + case IDR1: + case IDR2: + case IDR3: + case IDR4: + { + const struct aspeed_kcs_channel *channel; + + channel = aspeed_kcs_get_channel_by_register(reg); + if (s->regs[channel->str] & STR_IBF) { + enum aspeed_lpc_subdevice subdev; + + subdev = aspeed_kcs_subdevice_map[channel->id]; + qemu_irq_lower(s->subdevice_irqs[subdev]); + } + + s->regs[channel->str] &= ~STR_IBF; + break; + } + default: + break; + } + + return s->regs[reg]; +} + +static void aspeed_lpc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned int size) +{ + AspeedLPCState *s = ASPEED_LPC(opaque); + int reg = TO_REG(offset); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + + switch (reg) { + case ODR1: + case ODR2: + case ODR3: + case ODR4: + s->regs[aspeed_kcs_get_channel_by_register(reg)->str] |= STR_OBF; + break; + default: + break; + } + + s->regs[reg] = data; +} + +static const MemoryRegionOps aspeed_lpc_ops = { + .read = aspeed_lpc_read, + .write = aspeed_lpc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void aspeed_lpc_reset(DeviceState *dev) +{ + struct AspeedLPCState *s = ASPEED_LPC(dev); + + s->subdevice_irqs_pending = 0; + + memset(s->regs, 0, sizeof(s->regs)); + + s->regs[HICR7] = s->hicr7; +} + +static void aspeed_lpc_realize(DeviceState *dev, Error **errp) +{ + AspeedLPCState *s = ASPEED_LPC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->subdevice_irqs[aspeed_lpc_kcs_1]); + sysbus_init_irq(sbd, &s->subdevice_irqs[aspeed_lpc_kcs_2]); + sysbus_init_irq(sbd, &s->subdevice_irqs[aspeed_lpc_kcs_3]); + sysbus_init_irq(sbd, &s->subdevice_irqs[aspeed_lpc_kcs_4]); + sysbus_init_irq(sbd, &s->subdevice_irqs[aspeed_lpc_ibt]); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_lpc_ops, s, + TYPE_ASPEED_LPC, 0x1000); + + sysbus_init_mmio(sbd, &s->iomem); + + qdev_init_gpio_in(dev, aspeed_lpc_set_irq, ASPEED_LPC_NR_SUBDEVS); +} + +static void aspeed_lpc_init(Object *obj) +{ + object_property_add(obj, "idr1", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "odr1", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "str1", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "idr2", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "odr2", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "str2", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "idr3", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "odr3", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "str3", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "idr4", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "odr4", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); + object_property_add(obj, "str4", "uint32", aspeed_kcs_get_register_property, + aspeed_kcs_set_register_property, NULL, NULL); +} + +static const VMStateDescription vmstate_aspeed_lpc = { + .name = TYPE_ASPEED_LPC, + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AspeedLPCState, ASPEED_LPC_NR_REGS), + VMSTATE_UINT32(subdevice_irqs_pending, AspeedLPCState), + VMSTATE_END_OF_LIST(), + } +}; + +static Property aspeed_lpc_properties[] = { + DEFINE_PROP_UINT32("hicr7", AspeedLPCState, hicr7, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void aspeed_lpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_lpc_realize; + dc->reset = aspeed_lpc_reset; + dc->desc = "Aspeed LPC Controller", + dc->vmsd = &vmstate_aspeed_lpc; + device_class_set_props(dc, aspeed_lpc_properties); +} + +static const TypeInfo aspeed_lpc_info = { + .name = TYPE_ASPEED_LPC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedLPCState), + .class_init = aspeed_lpc_class_init, + .instance_init = aspeed_lpc_init, +}; + +static void aspeed_lpc_register_types(void) +{ + type_register_static(&aspeed_lpc_info); +} + +type_init(aspeed_lpc_register_types); diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 488d086a17..ca2f939dd5 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -1098,7 +1098,7 @@ static void mac_via_init(Object *obj) TYPE_ADB_BUS, DEVICE(obj), "adb.0"); } -static void postload_update_cb(void *opaque, int running, RunState state) +static void postload_update_cb(void *opaque, bool running, RunState state) { MacVIAState *m = MAC_VIA(opaque); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index e30a555db5..00356cf12e 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -103,7 +103,12 @@ softmmu_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c')) softmmu_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c')) softmmu_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c')) softmmu_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_scu.c', 'aspeed_sdmc.c', 'aspeed_xdma.c')) +softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( + 'aspeed_lpc.c', + 'aspeed_scu.c', + 'aspeed_sdmc.c', + 'aspeed_xdma.c')) + softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c')) softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 4b15db8ca4..cae005549e 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -127,7 +127,7 @@ npcm7xx_pwm_update_freq(const char *id, uint8_t index, uint32_t old_value, uint3 npcm7xx_pwm_update_duty(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Duty: old_duty: %u, new_duty: %u" # stm32f4xx_syscfg.c -stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d" +stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interrupt: GPIO: %d, Line: %d; Level: %d" stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d" stm32f4xx_syscfg_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " " stm32f4xx_syscfg_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index 042768922c..bf91803d65 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -579,7 +579,7 @@ static uint64_t allwinner_sun8i_emac_read(void *opaque, hwaddr offset, case REG_INT_STA: /* Interrupt Status */ value = s->int_sta; break; - case REG_INT_EN: /* Interupt Enable */ + case REG_INT_EN: /* Interrupt Enable */ value = s->int_en; break; case REG_TX_CTL_0: /* Transmit Control 0 */ diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 4dcb92d966..b75f2ab8fc 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -3298,7 +3298,7 @@ e1000e_autoneg_resume(E1000ECore *core) } static void -e1000e_vm_state_change(void *opaque, int running, RunState state) +e1000e_vm_state_change(void *opaque, bool running, RunState state) { E1000ECore *core = opaque; diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 93886bba60..bd9d62b559 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -27,6 +27,7 @@ */ #include "qemu/osdep.h" +#include "qemu-common.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/ptimer.h" diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index fe055d3381..d6be0d7d18 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu-common.h" #include "net/checksum.h" #include "qemu/log.h" #include "etsec.h" diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 9e51bc82ae..01f7752014 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -217,7 +217,7 @@ static int spapr_nvram_pre_load(void *opaque) return 0; } -static void postload_update_cb(void *opaque, int running, RunState state) +static void postload_update_cb(void *opaque, bool running, RunState state) { SpaprNvram *nvram = opaque; diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 01517a6c6c..1d94485ac8 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -231,6 +231,7 @@ static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data) assert(irq2 >= 0); qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop(fdt, node, "ranges", NULL, 0); qemu_fdt_setprop_string(fdt, node, "device_type", "network"); qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,etsec2"); qemu_fdt_setprop_string(fdt, node, "model", "eTSEC"); diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index b9bf5735ea..75a22ce50b 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -233,7 +233,7 @@ static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, case HIOMAP_C_RESET: case HIOMAP_C_LOCK: default: - qemu_log_mask(LOG_GUEST_ERROR, "HIOMAP: unknow command %02X\n", cmd[2]); + qemu_log_mask(LOG_GUEST_ERROR, "HIOMAP: unknown command %02X\n", cmd[2]); break; } } diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index e9ae1569ff..be7018e8ac 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -308,7 +308,7 @@ void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, MemoryRegion *mr) } void pnv_xscom_region_init(MemoryRegion *mr, - struct Object *owner, + Object *owner, const MemoryRegionOps *ops, void *opaque, const char *name, diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 5cbbff1f8d..bf28d6bfc8 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -1059,7 +1059,7 @@ static void timebase_load(PPCTimebase *tb) } } -void cpu_ppc_clock_vm_state_change(void *opaque, int running, +void cpu_ppc_clock_vm_state_change(void *opaque, bool running, RunState state) { PPCTimebase *tb = opaque; diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index 652a21b806..974c0c8a75 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -317,7 +317,7 @@ static void ppc_booke_timer_reset_handle(void *opaque) * action will be taken. To avoid this we always clear the watchdog state when * state changes to running. */ -static void cpu_state_change_handler(void *opaque, int running, RunState state) +static void cpu_state_change_handler(void *opaque, bool running, RunState state) { PowerPCCPU *cpu = opaque; CPUPPCState *env = &cpu->env; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 85fe65f894..d56418ca29 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -28,6 +28,7 @@ #include "qemu-common.h" #include "qemu/datadir.h" #include "qapi/error.h" +#include "qapi/qapi-events-machine.h" #include "qapi/visitor.h" #include "sysemu/sysemu.h" #include "sysemu/hostmem.h" @@ -3575,6 +3576,57 @@ static SpaprDimmState *spapr_recover_pending_dimm_state(SpaprMachineState *ms, return spapr_pending_dimm_unplugs_add(ms, avail_lmbs, dimm); } +void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev) +{ + SpaprDimmState *ds; + PCDIMMDevice *dimm; + SpaprDrc *drc; + uint32_t nr_lmbs; + uint64_t size, addr_start, addr; + g_autofree char *qapi_error = NULL; + int i; + + if (!dev) { + return; + } + + dimm = PC_DIMM(dev); + ds = spapr_pending_dimm_unplugs_find(spapr, dimm); + + /* + * 'ds == NULL' would mean that the DIMM doesn't have a pending + * unplug state, but one of its DRC is marked as unplug_requested. + * This is bad and weird enough to g_assert() out. + */ + g_assert(ds); + + spapr_pending_dimm_unplugs_remove(spapr, ds); + + size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &error_abort); + nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; + + addr_start = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP, + &error_abort); + + addr = addr_start; + for (i = 0; i < nr_lmbs; i++) { + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, + addr / SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); + + drc->unplug_requested = false; + addr += SPAPR_MEMORY_BLOCK_SIZE; + } + + /* + * Tell QAPI that something happened and the memory + * hotunplug wasn't successful. + */ + qapi_error = g_strdup_printf("Memory hotunplug rejected by the guest " + "for device %s", dev->id); + qapi_event_send_mem_unplug_error(dev->id, qapi_error); +} + /* Callback to be called during DRC release. */ void spapr_lmb_release(DeviceState *dev) { @@ -3654,13 +3706,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, addr / SPAPR_MEMORY_BLOCK_SIZE); g_assert(drc); - spapr_drc_detach(drc); + spapr_drc_unplug_request(drc); addr += SPAPR_MEMORY_BLOCK_SIZE; } drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr_start / SPAPR_MEMORY_BLOCK_SIZE); - g_assert(drc); spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs, spapr_drc_index(drc)); } @@ -3722,8 +3773,12 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, g_assert(drc); if (!spapr_drc_unplug_requested(drc)) { - spapr_drc_detach(drc); + spapr_drc_unplug_request(drc); spapr_hotplug_req_remove_by_index(drc); + } else { + error_setg(errp, "core-id %d unplug is still pending, %d seconds " + "timeout remaining", + cc->core_id, spapr_drc_unplug_timeout_remaining_sec(drc)); } } @@ -3985,8 +4040,12 @@ static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev, assert(drc); if (!spapr_drc_unplug_requested(drc)) { - spapr_drc_detach(drc); + spapr_drc_unplug_request(drc); spapr_hotplug_req_remove_by_index(drc); + } else { + error_setg(errp, + "PCI Host Bridge unplug already in progress for device %s", + dev->id); } } diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 8571d5bafe..8a71b03800 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -50,6 +50,22 @@ uint32_t spapr_drc_index(SpaprDrc *drc) | (drc->id & DRC_INDEX_ID_MASK); } +static void spapr_drc_release(SpaprDrc *drc) +{ + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + drck->release(drc->dev); + + drc->unplug_requested = false; + timer_del(drc->unplug_timeout_timer); + + g_free(drc->fdt); + drc->fdt = NULL; + drc->fdt_start_offset = 0; + object_property_del(OBJECT(drc), "device"); + drc->dev = NULL; +} + static uint32_t drc_isolate_physical(SpaprDrc *drc) { switch (drc->state) { @@ -68,7 +84,7 @@ static uint32_t drc_isolate_physical(SpaprDrc *drc) if (drc->unplug_requested) { uint32_t drc_index = spapr_drc_index(drc); trace_spapr_drc_set_isolation_state_finalizing(drc_index); - spapr_drc_detach(drc); + spapr_drc_release(drc); } return RTAS_OUT_SUCCESS; @@ -132,19 +148,6 @@ static uint32_t drc_isolate_logical(SpaprDrc *drc) drc->state = SPAPR_DRC_STATE_LOGICAL_AVAILABLE; - /* if we're awaiting release, but still in an unconfigured state, - * it's likely the guest is still in the process of configuring - * the device and is transitioning the devices to an ISOLATED - * state as a part of that process. so we only complete the - * removal when this transition happens for a device in a - * configured state, as suggested by the state diagram from PAPR+ - * 2.7, 13.4 - */ - if (drc->unplug_requested) { - uint32_t drc_index = spapr_drc_index(drc); - trace_spapr_drc_set_isolation_state_finalizing(drc_index); - spapr_drc_detach(drc); - } return RTAS_OUT_SUCCESS; } @@ -222,7 +225,7 @@ static uint32_t drc_set_unusable(SpaprDrc *drc) if (drc->unplug_requested) { uint32_t drc_index = spapr_drc_index(drc); trace_spapr_drc_set_allocation_state_finalizing(drc_index); - spapr_drc_detach(drc); + spapr_drc_release(drc); } return RTAS_OUT_SUCCESS; @@ -369,6 +372,17 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, } while (fdt_depth != 0); } +static void spapr_drc_start_unplug_timeout_timer(SpaprDrc *drc) +{ + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + if (drck->unplug_timeout_seconds != 0) { + timer_mod(drc->unplug_timeout_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + drck->unplug_timeout_seconds * 1000); + } +} + void spapr_drc_attach(SpaprDrc *drc, DeviceState *d) { trace_spapr_drc_attach(spapr_drc_index(drc)); @@ -385,30 +399,18 @@ void spapr_drc_attach(SpaprDrc *drc, DeviceState *d) NULL, 0); } -static void spapr_drc_release(SpaprDrc *drc) -{ - SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - - drck->release(drc->dev); - - drc->unplug_requested = false; - g_free(drc->fdt); - drc->fdt = NULL; - drc->fdt_start_offset = 0; - object_property_del(OBJECT(drc), "device"); - drc->dev = NULL; -} - -void spapr_drc_detach(SpaprDrc *drc) +void spapr_drc_unplug_request(SpaprDrc *drc) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - trace_spapr_drc_detach(spapr_drc_index(drc)); + trace_spapr_drc_unplug_request(spapr_drc_index(drc)); g_assert(drc->dev); drc->unplug_requested = true; + spapr_drc_start_unplug_timeout_timer(drc); + if (drc->state != drck->empty_state) { trace_spapr_drc_awaiting_quiesce(spapr_drc_index(drc)); return; @@ -417,6 +419,15 @@ void spapr_drc_detach(SpaprDrc *drc) spapr_drc_release(drc); } +int spapr_drc_unplug_timeout_remaining_sec(SpaprDrc *drc) +{ + if (drc->unplug_requested) { + return timer_deadline_ms(drc->unplug_timeout_timer) / 1000; + } + + return 0; +} + bool spapr_drc_reset(SpaprDrc *drc) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); @@ -488,11 +499,23 @@ static bool spapr_drc_needed(void *opaque) spapr_drc_unplug_requested(drc); } +static int spapr_drc_post_load(void *opaque, int version_id) +{ + SpaprDrc *drc = opaque; + + if (drc->unplug_requested) { + spapr_drc_start_unplug_timeout_timer(drc); + } + + return 0; +} + static const VMStateDescription vmstate_spapr_drc = { .name = "spapr_drc", .version_id = 1, .minimum_version_id = 1, .needed = spapr_drc_needed, + .post_load = spapr_drc_post_load, .fields = (VMStateField []) { VMSTATE_UINT32(state, SpaprDrc), VMSTATE_END_OF_LIST() @@ -503,6 +526,15 @@ static const VMStateDescription vmstate_spapr_drc = { } }; +static void drc_unplug_timeout_cb(void *opaque) +{ + SpaprDrc *drc = opaque; + + if (drc->unplug_requested) { + drc->unplug_requested = false; + } +} + static void drc_realize(DeviceState *d, Error **errp) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); @@ -525,6 +557,11 @@ static void drc_realize(DeviceState *d, Error **errp) object_property_add_alias(root_container, link_name, drc->owner, child_name); g_free(link_name); + + drc->unplug_timeout_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + drc_unplug_timeout_cb, + drc); + vmstate_register(VMSTATE_IF(drc), spapr_drc_index(drc), &vmstate_spapr_drc, drc); trace_spapr_drc_realize_complete(spapr_drc_index(drc)); @@ -542,6 +579,7 @@ static void drc_unrealize(DeviceState *d) name = g_strdup_printf("%x", spapr_drc_index(drc)); object_property_del(root_container, name); g_free(name); + timer_free(drc->unplug_timeout_timer); } SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type, @@ -683,6 +721,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) drck->drc_name_prefix = "CPU "; drck->release = spapr_core_release; drck->dt_populate = spapr_core_dt_populate; + drck->unplug_timeout_seconds = 15; } static void spapr_drc_pci_class_init(ObjectClass *k, void *data) @@ -1190,6 +1229,15 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + /* + * This indicates that the kernel is reconfiguring a LMB due to + * a failed hotunplug. Rollback the DIMM unplug process. + */ + if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_LMB && + drc->unplug_requested) { + spapr_memory_unplug_rollback(spapr, drc->dev); + } + if (!drc->fdt) { void *fdt; int fdt_size; diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index f1c7479816..feba18cb12 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1723,12 +1723,12 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, * functions, even if their unplug weren't requested * beforehand. */ - spapr_drc_detach(func_drc); + spapr_drc_unplug_request(func_drc); } } } - spapr_drc_detach(drc); + spapr_drc_unplug_request(drc); /* if this isn't func 0, defer unplug event. otherwise signal removal * for all present functions @@ -1743,6 +1743,10 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, } } } + } else { + error_setg(errp, + "PCI device unplug already in progress for device %s", + drc->dev->id); } } diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 1e91984526..b4bbfbb013 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -50,7 +50,7 @@ spapr_drc_set_allocation_state(uint32_t index, int state) "drc: 0x%"PRIx32", sta spapr_drc_set_allocation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_set_configured(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_attach(uint32_t index) "drc: 0x%"PRIx32 -spapr_drc_detach(uint32_t index) "drc: 0x%"PRIx32 +spapr_drc_unplug_request(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_awaiting_quiesce(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_reset(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_realize(uint32_t index) "drc: 0x%"PRIx32 diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 4f0c2fbca0..0b39101a5e 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -195,14 +195,14 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, hwaddr flashbase = virt_memmap[VIRT_FLASH].base; if (mc->dtb) { - fdt = s->fdt = load_device_tree(mc->dtb, &s->fdt_size); + fdt = mc->fdt = load_device_tree(mc->dtb, &s->fdt_size); if (!fdt) { error_report("load_device_tree() failed"); exit(1); } goto update_bootargs; } else { - fdt = s->fdt = create_device_tree(&s->fdt_size); + fdt = mc->fdt = create_device_tree(&s->fdt_size); if (!fdt) { error_report("create_device_tree() failed"); exit(1); @@ -444,12 +444,12 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, g_free(name); name = g_strdup_printf("/soc/flash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop_string(s->fdt, name, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + qemu_fdt_add_subnode(mc->fdt, name); + qemu_fdt_setprop_string(mc->fdt, name, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(mc->fdt, name, "reg", 2, flashbase, 2, flashsize, 2, flashbase + flashsize, 2, flashsize); - qemu_fdt_setprop_cell(s->fdt, name, "bank-width", 4); + qemu_fdt_setprop_cell(mc->fdt, name, "bank-width", 4); g_free(name); update_bootargs: @@ -667,9 +667,9 @@ static void virt_machine_init(MachineState *machine) hwaddr end = riscv_load_initrd(machine->initrd_filename, machine->ram_size, kernel_entry, &start); - qemu_fdt_setprop_cell(s->fdt, "/chosen", + qemu_fdt_setprop_cell(machine->fdt, "/chosen", "linux,initrd-start", start); - qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end", + qemu_fdt_setprop_cell(machine->fdt, "/chosen", "linux,initrd-end", end); } } else { @@ -690,12 +690,12 @@ static void virt_machine_init(MachineState *machine) /* Compute the fdt load address in dram */ fdt_load_addr = riscv_load_fdt(memmap[VIRT_DRAM].base, - machine->ram_size, s->fdt); + machine->ram_size, machine->fdt); /* load the reset vector */ riscv_setup_rom_reset_vec(machine, &s->soc[0], start_addr, virt_memmap[VIRT_MROM].base, virt_memmap[VIRT_MROM].size, kernel_entry, - fdt_load_addr, s->fdt); + fdt_load_addr, machine->fdt); /* SiFive Test MMIO device */ sifive_test_create(memmap[VIRT_TEST].base); diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c index 6e21d83181..0b94477486 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -78,7 +78,7 @@ static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) } } -static void kvm_s390_tod_vm_state_change(void *opaque, int running, +static void kvm_s390_tod_vm_state_change(void *opaque, bool running, RunState state) { S390TODState *td = opaque; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 2d674f94d7..2a0a98cac9 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -181,7 +181,7 @@ void scsi_req_retry(SCSIRequest *req) req->retry = true; } -static void scsi_dma_restart_cb(void *opaque, int running, RunState state) +static void scsi_dma_restart_cb(void *opaque, bool running, RunState state) { SCSIDevice *s = opaque; diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index bd7103cd0e..2eaea7e637 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2565,6 +2565,7 @@ static void scsi_disk_new_request_dump(uint32_t lun, uint32_t tag, uint8_t *buf) int len = scsi_cdb_length(buf); char *line_buffer, *p; + assert(len > 0 && len <= 16); line_buffer = g_malloc(len * 5 + 1); for (i = 0, p = line_buffer; i < len; i++) { diff --git a/hw/timer/meson.build b/hw/timer/meson.build index a429792b08..598d058506 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -19,7 +19,7 @@ softmmu_ss.add(when: 'CONFIG_HPET', if_true: files('hpet.c')) softmmu_ss.add(when: 'CONFIG_I8254', if_true: files('i8254_common.c', 'i8254.c')) softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_epit.c')) softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c')) -softmmu_ss.add(when: 'CONFIG_LM32', if_true: files('lm32_timer.c')) +softmmu_ss.add(when: 'CONFIG_LM32_DEVICES', if_true: files('lm32_timer.c')) softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-sysctl.c')) softmmu_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c')) softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c')) diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 2d566f7db1..5c76bed77a 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -301,7 +301,7 @@ static void *event_thread(void *arg) } else { if (event->reader != card->reader) { fprintf(stderr, - "ERROR: wrong reader: quiting event_thread\n"); + "ERROR: wrong reader: quitting event_thread\n"); break; } } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 212eb05d3d..f71af0ad2d 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2436,7 +2436,7 @@ static int usb_ehci_post_load(void *opaque, int version_id) return 0; } -static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) +static void usb_ehci_vm_state_change(void *opaque, bool running, RunState state) { EHCIState *ehci = opaque; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index f8c64c8b95..1cf2816772 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1126,7 +1126,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) OHCI_SET_BM(td.flags, TD_EC, 3); break; } - /* An error occured so we have to clear the interrupt counter. See + /* An error occurred so we have to clear the interrupt counter. See * spec at 6.4.4 on page 104 */ ohci->done_count = 0; } diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 354713a77d..2518306f52 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1783,7 +1783,7 @@ type_init(usb_host_register_types) static QEMUTimer *usb_auto_timer; static VMChangeStateEntry *usb_vmstate; -static void usb_host_vm_state(void *unused, int running, RunState state) +static void usb_host_vm_state(void *unused, bool running, RunState state) { if (running) { usb_host_auto_check(unused); diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 7e9e3fecbf..17f06f3417 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1395,7 +1395,7 @@ static void usbredir_chardev_event(void *opaque, QEMUChrEvent event) * init + destroy */ -static void usbredir_vm_state_change(void *priv, int running, RunState state) +static void usbredir_vm_state_change(void *priv, bool running, RunState state) { USBRedirDevice *dev = priv; diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 00daa50ed8..134bdccc4f 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -727,7 +727,7 @@ static SaveVMHandlers savevm_vfio_handlers = { /* ---------------------------------------------------------------------- */ -static void vfio_vmstate_change(void *opaque, int running, RunState state) +static void vfio_vmstate_change(void *opaque, bool running, RunState state) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 2a01662b08..e2163a0d63 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -909,7 +909,7 @@ check_dev_state: r = 0; } if (r) { - /* An error is occured. */ + /* An error occurred. */ dev->log_enabled = false; } diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 76ce937693..cc8e9f775d 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -133,7 +133,7 @@ static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) return f; } -static void virtio_rng_vm_state_change(void *opaque, int running, +static void virtio_rng_vm_state_change(void *opaque, bool running, RunState state) { VirtIORNG *vrng = opaque; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 1fd1917ca0..07f4e60b30 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3208,7 +3208,7 @@ void virtio_cleanup(VirtIODevice *vdev) qemu_del_vm_change_state_handler(vdev->vmstate); } -static void virtio_vmstate_change(void *opaque, int running, RunState state) +static void virtio_vmstate_change(void *opaque, bool running, RunState state) { VirtIODevice *vdev = opaque; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 35faa3aa26..d200f33c10 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -139,7 +139,7 @@ bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs); * Set a rate-limiting parameter for the job; the actual meaning may * vary depending on the job type. */ -void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); +bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); /** * block_job_query: diff --git a/include/block/nvme.h b/include/block/nvme.h index 07cfc92936..372d0f2799 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -566,6 +566,7 @@ enum NvmeAdminCommands { NVME_ADM_CMD_ASYNC_EV_REQ = 0x0c, NVME_ADM_CMD_ACTIVATE_FW = 0x10, NVME_ADM_CMD_DOWNLOAD_FW = 0x11, + NVME_ADM_CMD_NS_ATTACHMENT = 0x15, NVME_ADM_CMD_FORMAT_NVM = 0x80, NVME_ADM_CMD_SECURITY_SEND = 0x81, NVME_ADM_CMD_SECURITY_RECV = 0x82, @@ -579,6 +580,7 @@ enum NvmeIoCommands { NVME_CMD_COMPARE = 0x05, NVME_CMD_WRITE_ZEROES = 0x08, NVME_CMD_DSM = 0x09, + NVME_CMD_COPY = 0x19, NVME_CMD_ZONE_MGMT_SEND = 0x79, NVME_CMD_ZONE_MGMT_RECV = 0x7a, NVME_CMD_ZONE_APPEND = 0x7d, @@ -724,9 +726,41 @@ typedef struct QEMU_PACKED NvmeDsmRange { uint64_t slba; } NvmeDsmRange; +enum { + NVME_COPY_FORMAT_0 = 0x0, +}; + +typedef struct QEMU_PACKED NvmeCopyCmd { + uint8_t opcode; + uint8_t flags; + uint16_t cid; + uint32_t nsid; + uint32_t rsvd2[4]; + NvmeCmdDptr dptr; + uint64_t sdlba; + uint8_t nr; + uint8_t control[3]; + uint16_t rsvd13; + uint16_t dspec; + uint32_t reftag; + uint16_t apptag; + uint16_t appmask; +} NvmeCopyCmd; + +typedef struct QEMU_PACKED NvmeCopySourceRange { + uint8_t rsvd0[8]; + uint64_t slba; + uint16_t nlb; + uint8_t rsvd18[6]; + uint32_t reftag; + uint16_t apptag; + uint16_t appmask; +} NvmeCopySourceRange; + enum NvmeAsyncEventRequest { NVME_AER_TYPE_ERROR = 0, NVME_AER_TYPE_SMART = 1, + NVME_AER_TYPE_NOTICE = 2, NVME_AER_TYPE_IO_SPECIFIC = 6, NVME_AER_TYPE_VENDOR_SPECIFIC = 7, NVME_AER_INFO_ERR_INVALID_DB_REGISTER = 0, @@ -738,6 +772,7 @@ enum NvmeAsyncEventRequest { NVME_AER_INFO_SMART_RELIABILITY = 0, NVME_AER_INFO_SMART_TEMP_THRESH = 1, NVME_AER_INFO_SMART_SPARE_THRESH = 2, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED = 0, }; typedef struct QEMU_PACKED NvmeAerResult { @@ -804,9 +839,13 @@ enum NvmeStatusCodes { NVME_FEAT_NOT_CHANGEABLE = 0x010e, NVME_FEAT_NOT_NS_SPEC = 0x010f, NVME_FW_REQ_SUSYSTEM_RESET = 0x0110, + NVME_NS_ALREADY_ATTACHED = 0x0118, + NVME_NS_NOT_ATTACHED = 0x011A, + NVME_NS_CTRL_LIST_INVALID = 0x011C, NVME_CONFLICTING_ATTRS = 0x0180, NVME_INVALID_PROT_INFO = 0x0181, NVME_WRITE_TO_RO = 0x0182, + NVME_CMD_SIZE_LIMIT = 0x0183, NVME_ZONE_BOUNDARY_ERROR = 0x01b8, NVME_ZONE_FULL = 0x01b9, NVME_ZONE_READ_ONLY = 0x01ba, @@ -903,6 +942,7 @@ enum NvmeLogIdentifier { NVME_LOG_ERROR_INFO = 0x01, NVME_LOG_SMART_INFO = 0x02, NVME_LOG_FW_SLOT_INFO = 0x03, + NVME_LOG_CHANGED_NSLIST = 0x04, NVME_LOG_CMD_EFFECTS = 0x05, }; @@ -918,6 +958,7 @@ typedef struct QEMU_PACKED NvmePSD { uint8_t resv[16]; } NvmePSD; +#define NVME_CONTROLLER_LIST_SIZE 2048 #define NVME_IDENTIFY_DATA_SIZE 4096 enum NvmeIdCns { @@ -930,6 +971,7 @@ enum NvmeIdCns { NVME_ID_CNS_CS_NS_ACTIVE_LIST = 0x07, NVME_ID_CNS_NS_PRESENT_LIST = 0x10, NVME_ID_CNS_NS_PRESENT = 0x11, + NVME_ID_CNS_NS_ATTACHED_CTRL_LIST = 0x12, NVME_ID_CNS_CS_NS_PRESENT_LIST = 0x1a, NVME_ID_CNS_CS_NS_PRESENT = 0x1b, NVME_ID_CNS_IO_COMMAND_SET = 0x1c, @@ -994,7 +1036,7 @@ typedef struct QEMU_PACKED NvmeIdCtrl { uint8_t nvscc; uint8_t rsvd531; uint16_t acwu; - uint8_t rsvd534[2]; + uint16_t ocfs; uint32_t sgls; uint8_t rsvd540[228]; uint8_t subnqn[256]; @@ -1008,10 +1050,25 @@ typedef struct NvmeIdCtrlZoned { uint8_t rsvd1[4095]; } NvmeIdCtrlZoned; +typedef struct NvmeIdCtrlNvm { + uint8_t vsl; + uint8_t wzsl; + uint8_t wusl; + uint8_t dmrl; + uint32_t dmrsl; + uint64_t dmsl; + uint8_t rsvd16[4080]; +} NvmeIdCtrlNvm; + +enum NvmeIdCtrlOaes { + NVME_OAES_NS_ATTR = 1 << 8, +}; + enum NvmeIdCtrlOacs { NVME_OACS_SECURITY = 1 << 0, NVME_OACS_FORMAT = 1 << 1, NVME_OACS_FW = 1 << 2, + NVME_OACS_NS_MGMT = 1 << 3, }; enum NvmeIdCtrlOncs { @@ -1022,6 +1079,19 @@ enum NvmeIdCtrlOncs { NVME_ONCS_FEATURES = 1 << 4, NVME_ONCS_RESRVATIONS = 1 << 5, NVME_ONCS_TIMESTAMP = 1 << 6, + NVME_ONCS_COPY = 1 << 8, +}; + +enum NvmeIdCtrlOcfs { + NVME_OCFS_COPY_FORMAT_0 = 1 << 0, +}; + +enum NvmeIdctrlVwc { + NVME_VWC_PRESENT = 1 << 0, + NVME_VWC_NSID_BROADCAST_NO_SUPPORT = 0 << 1, + NVME_VWC_NSID_BROADCAST_RESERVED = 1 << 1, + NVME_VWC_NSID_BROADCAST_CTRL_SPEC = 2 << 1, + NVME_VWC_NSID_BROADCAST_SUPPORT = 3 << 1, }; enum NvmeIdCtrlFrmw { @@ -1034,6 +1104,10 @@ enum NvmeIdCtrlLpa { NVME_LPA_EXTENDED = 1 << 2, }; +enum NvmeIdCtrlCmic { + NVME_CMIC_MULTI_CTRL = 1 << 1, +}; + #define NVME_CTRL_SQES_MIN(sqes) ((sqes) & 0xf) #define NVME_CTRL_SQES_MAX(sqes) (((sqes) >> 4) & 0xf) #define NVME_CTRL_CQES_MIN(cqes) ((cqes) & 0xf) @@ -1171,7 +1245,10 @@ typedef struct QEMU_PACKED NvmeIdNs { uint16_t npdg; uint16_t npda; uint16_t nows; - uint8_t rsvd74[30]; + uint16_t mssrl; + uint32_t mcl; + uint8_t msrc; + uint8_t rsvd81[23]; uint8_t nguid[16]; uint64_t eui64; NvmeLBAF lbaf[16]; @@ -1199,6 +1276,10 @@ enum NvmeNsIdentifierType { NVME_NIDT_CSI = 0x04, }; +enum NvmeIdNsNmic { + NVME_NMIC_NS_SHARED = 1 << 0, +}; + enum NvmeCsi { NVME_CSI_NVM = 0x00, NVME_CSI_ZONED = 0x02, @@ -1323,6 +1404,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeZonedResult) != 8); QEMU_BUILD_BUG_ON(sizeof(NvmeCqe) != 16); QEMU_BUILD_BUG_ON(sizeof(NvmeDsmRange) != 16); + QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRange) != 32); QEMU_BUILD_BUG_ON(sizeof(NvmeCmd) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeDeleteQ) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeCreateCq) != 64); @@ -1330,6 +1412,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeIdentify) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeRwCmd) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeDsmCmd) != 64); + QEMU_BUILD_BUG_ON(sizeof(NvmeCopyCmd) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeRangeType) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512); @@ -1337,6 +1420,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeEffectsLog) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrl) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrlZoned) != 4096); + QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrlNvm) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeLBAF) != 4); QEMU_BUILD_BUG_ON(sizeof(NvmeLBAFE) != 16); QEMU_BUILD_BUG_ON(sizeof(NvmeIdNs) != 4096); diff --git a/include/exec/memory.h b/include/exec/memory.h index c6fb714e49..54ccf1a5f0 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -828,7 +828,7 @@ static inline bool MemoryRegionSection_eq(MemoryRegionSection *a, * @size: size of the region; any subregions beyond this size will be clipped */ void memory_region_init(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size); @@ -876,7 +876,7 @@ void memory_region_unref(MemoryRegion *mr); * @size: size of the region. */ void memory_region_init_io(MemoryRegion *mr, - struct Object *owner, + Object *owner, const MemoryRegionOps *ops, void *opaque, const char *name, @@ -898,7 +898,7 @@ void memory_region_init_io(MemoryRegion *mr, * RAM memory region to be migrated; that is the responsibility of the caller. */ void memory_region_init_ram_nomigrate(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, Error **errp); @@ -920,7 +920,7 @@ void memory_region_init_ram_nomigrate(MemoryRegion *mr, * The only difference is part of the RAM region can be remapped. */ void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, bool share, @@ -946,7 +946,7 @@ void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr, * RAM memory region to be migrated; that is the responsibility of the caller. */ void memory_region_init_resizeable_ram(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, uint64_t max_size, @@ -979,7 +979,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, * RAM memory region to be migrated; that is the responsibility of the caller. */ void memory_region_init_ram_from_file(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, uint64_t align, @@ -1005,7 +1005,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, * RAM memory region to be migrated; that is the responsibility of the caller. */ void memory_region_init_ram_from_fd(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, bool share, @@ -1030,7 +1030,7 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, * RAM memory region to be migrated; that is the responsibility of the caller. */ void memory_region_init_ram_ptr(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, void *ptr); @@ -1058,7 +1058,7 @@ void memory_region_init_ram_ptr(MemoryRegion *mr, * (For RAM device memory regions, migrating the contents rarely makes sense.) */ void memory_region_init_ram_device_ptr(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, void *ptr); @@ -1076,7 +1076,7 @@ void memory_region_init_ram_device_ptr(MemoryRegion *mr, * @size: size of the region. */ void memory_region_init_alias(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, MemoryRegion *orig, hwaddr offset, @@ -1101,7 +1101,7 @@ void memory_region_init_alias(MemoryRegion *mr, * @errp: pointer to Error*, to store an error if it happens. */ void memory_region_init_rom_nomigrate(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, Error **errp); @@ -1124,7 +1124,7 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr, * @errp: pointer to Error*, to store an error if it happens. */ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, - struct Object *owner, + Object *owner, const MemoryRegionOps *ops, void *opaque, const char *name, @@ -1183,7 +1183,7 @@ void memory_region_init_iommu(void *_iommu_mr, * If you pass a non-NULL non-device @owner then we will assert. */ void memory_region_init_ram(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, Error **errp); @@ -1210,7 +1210,7 @@ void memory_region_init_ram(MemoryRegion *mr, * @errp: pointer to Error*, to store an error if it happens. */ void memory_region_init_rom(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, Error **errp); @@ -1241,7 +1241,7 @@ void memory_region_init_rom(MemoryRegion *mr, * @errp: pointer to Error*, to store an error if it happens. */ void memory_region_init_rom_device(MemoryRegion *mr, - struct Object *owner, + Object *owner, const MemoryRegionOps *ops, void *opaque, const char *name, @@ -1254,7 +1254,7 @@ void memory_region_init_rom_device(MemoryRegion *mr, * * @mr: the memory region being queried. */ -struct Object *memory_region_owner(MemoryRegion *mr); +Object *memory_region_owner(MemoryRegion *mr); /** * memory_region_size: get a memory region's size. diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 11cfe6e358..9359d6da33 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -28,6 +28,7 @@ #include "hw/sd/aspeed_sdhci.h" #include "hw/usb/hcd-ehci.h" #include "qom/object.h" +#include "hw/misc/aspeed_lpc.h" #define ASPEED_SPIS_NUM 2 #define ASPEED_EHCIS_NUM 2 @@ -61,6 +62,7 @@ struct AspeedSoCState { AspeedGPIOState gpio_1_8v; AspeedSDHCIState sdhci; AspeedSDHCIState emmc; + AspeedLPCState lpc; }; #define TYPE_ASPEED_SOC "aspeed-soc" @@ -130,6 +132,7 @@ enum { ASPEED_DEV_SDRAM, ASPEED_DEV_XDMA, ASPEED_DEV_EMMC, + ASPEED_DEV_KCS, }; #endif /* ASPEED_SOC_H */ diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index ee9a93101e..921416f918 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -153,7 +153,6 @@ struct VirtMachineState { MemMapEntry *memmap; char *pciehb_nodename; const int *irqmap; - void *fdt; int fdt_size; uint32_t clock_phandle; uint32_t gic_phandle; diff --git a/include/hw/boards.h b/include/hw/boards.h index a46dfe5d1a..5fda5fd128 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -258,6 +258,7 @@ struct MachineState { /*< public >*/ + void *fdt; char *dtb; char *dumpdtb; int phandle_start; diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index 78409ab34a..6ee458e7bc 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -417,7 +417,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, /* * Since we want to be able to modify the mapped buffer, we set the - * 'writeble' parameter to 'true'. Modifications to the buffer are not + * 'writable' parameter to 'true'. Modifications to the buffer are not * written back to the file. */ mapped_file = g_mapped_file_new_from_fd(fd, true, NULL); diff --git a/include/hw/misc/aspeed_lpc.h b/include/hw/misc/aspeed_lpc.h new file mode 100644 index 0000000000..df418cfcd3 --- /dev/null +++ b/include/hw/misc/aspeed_lpc.h @@ -0,0 +1,47 @@ +/* + * ASPEED LPC Controller + * + * Copyright (C) 2017-2018 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef ASPEED_LPC_H +#define ASPEED_LPC_H + +#include "hw/sysbus.h" + +#include <stdint.h> + +#define TYPE_ASPEED_LPC "aspeed.lpc" +#define ASPEED_LPC(obj) OBJECT_CHECK(AspeedLPCState, (obj), TYPE_ASPEED_LPC) + +#define ASPEED_LPC_NR_REGS (0x260 >> 2) + +enum aspeed_lpc_subdevice { + aspeed_lpc_kcs_1 = 0, + aspeed_lpc_kcs_2, + aspeed_lpc_kcs_3, + aspeed_lpc_kcs_4, + aspeed_lpc_ibt, +}; + +#define ASPEED_LPC_NR_SUBDEVS 5 + +typedef struct AspeedLPCState { + /* <private> */ + SysBusDevice parent; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq irq; + + qemu_irq subdevice_irqs[ASPEED_LPC_NR_SUBDEVS]; + uint32_t subdevice_irqs_pending; + + uint32_t regs[ASPEED_LPC_NR_REGS]; + uint32_t hicr7; +} AspeedLPCState; + +#endif /* _ASPEED_LPC_H_ */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 8578f5a207..2ff9f7a8d6 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -139,7 +139,7 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, MemoryRegion *mr); void pnv_xscom_region_init(MemoryRegion *mr, - struct Object *owner, + Object *owner, const MemoryRegionOps *ops, void *opaque, const char *name, diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index ccbeeca1de..47cebaf3ac 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -847,6 +847,7 @@ int spapr_hpt_shift_for_ramsize(uint64_t ramsize); int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp); void spapr_clear_pending_events(SpaprMachineState *spapr); void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr); +void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev); int spapr_max_server_number(SpaprMachineState *spapr); void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte0, uint64_t pte1); diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h index 8982927d5c..26599c385a 100644 --- a/include/hw/ppc/spapr_drc.h +++ b/include/hw/ppc/spapr_drc.h @@ -187,6 +187,8 @@ typedef struct SpaprDrc { bool unplug_requested; void *fdt; int fdt_start_offset; + + QEMUTimer *unplug_timeout_timer; } SpaprDrc; struct SpaprMachineState; @@ -209,6 +211,8 @@ typedef struct SpaprDrcClass { int (*dt_populate)(SpaprDrc *drc, struct SpaprMachineState *spapr, void *fdt, int *fdt_start_offset, Error **errp); + + int unplug_timeout_seconds; } SpaprDrcClass; typedef struct SpaprDrcPhysical { @@ -243,7 +247,8 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask); * beforehand (eg. check drc->dev at pre-plug). */ void spapr_drc_attach(SpaprDrc *drc, DeviceState *d); -void spapr_drc_detach(SpaprDrc *drc); +void spapr_drc_unplug_request(SpaprDrc *drc); +int spapr_drc_unplug_timeout_remaining_sec(SpaprDrc *drc); /* * Reset all DRCs, causing pending hot-plug/unplug requests to complete. diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 84b7a3848f..632da52018 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -41,7 +41,6 @@ struct RISCVVirtState { DeviceState *plic[VIRT_SOCKETS_MAX]; PFlashCFI01 *flash[2]; - void *fdt; int fdt_size; }; diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 08c869ab0a..7901ab276c 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -133,7 +133,7 @@ struct SubchDev { bool ccw_fmt_1; bool thinint_active; uint8_t ccw_no_data_cnt; - uint16_t migrated_schid; /* used for missmatch detection */ + uint16_t migrated_schid; /* used for mismatch detection */ CcwDataStream cds; /* transport-provided data: */ int (*ccw_cb) (SubchDev *, CCW1); diff --git a/include/qemu-common.h b/include/qemu-common.h index 654621444e..73bcf763ed 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -13,7 +13,7 @@ #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) /* Copyright string for -version arguments, About dialogs, etc */ -#define QEMU_COPYRIGHT "Copyright (c) 2003-2020 " \ +#define QEMU_COPYRIGHT "Copyright (c) 2003-2021 " \ "Fabrice Bellard and the QEMU Project developers" /* Bug reporting information for --help arguments, About dialogs, etc */ diff --git a/include/qemu/id.h b/include/qemu/id.h index b55c406e69..46b759b284 100644 --- a/include/qemu/id.h +++ b/include/qemu/id.h @@ -5,6 +5,7 @@ typedef enum IdSubSystems { ID_QDEV, ID_BLOCK, ID_CHR, + ID_NET, ID_MAX /* last element, used as array size */ } IdSubSystems; diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 1678238384..5e76e3f8c2 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -795,6 +795,14 @@ static inline int64_t get_max_clock_jump(void) return 60 * NANOSECONDS_PER_SECOND; } +/** + * timer_deadline_ms: + * + * Returns the remaining miliseconds for @timer to expire, or zero + * if the timer is no longer pending. + */ +int64_t timer_deadline_ms(QEMUTimer *timer); + /* * Low level clock functions */ diff --git a/include/hw/semihosting/console.h b/include/semihosting/console.h index 0238f540f4..0238f540f4 100644 --- a/include/hw/semihosting/console.h +++ b/include/semihosting/console.h diff --git a/include/hw/semihosting/semihost.h b/include/semihosting/semihost.h index 0c55ade3ac..0c55ade3ac 100644 --- a/include/hw/semihosting/semihost.h +++ b/include/semihosting/semihost.h diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index 982c89345f..8a2fe55622 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -70,6 +70,23 @@ int qemu_fdt_setprop_u64(void *fdt, const char *node_path, const char *property, uint64_t val); int qemu_fdt_setprop_string(void *fdt, const char *node_path, const char *property, const char *string); + +/** + * qemu_fdt_setprop_string_array: set a string array property + * + * @fdt: pointer to the dt blob + * @name: node name + * @prop: property array + * @array: pointer to an array of string pointers + * @len: length of array + * + * assigns a string array to a property. This function converts and + * array of strings to a sequential string with \0 separators before + * setting the property. + */ +int qemu_fdt_setprop_string_array(void *fdt, const char *node_path, + const char *prop, char **array, int len); + int qemu_fdt_setprop_phandle(void *fdt, const char *node_path, const char *property, const char *target_node_path); diff --git a/include/sysemu/runstate.h b/include/sysemu/runstate.h index e557f470d4..a535691573 100644 --- a/include/sysemu/runstate.h +++ b/include/sysemu/runstate.h @@ -6,11 +6,11 @@ bool runstate_check(RunState state); void runstate_set(RunState new_state); -int runstate_is_running(void); +bool runstate_is_running(void); bool runstate_needs_reset(void); bool runstate_store(char *str, size_t size); -typedef void VMChangeStateHandler(void *opaque, int running, RunState state); +typedef void VMChangeStateHandler(void *opaque, bool running, RunState state); VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque); @@ -20,7 +20,13 @@ VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, VMChangeStateHandler *cb, void *opaque); void qemu_del_vm_change_state_handler(VMChangeStateEntry *e); -void vm_state_notify(int running, RunState state); +/** + * vm_state_notify: Notify the state of the VM + * + * @running: whether the VM is running or not. + * @state: the #RunState of the VM. + */ +void vm_state_notify(bool running, RunState state); static inline bool shutdown_caused_by_guest(ShutdownCause cause) { diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 7c42f65706..ee72a1c20f 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -22,7 +22,7 @@ #include "qemu.h" #include "cpu_loop-common.h" #include "qemu/guest-random.h" -#include "hw/semihosting/common-semi.h" +#include "semihosting/common-semi.h" #include "target/arm/syndrome.h" #define get_user_code_u32(x, gaddr, env) \ diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index cadfb7fa43..989d03cd89 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -22,7 +22,7 @@ #include "qemu.h" #include "elf.h" #include "cpu_loop-common.h" -#include "hw/semihosting/common-semi.h" +#include "semihosting/common-semi.h" #define get_user_code_u32(x, gaddr, env) \ ({ abi_long __r = get_user_u32((x), (gaddr)); \ diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 9665dabb09..6767f941e8 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -23,7 +23,7 @@ #include "qemu.h" #include "cpu_loop-common.h" #include "elf.h" -#include "hw/semihosting/common-semi.h" +#include "semihosting/common-semi.h" void cpu_loop(CPURISCVState *env) { diff --git a/linux-user/semihost.c b/linux-user/semihost.c index c0015ee7f6..82013b8b48 100644 --- a/linux-user/semihost.c +++ b/linux-user/semihost.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hw/semihosting/console.h" +#include "semihosting/console.h" #include "qemu.h" #include <termios.h> diff --git a/meson.build b/meson.build index adeec153d9..a7d2dd429d 100644 --- a/meson.build +++ b/meson.build @@ -1951,6 +1951,7 @@ subdir('migration') subdir('monitor') subdir('net') subdir('replay') +subdir('semihosting') subdir('hw') subdir('accel') subdir('plugins') diff --git a/nbd/server.c b/nbd/server.c index 7229f487d2..86a44a9b41 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -2087,8 +2087,8 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, return ret; } - flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) | - (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); + flags = (ret & BDRV_BLOCK_DATA ? 0 : NBD_STATE_HOLE) | + (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); if (nbd_extent_array_add(ea, num, flags) < 0) { return 0; diff --git a/net/net.c b/net/net.c index 6002ba50db..9a2a4c99a5 100644 --- a/net/net.c +++ b/net/net.c @@ -43,6 +43,7 @@ #include "qemu/cutils.h" #include "qemu/config-file.h" #include "qemu/ctype.h" +#include "qemu/id.h" #include "qemu/iov.h" #include "qemu/qemu-print.h" #include "qemu/main-loop.h" @@ -1110,8 +1111,7 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp) /* Create an ID for -net if the user did not specify one */ if (!is_netdev && !qemu_opts_id(opts)) { - static int idx; - qemu_opts_set_id(opts, g_strdup_printf("__org.qemu.net%i", idx++)); + qemu_opts_set_id(opts, id_generate(ID_NET)); } if (visit_type_Netdev(v, NULL, &object, errp)) { @@ -1349,7 +1349,7 @@ void qmp_set_link(const char *name, bool up, Error **errp) } } -static void net_vm_change_state_handler(void *opaque, int running, +static void net_vm_change_state_handler(void *opaque, bool running, RunState state) { NetClientState *nc; @@ -1466,7 +1466,7 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) /* Create an ID if the user did not specify one */ nd_id = g_strdup(qemu_opts_id(opts)); if (!nd_id) { - nd_id = g_strdup_printf("__org.qemu.nic%i", idx); + nd_id = id_generate(ID_NET); qemu_opts_set_id(opts, nd_id); } diff --git a/pc-bios/README b/pc-bios/README index db7129ef64..c101c9a04f 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -14,7 +14,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20200717. + built from git tag qemu-slof-20210217. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index 448dcada36..3f3918a9e1 100644 --- a/pc-bios/slof.bin +++ b/pc-bios/slof.bin Binary files differdiff --git a/qemu-options.hx b/qemu-options.hx index 90801286c6..622d3bfa5a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -176,7 +176,7 @@ SRST ``thread=single|multi`` Controls number of TCG threads. When the TCG is multi-threaded - there will be one thread per vCPU therefor taking advantage of + there will be one thread per vCPU therefore taking advantage of additional host cores. The default is to enable multi-threading where both the back-end and front-ends support it and no incompatible TCG features have been enabled (e.g. @@ -2445,7 +2445,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " use 'vhostfd=h' to connect to an already opened vhost net device\n" " use 'vhostfds=x:y:...:z to connect to multiple already opened vhost net devices\n" " use 'queues=n' to specify the number of queues to be created for multiqueue TAP\n" - " use 'poll-us=n' to speciy the maximum number of microseconds that could be\n" + " use 'poll-us=n' to specify the maximum number of microseconds that could be\n" " spent on busy polling for vhost net\n" "-netdev bridge,id=str[,br=bridge][,helper=helper]\n" " configure a host TAP network backend with ID 'str' that is\n" @@ -4299,12 +4299,12 @@ DEF("sandbox", HAS_ARG, QEMU_OPTION_sandbox, \ " use 'obsolete' to allow obsolete system calls that are provided\n" \ " by the kernel, but typically no longer used by modern\n" \ " C library implementations.\n" \ - " use 'elevateprivileges' to allow or deny QEMU process to elevate\n" \ - " its privileges by blacklisting all set*uid|gid system calls.\n" \ + " use 'elevateprivileges' to allow or deny the QEMU process ability\n" \ + " to elevate privileges using set*uid|gid system calls.\n" \ " The value 'children' will deny set*uid|gid system calls for\n" \ " main QEMU process but will allow forks and execves to run unprivileged\n" \ " use 'spawn' to avoid QEMU to spawn new threads or processes by\n" \ - " blacklisting *fork and execve\n" \ + " blocking *fork and execve\n" \ " use 'resourcecontrol' to disable process affinity and schedular priority\n", QEMU_ARCH_ALL) SRST diff --git a/roms/SLOF b/roms/SLOF -Subproject e18ddad8516ff2cfe36ec130200318f7251aa78 +Subproject 33a7322de13e9dca4b38851a345a58d37e7a441 diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 96b1cd69a5..5bc94d95cf 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -100,7 +100,7 @@ def validate_type(name): if bit == "const": continue if bit not in ALLOWED_TYPES: - raise ValueError("Argument type '%s' is not in whitelist. " + raise ValueError("Argument type '%s' is not allowed. " "Only standard C types and fixed size integer " "types should be used. struct, union, and " "other complex pointer types should be " diff --git a/hw/semihosting/Kconfig b/semihosting/Kconfig index eaf3a20ef5..eaf3a20ef5 100644 --- a/hw/semihosting/Kconfig +++ b/semihosting/Kconfig diff --git a/hw/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 23c6e3edcb..94950b6c56 100644 --- a/hw/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -34,9 +34,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hw/semihosting/semihost.h" -#include "hw/semihosting/console.h" -#include "hw/semihosting/common-semi.h" +#include "semihosting/semihost.h" +#include "semihosting/console.h" +#include "semihosting/common-semi.h" #include "qemu/log.h" #include "qemu/timer.h" #ifdef CONFIG_USER_ONLY diff --git a/hw/semihosting/common-semi.h b/semihosting/common-semi.h index 0bfab1c669..0bfab1c669 100644 --- a/hw/semihosting/common-semi.h +++ b/semihosting/common-semi.h diff --git a/hw/semihosting/config.c b/semihosting/config.c index 9807f10cb0..3548e0f627 100644 --- a/hw/semihosting/config.c +++ b/semihosting/config.c @@ -22,7 +22,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/error-report.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "chardev/char.h" #include "sysemu/sysemu.h" diff --git a/hw/semihosting/console.c b/semihosting/console.c index 9b4fee9260..c9ebd6fdd0 100644 --- a/hw/semihosting/console.c +++ b/semihosting/console.c @@ -17,8 +17,8 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hw/semihosting/semihost.h" -#include "hw/semihosting/console.h" +#include "semihosting/semihost.h" +#include "semihosting/console.h" #include "exec/gdbstub.h" #include "exec/exec-all.h" #include "qemu/log.h" diff --git a/hw/semihosting/meson.build b/semihosting/meson.build index ea8090abe3..ea8090abe3 100644 --- a/hw/semihosting/meson.build +++ b/semihosting/meson.build diff --git a/softmmu/device_tree.c b/softmmu/device_tree.c index b9a3ddc518..2691c58cf6 100644 --- a/softmmu/device_tree.c +++ b/softmmu/device_tree.c @@ -21,6 +21,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/bswap.h" +#include "qemu/cutils.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" #include "hw/loader.h" @@ -397,6 +398,31 @@ int qemu_fdt_setprop_string(void *fdt, const char *node_path, return r; } +/* + * libfdt doesn't allow us to add string arrays directly but they are + * test a series of null terminated strings with a length. We build + * the string up here so we can calculate the final length. + */ +int qemu_fdt_setprop_string_array(void *fdt, const char *node_path, + const char *prop, char **array, int len) +{ + int ret, i, total_len = 0; + char *str, *p; + for (i = 0; i < len; i++) { + total_len += strlen(array[i]) + 1; + } + p = str = g_malloc0(total_len); + for (i = 0; i < len; i++) { + int len = strlen(array[i]) + 1; + pstrcpy(p, len, array[i]); + p += len; + } + + ret = qemu_fdt_setprop(fdt, node_path, prop, str, total_len); + g_free(str); + return ret; +} + const void *qemu_fdt_getprop(void *fdt, const char *node_path, const char *property, int *lenp, Error **errp) { diff --git a/softmmu/memory.c b/softmmu/memory.c index 874a8fccde..9db47b7db6 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1581,7 +1581,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, #ifdef CONFIG_POSIX void memory_region_init_ram_from_file(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, uint64_t align, @@ -1607,7 +1607,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, } void memory_region_init_ram_from_fd(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, bool share, @@ -1679,7 +1679,7 @@ void memory_region_init_alias(MemoryRegion *mr, } void memory_region_init_rom_nomigrate(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, Error **errp) @@ -2679,7 +2679,7 @@ static void memory_global_dirty_log_do_stop(void) MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse); } -static void memory_vm_change_state_handler(void *opaque, int running, +static void memory_vm_change_state_handler(void *opaque, bool running, RunState state) { if (running) { @@ -3205,7 +3205,7 @@ void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled) } void memory_region_init_ram(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, Error **errp) @@ -3229,7 +3229,7 @@ void memory_region_init_ram(MemoryRegion *mr, } void memory_region_init_rom(MemoryRegion *mr, - struct Object *owner, + Object *owner, const char *name, uint64_t size, Error **errp) @@ -3253,7 +3253,7 @@ void memory_region_init_rom(MemoryRegion *mr, } void memory_region_init_rom_device(MemoryRegion *mr, - struct Object *owner, + Object *owner, const MemoryRegionOps *ops, void *opaque, const char *name, diff --git a/softmmu/qemu-seccomp.c b/softmmu/qemu-seccomp.c index 377ef6937c..9c29d9cf00 100644 --- a/softmmu/qemu-seccomp.c +++ b/softmmu/qemu-seccomp.c @@ -45,8 +45,8 @@ const struct scmp_arg_cmp sched_setscheduler_arg[] = { { .arg = 1, .op = SCMP_CMP_NE, .datum_a = SCHED_IDLE } }; -static const struct QemuSeccompSyscall blacklist[] = { - /* default set of syscalls to blacklist */ +static const struct QemuSeccompSyscall denylist[] = { + /* default set of syscalls that should get blocked */ { SCMP_SYS(reboot), QEMU_SECCOMP_SET_DEFAULT }, { SCMP_SYS(swapon), QEMU_SECCOMP_SET_DEFAULT }, { SCMP_SYS(swapoff), QEMU_SECCOMP_SET_DEFAULT }, @@ -175,18 +175,18 @@ static int seccomp_start(uint32_t seccomp_opts, Error **errp) goto seccomp_return; } - for (i = 0; i < ARRAY_SIZE(blacklist); i++) { + for (i = 0; i < ARRAY_SIZE(denylist); i++) { uint32_t action; - if (!(seccomp_opts & blacklist[i].set)) { + if (!(seccomp_opts & denylist[i].set)) { continue; } - action = qemu_seccomp_get_action(blacklist[i].set); - rc = seccomp_rule_add_array(ctx, action, blacklist[i].num, - blacklist[i].narg, blacklist[i].arg_cmp); + action = qemu_seccomp_get_action(denylist[i].set); + rc = seccomp_rule_add_array(ctx, action, denylist[i].num, + denylist[i].narg, denylist[i].arg_cmp); if (rc < 0) { error_setg_errno(errp, -rc, - "failed to add seccomp blacklist rules"); + "failed to add seccomp denylist rules"); goto seccomp_return; } } diff --git a/softmmu/runstate.c b/softmmu/runstate.c index 2874417b61..ce8977c6a2 100644 --- a/softmmu/runstate.c +++ b/softmmu/runstate.c @@ -218,7 +218,7 @@ void runstate_set(RunState new_state) current_run_state = new_state; } -int runstate_is_running(void) +bool runstate_is_running(void) { return runstate_check(RUN_STATE_RUNNING); } @@ -317,7 +317,7 @@ void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) g_free(e); } -void vm_state_notify(int running, RunState state) +void vm_state_notify(bool running, RunState state) { VMChangeStateEntry *e, *next; diff --git a/softmmu/vl.c b/softmmu/vl.c index ff488ea3e7..b7673b9613 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -108,7 +108,7 @@ #include "qapi/opts-visitor.h" #include "qapi/clone-visitor.h" #include "qom/object_interfaces.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "crypto/init.h" #include "sysemu/replay.h" #include "qapi/qapi-events-run-state.h" diff --git a/stubs/semihost.c b/stubs/semihost.c index 1d8b37f7b2..1b30f38b03 100644 --- a/stubs/semihost.c +++ b/stubs/semihost.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/option.h" #include "qemu/error-report.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "sysemu/sysemu.h" /* Empty config */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 904b0927cd..d9220be7c5 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -22,7 +22,7 @@ #include "exec/exec-all.h" #include <zlib.h> /* For crc32 */ #include "hw/irq.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "sysemu/cpus.h" #include "sysemu/cpu-timers.h" #include "sysemu/kvm.h" @@ -34,7 +34,7 @@ #ifdef CONFIG_TCG #include "arm_ldst.h" #include "exec/cpu_ldst.h" -#include "hw/semihosting/common-semi.h" +#include "semihosting/common-semi.h" #endif #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 00e124c812..bebea90122 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -844,7 +844,7 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) return MEMTXATTRS_UNSPECIFIED; } -void kvm_arm_vm_state_change(void *opaque, int running, RunState state) +void kvm_arm_vm_state_change(void *opaque, bool running, RunState state) { CPUState *cs = opaque; ARMCPU *cpu = ARM_CPU(cs); diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index eb81b7059e..68ec970c4f 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -352,7 +352,7 @@ void kvm_arm_get_virtual_time(CPUState *cs); */ void kvm_arm_put_virtual_time(CPUState *cs); -void kvm_arm_vm_state_change(void *opaque, int running, RunState state); +void kvm_arm_vm_state_change(void *opaque, bool running, RunState state); int kvm_arm_vgic_probe(void); diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index 731c435c00..d63ae465e1 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -21,7 +21,7 @@ #include "qemu/qemu-print.h" #include "exec/exec-all.h" #include <zlib.h> /* For crc32 */ -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "sysemu/cpus.h" #include "sysemu/kvm.h" #include "qemu/range.h" @@ -31,7 +31,7 @@ #ifdef CONFIG_TCG #include "arm_ldst.h" #include "exec/cpu_ldst.h" -#include "hw/semihosting/common-semi.h" +#include "semihosting/common-semi.h" #endif static void v7m_msr_xpsr(CPUARMState *env, uint32_t mask, diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index b591f096df..0b42e53500 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -28,7 +28,7 @@ #include "internals.h" #include "qemu/host-utils.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "exec/gen-icount.h" #include "exec/helper-proto.h" diff --git a/target/arm/translate.c b/target/arm/translate.c index 1653cca1aa..62b1c2081b 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -29,7 +29,7 @@ #include "qemu/log.h" #include "qemu/bitops.h" #include "arm_ldst.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index fe4d8e5730..db9f663a77 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -35,7 +35,7 @@ def gen_decl_ea_tcg(f, tag): def gen_free_ea_tcg(f): f.write(" tcg_temp_free(EA);\n") -def genptr_decl_pair_writeble(f, tag, regtype, regid, regno): +def genptr_decl_pair_writable(f, tag, regtype, regid, regno): regN="%s%sN" % (regtype,regid) f.write(" TCGv_i64 %s%sV = tcg_temp_local_new_i64();\n" % \ (regtype, regid)) @@ -54,7 +54,7 @@ def genptr_decl_pair_writeble(f, tag, regtype, regid, regno): (regN, regN)) f.write(" }\n") -def genptr_decl_writeble(f, tag, regtype, regid, regno): +def genptr_decl_writable(f, tag, regtype, regid, regno): regN="%s%sN" % (regtype,regid) f.write(" TCGv %s%sV = tcg_temp_local_new();\n" % \ (regtype, regid)) @@ -78,12 +78,12 @@ def genptr_decl(f, tag, regtype, regid, regno): f.write(" const int %s = insn->regno[%d];\n" % \ (regN, regno)) elif (regid in {"dd", "ee", "xx", "yy"}): - genptr_decl_pair_writeble(f, tag, regtype, regid, regno) + genptr_decl_pair_writable(f, tag, regtype, regid, regno) elif (regid in {"s", "t", "u", "v"}): f.write(" TCGv %s%sV = hex_gpr[insn->regno[%d]];\n" % \ (regtype, regid, regno)) elif (regid in {"d", "e", "x", "y"}): - genptr_decl_writeble(f, tag, regtype, regid, regno) + genptr_decl_writable(f, tag, regtype, regid, regno) else: print("Bad register parse: ", regtype, regid) elif (regtype == "P"): @@ -91,7 +91,7 @@ def genptr_decl(f, tag, regtype, regid, regno): f.write(" TCGv %s%sV = hex_pred[insn->regno[%d]];\n" % \ (regtype, regid, regno)) elif (regid in {"d", "e", "x"}): - genptr_decl_writeble(f, tag, regtype, regid, regno) + genptr_decl_writable(f, tag, regtype, regid, regno) else: print("Bad register parse: ", regtype, regid) elif (regtype == "C"): @@ -101,14 +101,14 @@ def genptr_decl(f, tag, regtype, regid, regno): f.write(" const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \ (regN, regno)) elif (regid == "dd"): - genptr_decl_pair_writeble(f, tag, regtype, regid, regno) + genptr_decl_pair_writable(f, tag, regtype, regid, regno) elif (regid == "s"): f.write(" TCGv %s%sV = tcg_temp_local_new();\n" % \ (regtype, regid)) f.write(" const int %s%sN = insn->regno[%d] + HEX_REG_SA0;\n" % \ (regtype, regid, regno)) elif (regid == "d"): - genptr_decl_writeble(f, tag, regtype, regid, regno) + genptr_decl_writable(f, tag, regtype, regid, regno) else: print("Bad register parse: ", regtype, regid) elif (regtype == "M"): diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 50008431c3..ae9fd9f31d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7081,7 +7081,7 @@ static void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, GuestPanicInformation *panic_info; if (!cs->crash_occurred) { - error_setg(errp, "No crash occured"); + error_setg(errp, "No crash occurred"); return; } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index c8d61daf68..7fe9f52710 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -693,7 +693,7 @@ static int kvm_inject_mce_oldstyle(X86CPU *cpu) return 0; } -static void cpu_update_state(void *opaque, int running, RunState state) +static void cpu_update_state(void *opaque, bool running, RunState state) { CPUX86State *env = opaque; diff --git a/target/i386/machine.c b/target/i386/machine.c index 3768a753af..3967dfc257 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1173,7 +1173,7 @@ static int nested_state_post_load(void *opaque, int version_id) return -EINVAL; } if (nested_state->size > max_nested_state_len) { - error_report("Recieved unsupported nested state size: " + error_report("Received unsupported nested state size: " "nested_state->size=%d, max=%d", nested_state->size, max_nested_state_len); return -EINVAL; diff --git a/target/i386/sev.c b/target/i386/sev.c index 0f414df02f..72b9e2ab40 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -691,7 +691,7 @@ sev_launch_finish(SevGuestState *sev) } static void -sev_vm_state_change(void *opaque, int running, RunState state) +sev_vm_state_change(void *opaque, bool running, RunState state) { SevGuestState *sev = opaque; diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index f0a35df3bb..f832f286ac 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1318,7 +1318,7 @@ void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) static Error *whpx_migration_blocker; -static void whpx_cpu_update_state(void *opaque, int running, RunState state) +static void whpx_cpu_update_state(void *opaque, bool running, RunState state) { CPUX86State *env = opaque; diff --git a/target/lm32/helper.c b/target/lm32/helper.c index 7c52ae76d6..01cc3c53a5 100644 --- a/target/lm32/helper.c +++ b/target/lm32/helper.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "qemu/host-utils.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "exec/log.h" bool lm32_cpu_tlb_fill(CPUState *cs, vaddr address, int size, diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index 202498deb5..9b0698f267 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -21,7 +21,7 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #if defined(CONFIG_USER_ONLY) @@ -117,7 +117,7 @@ static const char *m68k_exception_name(int index) case EXCP_FORMAT: return "Format Error"; case EXCP_UNINITIALIZED: - return "Unitialized Interruot"; + return "Uninitialized Interrupt"; case EXCP_SPURIOUS: return "Spurious Interrupt"; case EXCP_INT_LEVEL_1: diff --git a/target/mips/cpu.c b/target/mips/cpu.c index f6ef09c9e2..dce1e166bd 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -31,7 +31,7 @@ #include "exec/exec-all.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "qapi/qapi-commands-machine-target.h" #include "fpu_helper.h" diff --git a/target/mips/kvm.c b/target/mips/kvm.c index 123ec1be7e..086debd9f0 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -38,7 +38,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; -static void kvm_mips_update_state(void *opaque, int running, RunState state); +static void kvm_mips_update_state(void *opaque, bool running, RunState state); unsigned long kvm_arch_vcpu_id(CPUState *cs) { @@ -553,7 +553,7 @@ static int kvm_mips_restore_count(CPUState *cs) /* * Handle the VM clock being started or stopped */ -static void kvm_mips_update_state(void *opaque, int running, RunState state) +static void kvm_mips_update_state(void *opaque, bool running, RunState state) { CPUState *cs = opaque; int ret; diff --git a/target/mips/mips-semi.c b/target/mips/mips-semi.c index 898251aa02..6de60fa6dd 100644 --- a/target/mips/mips-semi.c +++ b/target/mips/mips-semi.c @@ -22,8 +22,8 @@ #include "qemu/log.h" #include "exec/helper-proto.h" #include "exec/softmmu-semi.h" -#include "hw/semihosting/semihost.h" -#include "hw/semihosting/console.h" +#include "semihosting/semihost.h" +#include "semihosting/console.h" typedef enum UHIOp { UHI_exit = 1, diff --git a/target/mips/translate.c b/target/mips/translate.c index 70891c37cd..0b6d82d228 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -29,7 +29,7 @@ #include "exec/translator.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "target/mips/trace.h" #include "trace-tcg.h" diff --git a/target/nios2/helper.c b/target/nios2/helper.c index 57c97bde3c..53be8398e9 100644 --- a/target/nios2/helper.c +++ b/target/nios2/helper.c @@ -26,7 +26,7 @@ #include "exec/cpu_ldst.h" #include "exec/log.h" #include "exec/helper-proto.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #if defined(CONFIG_USER_ONLY) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 63b9e8632c..118baf8d41 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -218,7 +218,7 @@ extern const VMStateDescription vmstate_ppc_timebase; .offset = vmstate_offset_value(_state, _field, PPCTimebase), \ } -void cpu_ppc_clock_vm_state_change(void *opaque, int running, +void cpu_ppc_clock_vm_state_change(void *opaque, bool running, RunState state); #endif diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 0b682a1f94..429de28494 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -2175,14 +2175,17 @@ static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b) return 0; } -static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, +static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, int *overflow) { int carry = 0; int i; + int is_zero = 1; + for (i = 1; i <= 31; i++) { uint8_t digit = bcd_get_digit(a, i, invalid) + bcd_get_digit(b, i, invalid) + carry; + is_zero &= (digit == 0); if (digit > 9) { carry = 1; digit -= 10; @@ -2194,6 +2197,7 @@ static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, } *overflow = carry; + return is_zero; } static void bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, @@ -2225,14 +2229,15 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) int sgnb = bcd_get_sgn(b); int invalid = (sgna == 0) || (sgnb == 0); int overflow = 0; + int zero = 0; uint32_t cr = 0; ppc_avr_t result = { .u64 = { 0, 0 } }; if (!invalid) { if (sgna == sgnb) { result.VsrB(BCD_DIG_BYTE(0)) = bcd_preferred_sgn(sgna, ps); - bcd_add_mag(&result, a, b, &invalid, &overflow); - cr = bcd_cmp_zero(&result); + zero = bcd_add_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? CRF_GT : CRF_LT; } else { int magnitude = bcd_cmp_mag(a, b); if (magnitude > 0) { @@ -2255,6 +2260,8 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) cr = CRF_SO; } else if (overflow) { cr |= CRF_SO; + } else if (zero) { + cr |= CRF_EQ; } *r = result; diff --git a/target/ppc/translate_init.c.inc b/target/ppc/translate_init.c.inc index 108ff2be2b..c03a7c4f52 100644 --- a/target/ppc/translate_init.c.inc +++ b/target/ppc/translate_init.c.inc @@ -566,35 +566,71 @@ static void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) static void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_store_spr(sprn, cpu_gpr[gprn]); gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ gen_stop_exception(ctx); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } static void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_stop_exception(ctx); + } } #endif diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ddea8fbeeb..2a990f6253 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -506,7 +506,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } } else { - qemu_log("vector verison is not specified, " + qemu_log("vector version is not specified, " "use the default value v0.7.1\n"); } set_vext_version(env, vext_version); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 2f43939fb6..83a6bcfad0 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -24,7 +24,7 @@ #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "trace.h" -#include "hw/semihosting/common-semi.h" +#include "semihosting/common-semi.h" int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) { diff --git a/target/unicore32/helper.c b/target/unicore32/helper.c index 54c26871fe..704393c27f 100644 --- a/target/unicore32/helper.c +++ b/target/unicore32/helper.c @@ -14,7 +14,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "hw/semihosting/console.h" +#include "semihosting/console.h" #undef DEBUG_UC32 diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 944a157747..0ae4efc48a 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -37,7 +37,7 @@ #include "qemu/log.h" #include "qemu/qemu-print.h" #include "exec/cpu_ldst.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "exec/translator.h" #include "exec/helper-proto.h" diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index 25f57a6500..79f2b043f2 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -29,7 +29,7 @@ #include "cpu.h" #include "chardev/char-fe.h" #include "exec/helper-proto.h" -#include "hw/semihosting/semihost.h" +#include "semihosting/semihost.h" #include "qapi/error.h" #include "qemu/log.h" diff --git a/tests/acceptance/boot_xen.py b/tests/acceptance/boot_xen.py new file mode 100644 index 0000000000..75c2d44492 --- /dev/null +++ b/tests/acceptance/boot_xen.py @@ -0,0 +1,118 @@ +# Functional test that boots a Xen hypervisor with a domU kernel and +# checks the console output is vaguely sane . +# +# Copyright (c) 2020 Linaro +# +# Author: +# Alex BennĂ©e <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from avocado import skipIf +from avocado_qemu import wait_for_console_pattern +from boot_linux_console import LinuxKernelTest + + +class BootXenBase(LinuxKernelTest): + """ + Boots a Xen hypervisor with a Linux DomU kernel. + """ + + timeout = 90 + XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' + + def fetch_guest_kernel(self): + # Using my own built kernel - which works + kernel_url = ('https://fileserver.linaro.org/' + 's/JSsewXGZ6mqxPr5/download?path=%2F&files=' + 'linux-5.9.9-arm64-ajb') + kernel_sha1 = '4f92bc4b9f88d5ab792fa7a43a68555d344e1b83' + kernel_path = self.fetch_asset(kernel_url, + asset_hash=kernel_sha1) + + return kernel_path + + def launch_xen(self, xen_path): + """ + Launch Xen with a dom0 guest kernel + """ + self.log.info("launch with xen_path: %s", xen_path) + kernel_path = self.fetch_guest_kernel() + + self.vm.set_console() + + xen_command_line = self.XEN_COMMON_COMMAND_LINE + self.vm.add_args('-machine', 'virtualization=on', + '-cpu', 'cortex-a57', + '-m', '768', + '-kernel', xen_path, + '-append', xen_command_line, + '-device', + 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' + % (kernel_path)) + + self.vm.launch() + + console_pattern = 'VFS: Cannot open root device' + wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") + + +class BootXen(BootXenBase): + + def test_arm64_xen_411_and_dom0(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=accel:tcg + :avocado: tags=cpu:cortex-a57 + :avocado: tags=machine:virt + """ + + # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ + xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' + 'download?path=%2F&files=' + 'xen-hypervisor-4.11-arm64_4.11.4%2B37-g3263f257ca-1_arm64.deb') + xen_sha1 = '034e634d4416adbad1212d59b62bccdcda63e62a' + xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) + xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.11-arm64") + + self.launch_xen(xen_path) + + def test_arm64_xen_414_and_dom0(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=accel:tcg + :avocado: tags=cpu:cortex-a57 + :avocado: tags=machine:virt + """ + + # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ + xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' + 'download?path=%2F&files=' + 'xen-hypervisor-4.14-arm64_4.14.0%2B80-gd101b417b7-1_arm64.deb') + xen_sha1 = 'b9d209dd689ed2b393e625303a225badefec1160' + xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) + xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.14-arm64") + + self.launch_xen(xen_path) + + def test_arm64_xen_415_and_dom0(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=accel:tcg + :avocado: tags=cpu:cortex-a57 + :avocado: tags=machine:virt + """ + + xen_url = ('https://fileserver.linaro.org/' + 's/JSsewXGZ6mqxPr5/download' + '?path=%2F&files=xen-upstream-4.15-unstable.deb') + xen_sha1 = 'fc191172b85cf355abb95d275a24cc0f6d6579d8' + xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) + xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.15-unstable") + + self.launch_xen(xen_path) diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker index 9d42b5a4b8..d034acbd25 100644 --- a/tests/docker/dockerfiles/debian10.docker +++ b/tests/docker/dockerfiles/debian10.docker @@ -32,6 +32,6 @@ RUN apt update && \ psmisc \ python3 \ python3-sphinx \ - $(apt-get -s build-dep qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2) + $(apt-get -s build-dep --arch-only qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2) ENV FEATURES docs diff --git a/tests/docker/test-tcg b/tests/docker/test-tcg new file mode 100755 index 0000000000..00993b73ba --- /dev/null +++ b/tests/docker/test-tcg @@ -0,0 +1,22 @@ +#!/bin/bash -e +# +# Build and run the TCG tests +# +# Copyright (c) 2021 Linaro Ltd. +# +# Authors: +# Alex BennĂ©e <alex.bennee@linaro.org> +# +# This work is licensed under the terms of the GNU GPL, version 2 +# or (at your option) any later version. See the COPYING file in +# the top-level directory. + +. common.rc + +cd "$BUILD_DIR" + +# although we are not building QEMU itself we still need a configured +# build for the unit tests to be built and run +TARGET_LIST=${TARGET_LIST:-$DEF_TARGET_LIST} \ +build_qemu +check_qemu check-tcg diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 06ffebd6db..5a4cad8c8b 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -123,7 +123,7 @@ static void not_implemented(void) fprintf(stderr, "Not implemented.\n"); } -static bool blacklisted(unsigned op, int rmode) +static bool is_allowed(unsigned op, int rmode) { /* odd has not been implemented for any 80-bit ops */ if (rmode == softfloat_round_odd) { @@ -161,10 +161,10 @@ static bool blacklisted(unsigned op, int rmode) case F32_TO_EXTF80: case F64_TO_EXTF80: case F128_TO_EXTF80: - return true; + return false; } } - return false; + return true; } static void do_testfloat(int op, int rmode, bool exact) @@ -194,7 +194,7 @@ static void do_testfloat(int op, int rmode, bool exact) verCases_writeFunctionName(stderr); fputs("\n", stderr); - if (blacklisted(op, rmode)) { + if (!is_allowed(op, rmode)) { not_implemented(); return; } diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index b1d8fd9107..01f7b1f240 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -92,16 +92,22 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp == 3. Invalid sizes == qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024 -qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. +qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for +qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 -qemu-img: TEST_DIR/t.qcow2: Value '-1024' is out of range for parameter 'size' +qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 +Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- +and exabytes, respectively. qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k -qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. +qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for +qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 -qemu-img: TEST_DIR/t.qcow2: Value '-1k' is out of range for parameter 'size' +qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 +Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- +and exabytes, respectively. qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 index fe193fd5f4..0d51fe401e 100644 --- a/tests/qemu-iotests/178.out.qcow2 +++ b/tests/qemu-iotests/178.out.qcow2 @@ -13,7 +13,8 @@ qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' qemu-img: --output must be used with human or json as argument. -qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. +qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for +qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img: Unknown file format 'foo' == Size calculation for a new file (human) == diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw index 445e460fad..116241ddef 100644 --- a/tests/qemu-iotests/178.out.raw +++ b/tests/qemu-iotests/178.out.raw @@ -13,7 +13,8 @@ qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' qemu-img: --output must be used with human or json as argument. -qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. +qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for +qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img: Unknown file format 'foo' == Size calculation for a new file (human) == diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out index 75f9f465e5..3f8c173cc8 100644 --- a/tests/qemu-iotests/241.out +++ b/tests/qemu-iotests/241.out @@ -5,7 +5,7 @@ QA output created by 241 size: 1024 min block: 1 [{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}] +{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) === Exporting unaligned raw image, forced server sector alignment === @@ -23,6 +23,6 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed size: 1024 min block: 1 [{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}] +{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) *** done diff --git a/tests/qtest/fuzz-test.c b/tests/qtest/fuzz-test.c index cdb1100a0b..6f161c93be 100644 --- a/tests/qtest/fuzz-test.c +++ b/tests/qtest/fuzz-test.c @@ -39,8 +39,7 @@ static void test_lp1878642_pci_bus_get_irq_level_assert(void) QTestState *s; s = qtest_init("-M pc-q35-5.0 " - "-nographic -monitor none -serial none " - "-d guest_errors -trace pci*"); + "-nographic -monitor none -serial none"); qtest_outl(s, 0xcf8, 0x8400f841); qtest_outl(s, 0xcfc, 0xebed205d); diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index 36b8a73a54..ce304f4933 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -251,6 +251,12 @@ for target in $target_list; do echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak fi ;; + ppc*) + if do_compiler "$target_compiler" $target_compiler_cflags \ + -mpower8-vector -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak + fi + ;; esac enabled_cross_compilers="$enabled_cross_compilers $target_compiler" diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target new file mode 100644 index 0000000000..0c6a4585fc --- /dev/null +++ b/tests/tcg/ppc64/Makefile.target @@ -0,0 +1,13 @@ +# -*- Mode: makefile -*- +# +# ppc64 specific tweaks + +VPATH += $(SRC_PATH)/tests/tcg/ppc64 +VPATH += $(SRC_PATH)/tests/tcg/ppc64le + +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),) +PPC64_TESTS=bcdsub +endif +bcdsub: CFLAGS += -mpower8-vector + +TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/ppc64le/Makefile.target b/tests/tcg/ppc64le/Makefile.target new file mode 100644 index 0000000000..1acfcff94a --- /dev/null +++ b/tests/tcg/ppc64le/Makefile.target @@ -0,0 +1,12 @@ +# -*- Mode: makefile -*- +# +# ppc64le specific tweaks + +VPATH += $(SRC_PATH)/tests/tcg/ppc64le + +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),) +PPC64LE_TESTS=bcdsub +endif +bcdsub: CFLAGS += -mpower8-vector + +TESTS += $(PPC64LE_TESTS) diff --git a/tests/tcg/ppc64le/bcdsub.c b/tests/tcg/ppc64le/bcdsub.c new file mode 100644 index 0000000000..8c188cae6d --- /dev/null +++ b/tests/tcg/ppc64le/bcdsub.c @@ -0,0 +1,130 @@ +#include <assert.h> +#include <unistd.h> +#include <signal.h> + +#define CRF_LT (1 << 3) +#define CRF_GT (1 << 2) +#define CRF_EQ (1 << 1) +#define CRF_SO (1 << 0) +#define UNDEF 0 + +#define BCDSUB(vra, vrb, ps) \ + asm ("bcdsub. %1,%2,%3,%4;" \ + "mfocrf %0,0b10;" \ + : "=r" (cr), "=v" (vrt) \ + : "v" (vra), "v" (vrb), "i" (ps) \ + : ); + +#define TEST(vra, vrb, ps, exp_res, exp_cr6) \ + do { \ + __int128 vrt = 0; \ + int cr = 0; \ + BCDSUB(vra, vrb, ps); \ + if (exp_res) \ + assert(vrt == exp_res); \ + assert((cr >> 4) == exp_cr6); \ + } while (0) + + +/* + * Unbounded result is equal to zero: + * sign = (PS) ? 0b1111 : 0b1100 + * CR6 = 0b0010 + */ +void test_bcdsub_eq(void) +{ + __int128 a, b; + + /* maximum positive BCD value */ + a = b = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c); + + TEST(a, b, 0, 0xc, CRF_EQ); + TEST(a, b, 1, 0xf, CRF_EQ); +} + +/* + * Unbounded result is greater than zero: + * sign = (PS) ? 0b1111 : 0b1100 + * CR6 = (overflow) ? 0b0101 : 0b0100 + */ +void test_bcdsub_gt(void) +{ + __int128 a, b, c; + + /* maximum positive BCD value */ + a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c); + + /* negative one BCD value */ + b = (__int128) 0x1d; + + TEST(a, b, 0, 0xc, (CRF_GT | CRF_SO)); + TEST(a, b, 1, 0xf, (CRF_GT | CRF_SO)); + + c = (((__int128) 0x9999999999999999) << 64 | 0x999999999999998c); + + TEST(c, b, 0, a, CRF_GT); + TEST(c, b, 1, (a | 0x3), CRF_GT); +} + +/* + * Unbounded result is less than zero: + * sign = 0b1101 + * CR6 = (overflow) ? 0b1001 : 0b1000 + */ +void test_bcdsub_lt(void) +{ + __int128 a, b; + + /* positive zero BCD value */ + a = (__int128) 0xc; + + /* positive one BCD value */ + b = (__int128) 0x1c; + + TEST(a, b, 0, 0x1d, CRF_LT); + TEST(a, b, 1, 0x1d, CRF_LT); + + /* maximum negative BCD value */ + a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999d); + + /* positive one BCD value */ + b = (__int128) 0x1c; + + TEST(a, b, 0, 0xd, (CRF_LT | CRF_SO)); + TEST(a, b, 1, 0xd, (CRF_LT | CRF_SO)); +} + +void test_bcdsub_invalid(void) +{ + __int128 a, b; + + /* positive one BCD value */ + a = (__int128) 0x1c; + b = 0xf00; + + TEST(a, b, 0, UNDEF, CRF_SO); + TEST(a, b, 1, UNDEF, CRF_SO); + + TEST(b, a, 0, UNDEF, CRF_SO); + TEST(b, a, 1, UNDEF, CRF_SO); + + a = 0xbad; + + TEST(a, b, 0, UNDEF, CRF_SO); + TEST(a, b, 1, UNDEF, CRF_SO); +} + +int main(void) +{ + struct sigaction action; + + action.sa_handler = _exit; + sigaction(SIGABRT, &action, NULL); + + test_bcdsub_eq(); + test_bcdsub_gt(); + test_bcdsub_lt(); + test_bcdsub_invalid(); + + return 0; +} diff --git a/tests/test-cutils.c b/tests/test-cutils.c index 1aa8351520..bad3a60993 100644 --- a/tests/test-cutils.c +++ b/tests/test-cutils.c @@ -1960,18 +1960,24 @@ static void test_qemu_strtosz_simple(void) g_assert_cmpint(res, ==, 0); g_assert(endptr == str + 1); - str = "12345"; + /* Leading 0 gives decimal results, not octal */ + str = "08"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 8); + g_assert(endptr == str + 2); + + /* Leading space is ignored */ + str = " 12345"; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert(endptr == str + 6); err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - /* Note: precision is 53 bits since we're parsing with strtod() */ - str = "9007199254740991"; /* 2^53-1 */ err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); @@ -1987,7 +1993,7 @@ static void test_qemu_strtosz_simple(void) str = "9007199254740993"; /* 2^53+1 */ err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000000); /* rounded to 53 bits */ + g_assert_cmpint(res, ==, 0x20000000000001); g_assert(endptr == str + 16); str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ @@ -1999,11 +2005,40 @@ static void test_qemu_strtosz_simple(void) str = "18446744073709550591"; /* 0xfffffffffffffbff */ err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffff800); /* rounded to 53 bits */ + g_assert_cmpint(res, ==, 0xfffffffffffffbff); g_assert(endptr == str + 20); - /* 0x7ffffffffffffe00..0x7fffffffffffffff get rounded to - * 0x8000000000000000, thus -ERANGE; see test_qemu_strtosz_erange() */ + str = "18446744073709551615"; /* 0xffffffffffffffff */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0xffffffffffffffff); + g_assert(endptr == str + 20); +} + +static void test_qemu_strtosz_hex(void) +{ + const char *str; + const char *endptr; + int err; + uint64_t res = 0xbaadf00d; + + str = "0x0"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert(endptr == str + 3); + + str = "0xab"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 171); + g_assert(endptr == str + 4); + + str = "0xae"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 174); + g_assert(endptr == str + 4); } static void test_qemu_strtosz_units(void) @@ -2064,14 +2099,36 @@ static void test_qemu_strtosz_units(void) static void test_qemu_strtosz_float(void) { - const char *str = "12.345M"; + const char *str; int err; const char *endptr; uint64_t res = 0xbaadf00d; + str = "0.5E"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, EiB / 2); + g_assert(endptr == str + 4); + + /* For convenience, a fraction of 0 is tolerated even on bytes */ + str = "1.0B"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 1); + g_assert(endptr == str + 4); + + /* An empty fraction is tolerated */ + str = "1.k"; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12.345 * MiB); + g_assert_cmpint(res, ==, 1024); + g_assert(endptr == str + 3); + + /* For convenience, we permit values that are not byte-exact */ + str = "12.345M"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB)); g_assert(endptr == str + 7); } @@ -2106,6 +2163,45 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert(endptr == str); + + /* Fractional values require scale larger than bytes */ + str = "1.1B"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = "1.1"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + /* No floating point exponents */ + str = "1.5e1k"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = "1.5E+0k"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + /* No hex fractions */ + str = "0x1.8k"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + /* No negative values */ + str = "-0"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = "-1"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } static void test_qemu_strtosz_trailing(void) @@ -2131,6 +2227,30 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); + + str = "0x"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(res, ==, 0); + g_assert(endptr == str + 1); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + + str = "0.NaN"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert(endptr == str + 2); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + + str = "123-45"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); } static void test_qemu_strtosz_erange(void) @@ -2140,22 +2260,7 @@ static void test_qemu_strtosz_erange(void) int err; uint64_t res = 0xbaadf00d; - str = "-1"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert(endptr == str + 2); - - str = "18446744073709550592"; /* 0xfffffffffffffc00 */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert(endptr == str + 20); - - str = "18446744073709551615"; /* 2^64-1 */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert(endptr == str + 20); - - str = "18446744073709551616"; /* 2^64 */ + str = "18446744073709551616"; /* 2^64; see strtosz_simple for 2^64-1 */ err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert(endptr == str + 20); @@ -2168,15 +2273,22 @@ static void test_qemu_strtosz_erange(void) static void test_qemu_strtosz_metric(void) { - const char *str = "12345k"; + const char *str; int err; const char *endptr; uint64_t res = 0xbaadf00d; + str = "12345k"; err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345000); g_assert(endptr == str + 6); + + str = "12.345M"; + err = qemu_strtosz_metric(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345000); + g_assert(endptr == str + 7); } int main(int argc, char **argv) @@ -2443,6 +2555,8 @@ int main(int argc, char **argv) g_test_add_func("/cutils/strtosz/simple", test_qemu_strtosz_simple); + g_test_add_func("/cutils/strtosz/hex", + test_qemu_strtosz_hex); g_test_add_func("/cutils/strtosz/units", test_qemu_strtosz_units); g_test_add_func("/cutils/strtosz/float", diff --git a/tests/test-keyval.c b/tests/test-keyval.c index ee927fe4e4..e20c07cf3e 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -445,9 +445,9 @@ static void test_keyval_visit_size(void) visit_end_struct(v, NULL); visit_free(v); - /* Note: precision is 53 bits since we're parsing with strtod() */ + /* Note: full 64 bits of precision */ - /* Around limit of precision: 2^53-1, 2^53, 2^53+1 */ + /* Around double limit of precision: 2^53-1, 2^53, 2^53+1 */ qdict = keyval_parse("sz1=9007199254740991," "sz2=9007199254740992," "sz3=9007199254740993", @@ -460,22 +460,25 @@ static void test_keyval_visit_size(void) visit_type_size(v, "sz2", &sz, &error_abort); g_assert_cmphex(sz, ==, 0x20000000000000); visit_type_size(v, "sz3", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0x20000000000000); + g_assert_cmphex(sz, ==, 0x20000000000001); visit_check_struct(v, &error_abort); visit_end_struct(v, NULL); visit_free(v); - /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */ - qdict = keyval_parse("sz1=9223372036854774784," /* 7ffffffffffffc00 */ - "sz2=9223372036854775295", /* 7ffffffffffffdff */ + /* Close to signed integer limit 2^63 */ + qdict = keyval_parse("sz1=9223372036854775807," /* 7fffffffffffffff */ + "sz2=9223372036854775808," /* 8000000000000000 */ + "sz3=9223372036854775809", /* 8000000000000001 */ NULL, NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); + g_assert_cmphex(sz, ==, 0x7fffffffffffffff); visit_type_size(v, "sz2", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); + g_assert_cmphex(sz, ==, 0x8000000000000000); + visit_type_size(v, "sz3", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0x8000000000000001); visit_check_struct(v, &error_abort); visit_end_struct(v, NULL); visit_free(v); @@ -490,14 +493,26 @@ static void test_keyval_visit_size(void) visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmphex(sz, ==, 0xfffffffffffff800); visit_type_size(v, "sz2", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0xfffffffffffff800); + g_assert_cmphex(sz, ==, 0xfffffffffffffbff); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Actual limit 2^64-1*/ + qdict = keyval_parse("sz1=18446744073709551615", /* ffffffffffffffff */ + NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0xffffffffffffffff); visit_check_struct(v, &error_abort); visit_end_struct(v, NULL); visit_free(v); /* Beyond limits */ qdict = keyval_parse("sz1=-1," - "sz2=18446744073709550592", /* fffffffffffffc00 */ + "sz2=18446744073709551616", /* 2^64 */ NULL, NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); qobject_unref(qdict); diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index 8bbb17b1c7..6568e31a72 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -654,9 +654,9 @@ static void test_opts_parse_size(void) g_assert_cmpuint(opts_count(opts), ==, 1); g_assert_cmpuint(qemu_opt_get_size(opts, "size1", 1), ==, 0); - /* Note: precision is 53 bits since we're parsing with strtod() */ + /* Note: full 64 bits of precision */ - /* Around limit of precision: 2^53-1, 2^53, 2^54 */ + /* Around double limit of precision: 2^53-1, 2^53, 2^53+1 */ opts = qemu_opts_parse(&opts_list_02, "size1=9007199254740991," "size2=9007199254740992," @@ -668,18 +668,21 @@ static void test_opts_parse_size(void) g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), ==, 0x20000000000000); g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1), - ==, 0x20000000000000); + ==, 0x20000000000001); - /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */ + /* Close to signed int limit: 2^63-1, 2^63, 2^63+1 */ opts = qemu_opts_parse(&opts_list_02, - "size1=9223372036854774784," /* 7ffffffffffffc00 */ - "size2=9223372036854775295", /* 7ffffffffffffdff */ + "size1=9223372036854775807," /* 7fffffffffffffff */ + "size2=9223372036854775808," /* 8000000000000000 */ + "size3=9223372036854775809", /* 8000000000000001 */ false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmpuint(opts_count(opts), ==, 3); g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), - ==, 0x7ffffffffffffc00); + ==, 0x7fffffffffffffff); g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), - ==, 0x7ffffffffffffc00); + ==, 0x8000000000000000); + g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1), + ==, 0x8000000000000001); /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */ opts = qemu_opts_parse(&opts_list_02, @@ -690,14 +693,22 @@ static void test_opts_parse_size(void) g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), ==, 0xfffffffffffff800); g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), - ==, 0xfffffffffffff800); + ==, 0xfffffffffffffbff); + + /* Actual limit, 2^64-1 */ + opts = qemu_opts_parse(&opts_list_02, + "size1=18446744073709551615", /* ffffffffffffffff */ + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), + ==, 0xffffffffffffffff); /* Beyond limits */ opts = qemu_opts_parse(&opts_list_02, "size1=-1", false, &err); error_free_or_abort(&err); g_assert(!opts); opts = qemu_opts_parse(&opts_list_02, - "size1=18446744073709550592", /* fffffffffffffc00 */ + "size1=18446744073709551616", /* 2^64 */ false, &err); error_free_or_abort(&err); g_assert(!opts); diff --git a/ui/console.c b/ui/console.c index 171a7bf14b..c2fdf975b6 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1724,7 +1724,7 @@ bool dpy_gfx_check_format(QemuConsole *con, return false; } } else { - /* default is to whitelist native 32 bpp only */ + /* default is to allow native 32 bpp only */ if (format != qemu_default_pixman_format(32, true)) { return false; } diff --git a/ui/gtk.c b/ui/gtk.c index eb1c9658e2..1ea1253528 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -681,7 +681,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = { /** QEMU Events **/ -static void gd_change_runstate(void *opaque, int running, RunState state) +static void gd_change_runstate(void *opaque, bool running, RunState state) { GtkDisplayState *s = opaque; diff --git a/ui/spice-core.c b/ui/spice-core.c index beee932f55..cadec766fe 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -615,7 +615,7 @@ static int add_channel(void *opaque, const char *name, const char *value, return 0; } -static void vm_change_state_handler(void *opaque, int running, +static void vm_change_state_handler(void *opaque, bool running, RunState state) { if (running) { diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index f67111a366..df7dc08e9f 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -288,7 +288,7 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le goto authreject; } - /* Check username whitelist ACL */ + /* Check the username access control list */ if (vnc_auth_sasl_check_access(vs) < 0) { goto authreject; } @@ -409,7 +409,7 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l goto authreject; } - /* Check username whitelist ACL */ + /* Check the username access control list */ if (vnc_auth_sasl_check_access(vs) < 0) { goto authreject; } diff --git a/util/cutils.c b/util/cutils.c index 70c7d6efbd..d89a40a8c3 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -241,52 +241,108 @@ static int64_t suffix_mul(char suffix, int64_t unit) } /* - * Convert string to bytes, allowing either B/b for bytes, K/k for KB, - * M/m for MB, G/g for GB or T/t for TB. End pointer will be returned - * in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on - * other error. + * Convert size string to bytes. + * + * The size parsing supports the following syntaxes + * - 12345 - decimal, scale determined by @default_suffix and @unit + * - 12345{bBkKmMgGtTpPeE} - decimal, scale determined by suffix and @unit + * - 12345.678{kKmMgGtTpPeE} - decimal, scale determined by suffix, and + * fractional portion is truncated to byte + * - 0x7fEE - hexadecimal, unit determined by @default_suffix + * + * The following cause a deprecation warning, and may be removed in the future + * - 0xabc{kKmMgGtTpP} - hex with scaling suffix + * + * The following are intentionally not supported + * - octal, such as 08 + * - fractional hex, such as 0x1.8 + * - floating point exponents, such as 1e3 + * + * The end pointer will be returned in *end, if not NULL. If there is + * no fraction, the input can be decimal or hexadecimal; if there is a + * fraction, then the input must be decimal and there must be a suffix + * (possibly by @default_suffix) larger than Byte, and the fractional + * portion may suffer from precision loss or rounding. The input must + * be positive. + * + * Return -ERANGE on overflow (with *@end advanced), and -EINVAL on + * other error (with *@end left unchanged). */ static int do_strtosz(const char *nptr, const char **end, const char default_suffix, int64_t unit, uint64_t *result) { int retval; - const char *endptr; + const char *endptr, *f; unsigned char c; - int mul_required = 0; - double val, mul, integral, fraction; + bool mul_required = false, hex = false; + uint64_t val; + int64_t mul; + double fraction = 0.0; - retval = qemu_strtod_finite(nptr, &endptr, &val); + /* Parse integral portion as decimal. */ + retval = qemu_strtou64(nptr, &endptr, 10, &val); if (retval) { goto out; } - fraction = modf(val, &integral); - if (fraction != 0) { - mul_required = 1; + if (memchr(nptr, '-', endptr - nptr) != NULL) { + endptr = nptr; + retval = -EINVAL; + goto out; + } + if (val == 0 && (*endptr == 'x' || *endptr == 'X')) { + /* Input looks like hex, reparse, and insist on no fraction. */ + retval = qemu_strtou64(nptr, &endptr, 16, &val); + if (retval) { + goto out; + } + if (*endptr == '.') { + endptr = nptr; + retval = -EINVAL; + goto out; + } + hex = true; + } else if (*endptr == '.') { + /* + * Input looks like a fraction. Make sure even 1.k works + * without fractional digits. If we see an exponent, treat + * the entire input as invalid instead. + */ + f = endptr; + retval = qemu_strtod_finite(f, &endptr, &fraction); + if (retval) { + fraction = 0.0; + endptr++; + } else if (memchr(f, 'e', endptr - f) || memchr(f, 'E', endptr - f)) { + endptr = nptr; + retval = -EINVAL; + goto out; + } else if (fraction != 0) { + mul_required = true; + } } c = *endptr; mul = suffix_mul(c, unit); - if (mul >= 0) { + if (mul > 0) { + if (hex) { + warn_report("Using a multiplier suffix on hex numbers " + "is deprecated: %s", nptr); + } endptr++; } else { mul = suffix_mul(default_suffix, unit); - assert(mul >= 0); + assert(mul > 0); } if (mul == 1 && mul_required) { + endptr = nptr; retval = -EINVAL; goto out; } - /* - * Values near UINT64_MAX overflow to 2**64 when converting to double - * precision. Compare against the maximum representable double precision - * value below 2**64, computed as "the next value after 2**64 (0x1p64) in - * the direction of 0". - */ - if ((val * mul > nextafter(0x1p64, 0)) || val < 0) { + if (val > (UINT64_MAX - ((uint64_t) (fraction * mul))) / mul) { retval = -ERANGE; goto out; } - *result = val * mul; + *result = val * mul + (uint64_t) (fraction * mul); retval = 0; out: diff --git a/util/id.c b/util/id.c index 5addb4460e..ded41c5025 100644 --- a/util/id.c +++ b/util/id.c @@ -35,6 +35,7 @@ static const char *const id_subsys_str[ID_MAX] = { [ID_QDEV] = "qdev", [ID_BLOCK] = "block", [ID_CHR] = "chr", + [ID_NET] = "net", }; /* diff --git a/util/qemu-timer.c b/util/qemu-timer.c index f36c75e594..be529c1f65 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -242,6 +242,19 @@ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) return delta; } +/* + * Returns the time remaining for the deadline, in ms. + */ +int64_t timer_deadline_ms(QEMUTimer *timer) +{ + if (timer_pending(timer)) { + return qemu_timeout_ns_to_ms(timer->expire_time) - + qemu_clock_get_ms(timer->timer_list->clock->type); + } + + return 0; +} + /* Calculate the soonest deadline across all timerlists attached * to the clock. This is used for the icount timeout so we * ignore whether or not the clock should be used in deadline |