diff options
287 files changed, 17738 insertions, 1670 deletions
@@ -44,11 +44,13 @@ Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgte Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com> Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com> Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com> +Alexander Graf <agraf@csgraf.de> <agraf@suse.de> Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com> Frederic Konrad <konrad@adacore.com> <fred.konrad@greensocs.com> James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> Leif Lindholm <leif@nuviainc.com> <leif.lindholm@linaro.org> +Radoslaw Biernacki <rad@semihalf.com> <radoslaw.biernacki@linaro.org> Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com> Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com> Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org> diff --git a/MAINTAINERS b/MAINTAINERS index dec252f38b..f01284ebce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -787,6 +787,7 @@ F: hw/gpio/max7310.c F: hw/gpio/zaurus.c F: hw/misc/mst_fpga.c F: hw/misc/max111x.c +F: include/hw/misc/max111x.h F: include/hw/arm/pxa.h F: include/hw/arm/sharpsl.h F: include/hw/display/tc6393xb.h @@ -808,7 +809,7 @@ F: include/hw/misc/imx6_*.h F: include/hw/ssi/imx_spi.h SBSA-REF -M: Radoslaw Biernacki <radoslaw.biernacki@linaro.org> +M: Radoslaw Biernacki <rad@semihalf.com> M: Peter Maydell <peter.maydell@linaro.org> R: Leif Lindholm <leif@nuviainc.com> L: qemu-arm@nongnu.org @@ -1318,6 +1319,7 @@ F: include/hw/pci-host/sabre.h F: hw/pci-bridge/simba.c F: include/hw/pci-bridge/simba.h F: pc-bios/openbios-sparc64 +F: tests/acceptance/machine_sparc64_sun4u.py Sun4v M: Artyom Tarasenko <atar4qemu@gmail.com> @@ -1790,6 +1792,15 @@ F: hw/virtio/virtio-crypto.c F: hw/virtio/virtio-crypto-pci.c F: include/hw/virtio/virtio-crypto.h +virtio-mem +M: David Hildenbrand <david@redhat.com> +S: Supported +W: https://virtio-mem.gitlab.io/ +F: hw/virtio/virtio-mem.c +F: hw/virtio/virtio-mem-pci.h +F: hw/virtio/virtio-mem-pci.c +F: include/hw/virtio/virtio-mem.h + nvme M: Keith Busch <kbusch@kernel.org> L: qemu-block@nongnu.org @@ -2615,6 +2626,15 @@ F: tests/uefi-test-tools/ F: .gitlab-ci.d/edk2.yml F: .gitlab-ci.d/edk2/ +VT-d Emulation +M: Michael S. Tsirkin <mst@redhat.com> +M: Peter Xu <peterx@redhat.com> +R: Jason Wang <jasowang@redhat.com> +S: Supported +F: hw/i386/intel_iommu.c +F: hw/i386/intel_iommu_internal.h +F: include/hw/i386/intel_iommu.h + Usermode Emulation ------------------ Overall usermode emulation @@ -2629,8 +2649,7 @@ F: bsd-user/ F: default-configs/*-bsd-user.mak Linux user -M: Riku Voipio <riku.voipio@iki.fi> -R: Laurent Vivier <laurent@vivier.eu> +M: Laurent Vivier <laurent@vivier.eu> S: Maintained F: linux-user/ F: default-configs/*-linux-user.mak diff --git a/Makefile.objs b/Makefile.objs index 98383972ee..d22b3b45d7 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -59,6 +59,7 @@ common-obj-y += migration/ common-obj-y += audio/ common-obj-m += audio/ common-obj-y += hw/ +common-obj-m += hw/ common-obj-y += replay/ @@ -70,6 +71,7 @@ common-obj-$(CONFIG_TPM) += tpm.o common-obj-y += backends/ common-obj-y += chardev/ +common-obj-m += chardev/ common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o qemu-seccomp.o-cflags := $(SECCOMP_CFLAGS) diff --git a/Makefile.target b/Makefile.target index 8ed1eba95b..02bd9d7117 100644 --- a/Makefile.target +++ b/Makefile.target @@ -179,6 +179,20 @@ endif # CONFIG_SOFTMMU dummy := $(call unnest-vars,,obj-y) all-obj-y := $(obj-y) +# +# common-obj-m has some crap here, probably as side effect from +# unnest-vars recursing into target directories to fill obj-y and not +# properly handling the -m case. +# +# Clear common-obj-m as workaround. Fixes suspious dependency errors +# when building devices as modules. A bit hackish, but should be ok +# as long as we do not have any target-specific modules. +# +# The meson-based build system currently in development doesn't need +# unnest-vars and will obsolete this workaround. +# +common-obj-m := + include $(SRC_PATH)/Makefile.objs dummy := $(call unnest-vars,.., \ authz-obj-y \ diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index d54a8701d8..ab36fbfa0c 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -40,7 +40,6 @@ #include "trace.h" #include "hw/irq.h" #include "sysemu/sev.h" -#include "sysemu/balloon.h" #include "qapi/visitor.h" #include "qapi/qapi-types-common.h" #include "qapi/qapi-visit-common.h" @@ -2229,7 +2228,8 @@ static int kvm_init(MachineState *ms) s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); if (!s->sync_mmu) { - qemu_balloon_inhibit(true); + ret = ram_block_discard_disable(true); + assert(!ret); } return 0; @@ -36,23 +36,6 @@ static QEMUBalloonEvent *balloon_event_fn; static QEMUBalloonStatus *balloon_stat_fn; static void *balloon_opaque; -static int balloon_inhibit_count; - -bool qemu_balloon_is_inhibited(void) -{ - return atomic_read(&balloon_inhibit_count) > 0; -} - -void qemu_balloon_inhibit(bool state) -{ - if (state) { - atomic_inc(&balloon_inhibit_count); - } else { - atomic_dec(&balloon_inhibit_count); - } - - assert(atomic_read(&balloon_inhibit_count) >= 0); -} static bool have_balloon(Error **errp) { @@ -5408,21 +5408,6 @@ int bdrv_has_zero_init(BlockDriverState *bs) return 0; } -bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs) -{ - BlockDriverInfo bdi; - - if (bs->backing) { - return false; - } - - if (bdrv_get_info(bs, &bdi) == 0) { - return bdi.unallocated_blocks_are_zero; - } - - return false; -} - bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) { if (!(bs->open_flags & BDRV_O_UNMAP)) { @@ -6482,6 +6467,7 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + bool force, Error **errp) { if (!bs->drv) { @@ -6493,7 +6479,8 @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, bs->drv->format_name); return -ENOTSUP; } - return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp); + return bs->drv->bdrv_amend_options(bs, opts, status_cb, + cb_opaque, force, errp); } /* diff --git a/block/Makefile.objs b/block/Makefile.objs index 96028eedce..577e578bc2 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -19,7 +19,7 @@ block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += file-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o block-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o -block-obj-y += null.o mirror.o commit.o io.o create.o +block-obj-y += null.o mirror.o commit.o io.o create.o amend.o block-obj-y += throttle-groups.o block-obj-$(CONFIG_LINUX) += nvme.o diff --git a/block/amend.c b/block/amend.c new file mode 100644 index 0000000000..f4612dcf08 --- /dev/null +++ b/block/amend.c @@ -0,0 +1,113 @@ +/* + * Block layer code related to image options amend + * + * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com> + * Copyright (c) 2020 Red Hat. Inc + * + * Heavily based on create.c + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/block_int.h" +#include "qemu/job.h" +#include "qemu/main-loop.h" +#include "qapi/qapi-commands-block-core.h" +#include "qapi/qapi-visit-block-core.h" +#include "qapi/clone-visitor.h" +#include "qapi/error.h" + +typedef struct BlockdevAmendJob { + Job common; + BlockdevAmendOptions *opts; + BlockDriverState *bs; + bool force; +} BlockdevAmendJob; + +static int coroutine_fn blockdev_amend_run(Job *job, Error **errp) +{ + BlockdevAmendJob *s = container_of(job, BlockdevAmendJob, common); + int ret; + + job_progress_set_remaining(&s->common, 1); + ret = s->bs->drv->bdrv_co_amend(s->bs, s->opts, s->force, errp); + job_progress_update(&s->common, 1); + qapi_free_BlockdevAmendOptions(s->opts); + return ret; +} + +static const JobDriver blockdev_amend_job_driver = { + .instance_size = sizeof(BlockdevAmendJob), + .job_type = JOB_TYPE_AMEND, + .run = blockdev_amend_run, +}; + +void qmp_x_blockdev_amend(const char *job_id, + const char *node_name, + BlockdevAmendOptions *options, + bool has_force, + bool force, + Error **errp) +{ + BlockdevAmendJob *s; + const char *fmt = BlockdevDriver_str(options->driver); + BlockDriver *drv = bdrv_find_format(fmt); + BlockDriverState *bs = bdrv_find_node(node_name); + + + if (!drv) { + error_setg(errp, "Block driver '%s' not found or not supported", fmt); + return; + } + + /* + * If the driver is in the schema, we know that it exists. But it may not + * be whitelisted. + */ + if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) { + error_setg(errp, "Driver is not whitelisted"); + return; + } + + if (bs->drv != drv) { + error_setg(errp, + "x-blockdev-amend doesn't support changing the block driver"); + return; + } + + /* Error out if the driver doesn't support .bdrv_co_amend */ + if (!drv->bdrv_co_amend) { + error_setg(errp, "Driver does not support x-blockdev-amend"); + return; + } + + /* Create the block job */ + s = job_create(job_id, &blockdev_amend_job_driver, NULL, + bdrv_get_aio_context(bs), JOB_DEFAULT | JOB_MANUAL_DISMISS, + NULL, NULL, errp); + if (!s) { + return; + } + + s->bs = bs, + s->opts = QAPI_CLONE(BlockdevAmendOptions, options), + s->force = has_force ? force : false; + job_start(&s->common); +} diff --git a/block/block-copy.c b/block/block-copy.c index bb8d0569f2..f7428a7c08 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -622,8 +622,10 @@ out: * block_copy_task_run. If it fails, it means some task already failed * for real reason, let's return first failure. * Still, assert that we don't rewrite failure by success. + * + * Note: ret may be positive here because of block-status result. */ - assert(ret == 0 || aio_task_pool_status(aio) < 0); + assert(ret >= 0 || aio_task_pool_status(aio) < 0); ret = aio_task_pool_status(aio); aio_task_pool_free(aio); diff --git a/block/crypto.c b/block/crypto.c index 973b57b3eb..2636e959ae 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -37,6 +37,7 @@ typedef struct BlockCrypto BlockCrypto; struct BlockCrypto { QCryptoBlock *block; + bool updating_keys; }; @@ -71,6 +72,24 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block, return ret; } +static ssize_t block_crypto_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) +{ + BlockDriverState *bs = opaque; + ssize_t ret; + + ret = bdrv_pwrite(bs->file, offset, buf, buflen); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write encryption header"); + return ret; + } + return ret; +} + struct BlockCryptoCreateData { BlockBackend *blk; @@ -79,12 +98,12 @@ struct BlockCryptoCreateData { }; -static ssize_t block_crypto_write_func(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static ssize_t block_crypto_create_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { struct BlockCryptoCreateData *data = opaque; ssize_t ret; @@ -97,11 +116,10 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block, return ret; } - -static ssize_t block_crypto_init_func(QCryptoBlock *block, - size_t headerlen, - void *opaque, - Error **errp) +static ssize_t block_crypto_create_init_func(QCryptoBlock *block, + size_t headerlen, + void *opaque, + Error **errp) { struct BlockCryptoCreateData *data = opaque; Error *local_error = NULL; @@ -167,6 +185,19 @@ static QemuOptsList block_crypto_create_opts_luks = { }; +static QemuOptsList block_crypto_amend_opts_luks = { + .name = "crypto", + .head = QTAILQ_HEAD_INITIALIZER(block_crypto_create_opts_luks.head), + .desc = { + BLOCK_CRYPTO_OPT_DEF_LUKS_STATE(""), + BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(""), + BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET(""), + BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET(""), + BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""), + { /* end of list */ } + }, +}; + QCryptoBlockOpenOptions * block_crypto_open_opts_init(QDict *opts, Error **errp) { @@ -202,6 +233,23 @@ block_crypto_create_opts_init(QDict *opts, Error **errp) return ret; } +QCryptoBlockAmendOptions * +block_crypto_amend_opts_init(QDict *opts, Error **errp) +{ + Visitor *v; + QCryptoBlockAmendOptions *ret; + + v = qobject_input_visitor_new_flat_confused(opts, errp); + if (!v) { + return NULL; + } + + visit_type_QCryptoBlockAmendOptions(v, NULL, &ret, errp); + + visit_free(v); + return ret; +} + static int block_crypto_open_generic(QCryptoBlockFormat format, QemuOptsList *opts_spec, @@ -296,8 +344,8 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, }; crypto = qcrypto_block_create(opts, NULL, - block_crypto_init_func, - block_crypto_write_func, + block_crypto_create_init_func, + block_crypto_create_write_func, &data, errp); @@ -710,7 +758,6 @@ static int block_crypto_get_info_luks(BlockDriverState *bs, return ret; } - bdi->unallocated_blocks_are_zero = false; bdi->cluster_size = subbdi.cluster_size; return 0; @@ -742,6 +789,131 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) return spec_info; } +static int +block_crypto_amend_options_generic_luks(BlockDriverState *bs, + QCryptoBlockAmendOptions *amend_options, + bool force, + Error **errp) +{ + BlockCrypto *crypto = bs->opaque; + int ret; + + assert(crypto); + assert(crypto->block); + + /* apply for exclusive read/write permissions to the underlying file*/ + crypto->updating_keys = true; + ret = bdrv_child_refresh_perms(bs, bs->file, errp); + if (ret) { + goto cleanup; + } + + ret = qcrypto_block_amend_options(crypto->block, + block_crypto_read_func, + block_crypto_write_func, + bs, + amend_options, + force, + errp); +cleanup: + /* release exclusive read/write permissions to the underlying file*/ + crypto->updating_keys = false; + bdrv_child_refresh_perms(bs, bs->file, errp); + return ret; +} + +static int +block_crypto_amend_options_luks(BlockDriverState *bs, + QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque, + bool force, + Error **errp) +{ + BlockCrypto *crypto = bs->opaque; + QDict *cryptoopts = NULL; + QCryptoBlockAmendOptions *amend_options = NULL; + int ret = -EINVAL; + + assert(crypto); + assert(crypto->block); + + cryptoopts = qemu_opts_to_qdict(opts, NULL); + qdict_put_str(cryptoopts, "format", "luks"); + amend_options = block_crypto_amend_opts_init(cryptoopts, errp); + qobject_unref(cryptoopts); + if (!amend_options) { + goto cleanup; + } + ret = block_crypto_amend_options_generic_luks(bs, amend_options, + force, errp); +cleanup: + qapi_free_QCryptoBlockAmendOptions(amend_options); + return ret; +} + +static int +coroutine_fn block_crypto_co_amend_luks(BlockDriverState *bs, + BlockdevAmendOptions *opts, + bool force, + Error **errp) +{ + QCryptoBlockAmendOptions amend_opts; + + amend_opts = (QCryptoBlockAmendOptions) { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = *qapi_BlockdevAmendOptionsLUKS_base(&opts->u.luks), + }; + return block_crypto_amend_options_generic_luks(bs, &amend_opts, + force, errp); +} + +static void +block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + + BlockCrypto *crypto = bs->opaque; + + bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared); + + /* + * For backward compatibility, manually share the write + * and resize permission + */ + *nshared |= (BLK_PERM_WRITE | BLK_PERM_RESIZE); + /* + * Since we are not fully a format driver, don't always request + * the read/resize permission but only when explicitly + * requested + */ + *nperm &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); + *nperm |= perm & (BLK_PERM_WRITE | BLK_PERM_RESIZE); + + /* + * This driver doesn't modify LUKS metadata except + * when updating the encryption slots. + * Thus unlike a proper format driver we don't ask for + * shared write/read permission. However we need it + * when we are updating the keys, to ensure that only we + * have access to the device. + * + * Encryption update will set the crypto->updating_keys + * during that period and refresh permissions + * + */ + if (crypto->updating_keys) { + /* need exclusive write access for header update */ + *nperm |= BLK_PERM_WRITE; + /* unshare read and write permission */ + *nshared &= ~(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE); + } +} + + static const char *const block_crypto_strong_runtime_opts[] = { BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET, @@ -754,13 +926,12 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_probe = block_crypto_probe_luks, .bdrv_open = block_crypto_open_luks, .bdrv_close = block_crypto_close, - /* This driver doesn't modify LUKS metadata except when creating image. - * Allow share-rw=on as a special case. */ - .bdrv_child_perm = bdrv_default_perms, + .bdrv_child_perm = block_crypto_child_perms, .bdrv_co_create = block_crypto_co_create_luks, .bdrv_co_create_opts = block_crypto_co_create_opts_luks, .bdrv_co_truncate = block_crypto_co_truncate, .create_opts = &block_crypto_create_opts_luks, + .amend_opts = &block_crypto_amend_opts_luks, .bdrv_reopen_prepare = block_crypto_reopen_prepare, .bdrv_refresh_limits = block_crypto_refresh_limits, @@ -770,6 +941,8 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_measure = block_crypto_measure, .bdrv_get_info = block_crypto_get_info_luks, .bdrv_get_specific_info = block_crypto_get_specific_info_luks, + .bdrv_amend_options = block_crypto_amend_options_luks, + .bdrv_co_amend = block_crypto_co_amend_luks, .is_format = true, diff --git a/block/crypto.h b/block/crypto.h index b935695e79..c72c3dec61 100644 --- a/block/crypto.h +++ b/block/crypto.h @@ -41,6 +41,11 @@ #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg" #define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg" #define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time" +#define BLOCK_CRYPTO_OPT_LUKS_KEYSLOT "keyslot" +#define BLOCK_CRYPTO_OPT_LUKS_STATE "state" +#define BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET "old-secret" +#define BLOCK_CRYPTO_OPT_LUKS_NEW_SECRET "new-secret" + #define BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(prefix) \ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, \ @@ -88,9 +93,41 @@ .help = "Time to spend in PBKDF in milliseconds", \ } +#define BLOCK_CRYPTO_OPT_DEF_LUKS_STATE(prefix) \ + { \ + .name = prefix BLOCK_CRYPTO_OPT_LUKS_STATE, \ + .type = QEMU_OPT_STRING, \ + .help = "Select new state of affected keyslots (active/inactive)",\ + } + +#define BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(prefix) \ + { \ + .name = prefix BLOCK_CRYPTO_OPT_LUKS_KEYSLOT, \ + .type = QEMU_OPT_NUMBER, \ + .help = "Select a single keyslot to modify explicitly",\ + } + +#define BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET(prefix) \ + { \ + .name = prefix BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET, \ + .type = QEMU_OPT_STRING, \ + .help = "Select all keyslots that match this password", \ + } + +#define BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET(prefix) \ + { \ + .name = prefix BLOCK_CRYPTO_OPT_LUKS_NEW_SECRET, \ + .type = QEMU_OPT_STRING, \ + .help = "New secret to set in the matching keyslots. " \ + "Empty string to erase", \ + } + QCryptoBlockCreateOptions * block_crypto_create_opts_init(QDict *opts, Error **errp); +QCryptoBlockAmendOptions * +block_crypto_amend_opts_init(QDict *opts, Error **errp); + QCryptoBlockOpenOptions * block_crypto_open_opts_init(QDict *opts, Error **errp); diff --git a/block/file-posix.c b/block/file-posix.c index 3ab8f5a0fa..d86ea57769 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2878,9 +2878,6 @@ static int coroutine_fn raw_co_pwrite_zeroes( static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { - BDRVRawState *s = bs->opaque; - - bdi->unallocated_blocks_are_zero = s->discard_zeroes; return 0; } diff --git a/block/io.c b/block/io.c index df8f2a98d4..b6564e34c5 100644 --- a/block/io.c +++ b/block/io.c @@ -2406,16 +2406,16 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { ret |= BDRV_BLOCK_ALLOCATED; - } else if (want_zero) { - if (bdrv_unallocated_blocks_are_zero(bs)) { - ret |= BDRV_BLOCK_ZERO; - } else if (bs->backing) { + } else if (want_zero && bs->drv->supports_backing) { + if (bs->backing) { BlockDriverState *bs2 = bs->backing->bs; int64_t size2 = bdrv_getlength(bs2); if (size2 >= 0 && offset >= size2) { ret |= BDRV_BLOCK_ZERO; } + } else { + ret |= BDRV_BLOCK_ZERO; } } diff --git a/block/iscsi.c b/block/iscsi.c index a8b76979d8..767e3e75fd 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2163,7 +2163,6 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { IscsiLun *iscsilun = bs->opaque; - bdi->unallocated_blocks_are_zero = iscsilun->lbprz; bdi->cluster_size = iscsilun->cluster_size; return 0; } diff --git a/block/qcow2.c b/block/qcow2.c index 0cd2e6757e..38198b4e75 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -176,6 +176,19 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, return ret; } +static QDict* +qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp) +{ + QDict *cryptoopts_qdict; + QDict *opts_qdict; + + /* Extract "encrypt." options into a qdict */ + opts_qdict = qemu_opts_to_qdict(opts, NULL); + qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt."); + qobject_unref(opts_qdict); + qdict_put_str(cryptoopts_qdict, "format", fmt); + return cryptoopts_qdict; +} /* * read qcow2 extension and fill bs @@ -3042,17 +3055,6 @@ static int qcow2_change_backing_file(BlockDriverState *bs, return qcow2_update_header(bs); } -static int qcow2_crypt_method_from_format(const char *encryptfmt) -{ - if (g_str_equal(encryptfmt, "luks")) { - return QCOW_CRYPT_LUKS; - } else if (g_str_equal(encryptfmt, "aes")) { - return QCOW_CRYPT_AES; - } else { - return -EINVAL; - } -} - static int qcow2_set_up_encryption(BlockDriverState *bs, QCryptoBlockCreateOptions *cryptoopts, Error **errp) @@ -4239,8 +4241,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, old_file_size = ROUND_UP(old_file_size, s->cluster_size); } - nb_new_data_clusters = DIV_ROUND_UP(offset - old_length, - s->cluster_size); + nb_new_data_clusters = (ROUND_UP(offset, s->cluster_size) - + start_of_cluster(s, old_length)) >> s->cluster_bits; /* This is an overestimation; we will not actually allocate space for * these in the file but just make sure the new refcount structures are @@ -4317,10 +4319,21 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, int64_t nb_clusters = MIN( nb_new_data_clusters, s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset)); - QCowL2Meta allocation = { + unsigned cow_start_length = offset_into_cluster(s, guest_offset); + QCowL2Meta allocation; + guest_offset = start_of_cluster(s, guest_offset); + allocation = (QCowL2Meta) { .offset = guest_offset, .alloc_offset = host_offset, .nb_clusters = nb_clusters, + .cow_start = { + .offset = 0, + .nb_bytes = cow_start_length, + }, + .cow_end = { + .offset = nb_clusters << s->cluster_bits, + .nb_bytes = 0, + }, }; qemu_co_queue_init(&allocation.dependent_requests); @@ -4860,16 +4873,9 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, if (has_luks) { g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; - QDict *opts_qdict; - QDict *cryptoopts; + QDict *cryptoopts = qcow2_extract_crypto_opts(opts, "luks", errp); size_t headerlen; - opts_qdict = qemu_opts_to_qdict(opts, NULL); - qdict_extract_subqdict(opts_qdict, &cryptoopts, "encrypt."); - qobject_unref(opts_qdict); - - qdict_put_str(cryptoopts, "format", "luks"); - create_opts = block_crypto_create_opts_init(cryptoopts, errp); qobject_unref(cryptoopts); if (!create_opts) { @@ -4981,7 +4987,6 @@ err: static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcow2State *s = bs->opaque; - bdi->unallocated_blocks_are_zero = true; bdi->cluster_size = s->cluster_size; bdi->vm_state_offset = qcow2_vm_state_offset(s); return 0; @@ -5273,6 +5278,7 @@ typedef enum Qcow2AmendOperation { QCOW2_NO_OPERATION = 0, QCOW2_UPGRADING, + QCOW2_UPDATING_ENCRYPTION, QCOW2_CHANGING_REFCOUNT_ORDER, QCOW2_DOWNGRADING, } Qcow2AmendOperation; @@ -5340,6 +5346,7 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs, static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + bool force, Error **errp) { BDRVQcow2State *s = bs->opaque; @@ -5349,13 +5356,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, bool lazy_refcounts = s->use_lazy_refcounts; bool data_file_raw = data_file_is_raw(bs); const char *compat = NULL; - uint64_t cluster_size = s->cluster_size; - bool encrypt; - int encformat; int refcount_bits = s->refcount_bits; int ret; QemuOptDesc *desc = opts->list->desc; Qcow2AmendHelperCBInfo helper_cb_info; + bool encryption_update = false; while (desc && desc->name) { if (!qemu_opt_find(opts, desc->name)) { @@ -5376,44 +5381,24 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, error_setg(errp, "Unknown compatibility level %s", compat); return -EINVAL; } - } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { - error_setg(errp, "Cannot change preallocation mode"); - return -ENOTSUP; } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) { backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); - } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) { - encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, - !!s->crypto); - - if (encrypt != !!s->crypto) { + } else if (g_str_has_prefix(desc->name, "encrypt.")) { + if (!s->crypto) { error_setg(errp, - "Changing the encryption flag is not supported"); - return -ENOTSUP; + "Can't amend encryption options - encryption not present"); + return -EINVAL; } - } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) { - encformat = qcow2_crypt_method_from_format( - qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT)); - - if (encformat != s->crypt_method_header) { + if (s->crypt_method_header != QCOW_CRYPT_LUKS) { error_setg(errp, - "Changing the encryption format is not supported"); - return -ENOTSUP; - } - } else if (g_str_has_prefix(desc->name, "encrypt.")) { - error_setg(errp, - "Changing the encryption parameters is not supported"); - return -ENOTSUP; - } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { - cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, - cluster_size); - if (cluster_size != s->cluster_size) { - error_setg(errp, "Changing the cluster size is not supported"); + "Only LUKS encryption options can be amended"); return -ENOTSUP; } + encryption_update = true; } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, lazy_refcounts); @@ -5443,22 +5428,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, "images"); return -EINVAL; } - } else if (!strcmp(desc->name, BLOCK_OPT_COMPRESSION_TYPE)) { - const char *ct_name = - qemu_opt_get(opts, BLOCK_OPT_COMPRESSION_TYPE); - int compression_type = - qapi_enum_parse(&Qcow2CompressionType_lookup, ct_name, -1, - NULL); - if (compression_type == -1) { - error_setg(errp, "Unknown compression type: %s", ct_name); - return -ENOTSUP; - } - - if (compression_type != s->compression_type) { - error_setg(errp, "Changing the compression type " - "is not supported"); - return -ENOTSUP; - } } else { /* if this point is reached, this probably means a new option was * added without having it covered here */ @@ -5472,7 +5441,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, .original_status_cb = status_cb, .original_cb_opaque = cb_opaque, .total_operations = (new_version != old_version) - + (s->refcount_bits != refcount_bits) + + (s->refcount_bits != refcount_bits) + + (encryption_update == true) }; /* Upgrade first (some features may require compat=1.1) */ @@ -5485,6 +5455,33 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } } + if (encryption_update) { + QDict *amend_opts_dict; + QCryptoBlockAmendOptions *amend_opts; + + helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION; + amend_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp); + if (!amend_opts_dict) { + return -EINVAL; + } + amend_opts = block_crypto_amend_opts_init(amend_opts_dict, errp); + qobject_unref(amend_opts_dict); + if (!amend_opts) { + return -EINVAL; + } + ret = qcrypto_block_amend_options(s->crypto, + qcow2_crypto_hdr_read_func, + qcow2_crypto_hdr_write_func, + bs, + amend_opts, + force, + errp); + qapi_free_QCryptoBlockAmendOptions(amend_opts); + if (ret < 0) { + return ret; + } + } + if (s->refcount_bits != refcount_bits) { int refcount_order = ctz32(refcount_bits); @@ -5598,6 +5595,44 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, return 0; } +static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, + BlockdevAmendOptions *opts, + bool force, + Error **errp) +{ + BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2; + BDRVQcow2State *s = bs->opaque; + int ret = 0; + + if (qopts->has_encrypt) { + if (!s->crypto) { + error_setg(errp, "image is not encrypted, can't amend"); + return -EOPNOTSUPP; + } + + if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) { + error_setg(errp, + "Amend can't be used to change the qcow2 encryption format"); + return -EOPNOTSUPP; + } + + if (s->crypt_method_header != QCOW_CRYPT_LUKS) { + error_setg(errp, + "Only LUKS encryption options can be amended for qcow2 with blockdev-amend"); + return -EOPNOTSUPP; + } + + ret = qcrypto_block_amend_options(s->crypto, + qcow2_crypto_hdr_read_func, + qcow2_crypto_hdr_write_func, + bs, + qopts->encrypt, + force, + errp); + } + return ret; +} + /* * If offset or size are negative, respectively, they will not be included in * the BLOCK_IMAGE_CORRUPTED event emitted. @@ -5648,89 +5683,108 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, s->signaled_corruption = true; } +#define QCOW_COMMON_OPTIONS \ + { \ + .name = BLOCK_OPT_SIZE, \ + .type = QEMU_OPT_SIZE, \ + .help = "Virtual disk size" \ + }, \ + { \ + .name = BLOCK_OPT_COMPAT_LEVEL, \ + .type = QEMU_OPT_STRING, \ + .help = "Compatibility level (v2 [0.10] or v3 [1.1])" \ + }, \ + { \ + .name = BLOCK_OPT_BACKING_FILE, \ + .type = QEMU_OPT_STRING, \ + .help = "File name of a base image" \ + }, \ + { \ + .name = BLOCK_OPT_BACKING_FMT, \ + .type = QEMU_OPT_STRING, \ + .help = "Image format of the base image" \ + }, \ + { \ + .name = BLOCK_OPT_DATA_FILE, \ + .type = QEMU_OPT_STRING, \ + .help = "File name of an external data file" \ + }, \ + { \ + .name = BLOCK_OPT_DATA_FILE_RAW, \ + .type = QEMU_OPT_BOOL, \ + .help = "The external data file must stay valid " \ + "as a raw image" \ + }, \ + { \ + .name = BLOCK_OPT_LAZY_REFCOUNTS, \ + .type = QEMU_OPT_BOOL, \ + .help = "Postpone refcount updates", \ + .def_value_str = "off" \ + }, \ + { \ + .name = BLOCK_OPT_REFCOUNT_BITS, \ + .type = QEMU_OPT_NUMBER, \ + .help = "Width of a reference count entry in bits", \ + .def_value_str = "16" \ + } + static QemuOptsList qcow2_create_opts = { .name = "qcow2-create-opts", .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head), .desc = { - { - .name = BLOCK_OPT_SIZE, - .type = QEMU_OPT_SIZE, - .help = "Virtual disk size" - }, - { - .name = BLOCK_OPT_COMPAT_LEVEL, - .type = QEMU_OPT_STRING, - .help = "Compatibility level (v2 [0.10] or v3 [1.1])" - }, - { - .name = BLOCK_OPT_BACKING_FILE, - .type = QEMU_OPT_STRING, - .help = "File name of a base image" - }, - { - .name = BLOCK_OPT_BACKING_FMT, - .type = QEMU_OPT_STRING, - .help = "Image format of the base image" - }, - { - .name = BLOCK_OPT_DATA_FILE, - .type = QEMU_OPT_STRING, - .help = "File name of an external data file" - }, - { - .name = BLOCK_OPT_DATA_FILE_RAW, - .type = QEMU_OPT_BOOL, - .help = "The external data file must stay valid as a raw image" - }, - { - .name = BLOCK_OPT_ENCRYPT, - .type = QEMU_OPT_BOOL, - .help = "Encrypt the image with format 'aes'. (Deprecated " - "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", + { \ + .name = BLOCK_OPT_ENCRYPT, \ + .type = QEMU_OPT_BOOL, \ + .help = "Encrypt the image with format 'aes'. (Deprecated " \ + "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \ + }, \ + { \ + .name = BLOCK_OPT_ENCRYPT_FORMAT, \ + .type = QEMU_OPT_STRING, \ + .help = "Encrypt the image, format choices: 'aes', 'luks'", \ + }, \ + BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \ + "ID of secret providing qcow AES key or LUKS passphrase"), \ + BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \ + BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \ + BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \ + BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \ + BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \ + BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \ + { \ + .name = BLOCK_OPT_CLUSTER_SIZE, \ + .type = QEMU_OPT_SIZE, \ + .help = "qcow2 cluster size", \ + .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \ + }, \ + { \ + .name = BLOCK_OPT_PREALLOC, \ + .type = QEMU_OPT_STRING, \ + .help = "Preallocation mode (allowed values: off, " \ + "metadata, falloc, full)" \ + }, \ + { \ + .name = BLOCK_OPT_COMPRESSION_TYPE, \ + .type = QEMU_OPT_STRING, \ + .help = "Compression method used for image cluster " \ + "compression", \ + .def_value_str = "zlib" \ }, - { - .name = BLOCK_OPT_ENCRYPT_FORMAT, - .type = QEMU_OPT_STRING, - .help = "Encrypt the image, format choices: 'aes', 'luks'", - }, - BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", - "ID of secret providing qcow AES key or LUKS passphrase"), - BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), - BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), - BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), - BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), - BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), + QCOW_COMMON_OPTIONS, + { /* end of list */ } + } +}; + +static QemuOptsList qcow2_amend_opts = { + .name = "qcow2-amend-opts", + .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head), + .desc = { + BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."), + BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."), + BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."), + BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("encrypt."), BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), - { - .name = BLOCK_OPT_CLUSTER_SIZE, - .type = QEMU_OPT_SIZE, - .help = "qcow2 cluster size", - .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) - }, - { - .name = BLOCK_OPT_PREALLOC, - .type = QEMU_OPT_STRING, - .help = "Preallocation mode (allowed values: off, metadata, " - "falloc, full)" - }, - { - .name = BLOCK_OPT_LAZY_REFCOUNTS, - .type = QEMU_OPT_BOOL, - .help = "Postpone refcount updates", - .def_value_str = "off" - }, - { - .name = BLOCK_OPT_REFCOUNT_BITS, - .type = QEMU_OPT_NUMBER, - .help = "Width of a reference count entry in bits", - .def_value_str = "16" - }, - { - .name = BLOCK_OPT_COMPRESSION_TYPE, - .type = QEMU_OPT_STRING, - .help = "Compression method used for image cluster compression", - .def_value_str = "zlib" - }, + QCOW_COMMON_OPTIONS, { /* end of list */ } } }; @@ -5791,10 +5845,12 @@ BlockDriver bdrv_qcow2 = { .bdrv_inactivate = qcow2_inactivate, .create_opts = &qcow2_create_opts, + .amend_opts = &qcow2_amend_opts, .strong_runtime_opts = qcow2_strong_runtime_opts, .mutable_opts = mutable_opts, .bdrv_co_check = qcow2_co_check, .bdrv_amend_options = qcow2_amend_options, + .bdrv_co_amend = qcow2_co_amend, .bdrv_detach_aio_context = qcow2_detach_aio_context, .bdrv_attach_aio_context = qcow2_attach_aio_context, diff --git a/block/qed.c b/block/qed.c index c0c65015c7..ece8b9bb60 100644 --- a/block/qed.c +++ b/block/qed.c @@ -849,56 +849,18 @@ static BDRVQEDState *acb_to_s(QEDAIOCB *acb) * @s: QED state * @pos: Byte position in device * @qiov: Destination I/O vector - * @backing_qiov: Possibly shortened copy of qiov, to be allocated here - * @cb: Completion function - * @opaque: User data for completion function * * This function reads qiov->size bytes starting at pos from the backing file. * If there is no backing file then zeroes are read. */ static int coroutine_fn qed_read_backing_file(BDRVQEDState *s, uint64_t pos, - QEMUIOVector *qiov, - QEMUIOVector **backing_qiov) + QEMUIOVector *qiov) { - uint64_t backing_length = 0; - size_t size; - int ret; - - /* If there is a backing file, get its length. Treat the absence of a - * backing file like a zero length backing file. - */ if (s->bs->backing) { - int64_t l = bdrv_getlength(s->bs->backing->bs); - if (l < 0) { - return l; - } - backing_length = l; - } - - /* Zero all sectors if reading beyond the end of the backing file */ - if (pos >= backing_length || - pos + qiov->size > backing_length) { - qemu_iovec_memset(qiov, 0, 0, qiov->size); - } - - /* Complete now if there are no backing file sectors to read */ - if (pos >= backing_length) { - return 0; - } - - /* If the read straddles the end of the backing file, shorten it */ - size = MIN((uint64_t)backing_length - pos, qiov->size); - - assert(*backing_qiov == NULL); - *backing_qiov = g_new(QEMUIOVector, 1); - qemu_iovec_init(*backing_qiov, qiov->niov); - qemu_iovec_concat(*backing_qiov, qiov, 0, size); - - BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO); - ret = bdrv_co_preadv(s->bs->backing, pos, size, *backing_qiov, 0); - if (ret < 0) { - return ret; + BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO); + return bdrv_co_preadv(s->bs->backing, pos, qiov->size, qiov, 0); } + qemu_iovec_memset(qiov, 0, 0, qiov->size); return 0; } @@ -915,7 +877,6 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s, uint64_t offset) { QEMUIOVector qiov; - QEMUIOVector *backing_qiov = NULL; int ret; /* Skip copy entirely if there is no work to do */ @@ -925,13 +886,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s, qemu_iovec_init_buf(&qiov, qemu_blockalign(s->bs, len), len); - ret = qed_read_backing_file(s, pos, &qiov, &backing_qiov); - - if (backing_qiov) { - qemu_iovec_destroy(backing_qiov); - g_free(backing_qiov); - backing_qiov = NULL; - } + ret = qed_read_backing_file(s, pos, &qiov); if (ret) { goto out; @@ -1339,8 +1294,7 @@ static int coroutine_fn qed_aio_read_data(void *opaque, int ret, qemu_iovec_memset(&acb->cur_qiov, 0, 0, acb->cur_qiov.size); r = 0; } else if (ret != QED_CLUSTER_FOUND) { - r = qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov, - &acb->backing_qiov); + r = qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov); } else { BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); r = bdrv_co_preadv(bs->file, offset, acb->cur_qiov.size, @@ -1365,12 +1319,6 @@ static int coroutine_fn qed_aio_next_io(QEDAIOCB *acb) while (1) { trace_qed_aio_next_io(s, acb, 0, acb->cur_pos + acb->cur_qiov.size); - if (acb->backing_qiov) { - qemu_iovec_destroy(acb->backing_qiov); - g_free(acb->backing_qiov); - acb->backing_qiov = NULL; - } - acb->qiov_offset += acb->cur_qiov.size; acb->cur_pos += acb->cur_qiov.size; qemu_iovec_reset(&acb->cur_qiov); @@ -1514,7 +1462,6 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) memset(bdi, 0, sizeof(*bdi)); bdi->cluster_size = s->header.cluster_size; bdi->is_dirty = s->header.features & QED_F_NEED_CHECK; - bdi->unallocated_blocks_are_zero = true; return 0; } diff --git a/block/qed.h b/block/qed.h index 42c115d822..3d12bf78d4 100644 --- a/block/qed.h +++ b/block/qed.h @@ -140,7 +140,6 @@ typedef struct QEDAIOCB { /* Current cluster scatter-gather list */ QEMUIOVector cur_qiov; - QEMUIOVector *backing_qiov; uint64_t cur_pos; /* position on block device, in bytes */ uint64_t cur_cluster; /* cluster offset in image file */ unsigned int cur_nclusters; /* number of clusters being accessed */ diff --git a/block/vdi.c b/block/vdi.c index 2f506a01ba..c4527a9d8c 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -334,7 +334,6 @@ static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) logout("\n"); bdi->cluster_size = s->block_size; bdi->vm_state_offset = 0; - bdi->unallocated_blocks_are_zero = true; return 0; } @@ -536,7 +535,7 @@ static int coroutine_fn vdi_co_block_status(BlockDriverState *bs, *pnum = MIN(s->block_size - index_in_block, bytes); result = VDI_IS_ALLOCATED(bmap_entry); if (!result) { - return 0; + return BDRV_BLOCK_ZERO; } *map = s->header.offset_data + (uint64_t)bmap_entry * s->block_size + diff --git a/block/vhdx.c b/block/vhdx.c index fa9e544a5e..645dc4b4f4 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1164,9 +1164,6 @@ static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) bdi->cluster_size = s->block_size; - bdi->unallocated_blocks_are_zero = - (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) == 0; - return 0; } diff --git a/block/vpc.c b/block/vpc.c index c055591641..01fcd37e3c 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -606,7 +606,6 @@ static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) bdi->cluster_size = s->block_size; } - bdi->unallocated_blocks_are_zero = true; return 0; } @@ -745,7 +744,7 @@ static int coroutine_fn vpc_co_block_status(BlockDriverState *bs, image_offset = get_image_offset(bs, offset, false, NULL); allocated = (image_offset != -1); *pnum = 0; - ret = 0; + ret = BDRV_BLOCK_ZERO; do { /* All sectors in a block are contiguous (without using the bitmap) */ diff --git a/block/vvfat.c b/block/vvfat.c index c65a98e3ee..2eb8cbb19f 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -140,48 +140,16 @@ static inline void* array_insert(array_t* array,unsigned int index,unsigned int return array->pointer+index*array->item_size; } -/* this performs a "roll", so that the element which was at index_from becomes - * index_to, but the order of all other elements is preserved. */ -static inline int array_roll(array_t* array,int index_to,int index_from,int count) -{ - char* buf; - char* from; - char* to; - int is; - - if(!array || - index_to<0 || index_to>=array->next || - index_from<0 || index_from>=array->next) - return -1; - - if(index_to==index_from) - return 0; - - is=array->item_size; - from=array->pointer+index_from*is; - to=array->pointer+index_to*is; - buf=g_malloc(is*count); - memcpy(buf,from,is*count); - - if(index_to<index_from) - memmove(to+is*count,to,from-to); - else - memmove(from,from+is*count,to-from); - - memcpy(to,buf,is*count); - - g_free(buf); - - return 0; -} - static inline int array_remove_slice(array_t* array,int index, int count) { assert(index >=0); assert(count > 0); assert(index + count <= array->next); - if(array_roll(array,array->next-1,index,count)) - return -1; + + memmove(array->pointer + index * array->item_size, + array->pointer + (index + count) * array->item_size, + (array->next - index - count) * array->item_size); + array->next -= count; return 0; } @@ -520,12 +488,31 @@ static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin) direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff); } +static bool valid_filename(const unsigned char *name) +{ + unsigned char c; + if (!strcmp((const char*)name, ".") || !strcmp((const char*)name, "..")) { + return false; + } + for (; (c = *name); name++) { + if (!((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + c > 127 || + strchr("$%'-_@~`!(){}^#&.+,;=[]", c) != NULL)) + { + return false; + } + } + return true; +} + static uint8_t to_valid_short_char(gunichar c) { c = g_unichar_toupper(c); if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || - strchr("$%'-_@~`!(){}^#&", c) != 0) { + strchr("$%'-_@~`!(){}^#&", c) != NULL) { return c; } else { return 0; @@ -2098,6 +2085,10 @@ DLOG(fprintf(stderr, "check direntry %d:\n", i); print_direntry(direntries + i)) } lfn.checksum = 0x100; /* cannot use long name twice */ + if (!valid_filename(lfn.name)) { + fprintf(stderr, "Invalid file name\n"); + goto fail; + } if (path_len + 1 + lfn.len >= PATH_MAX) { fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name); goto fail; diff --git a/bsd-user/main.c b/bsd-user/main.c index 0bfe46cff9..ac40d79bfa 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -413,7 +413,11 @@ static void save_window(CPUSPARCState *env) save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); env->wim = new_wim; #else - save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); + /* + * cansave is zero if the spill trap handler is triggered by `save` and + * nonzero if triggered by a `flushw` + */ + save_window_offset(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2)); env->cansave++; env->canrestore--; #endif diff --git a/chardev/Makefile.objs b/chardev/Makefile.objs index d68e1347f9..3a58c9d329 100644 --- a/chardev/Makefile.objs +++ b/chardev/Makefile.objs @@ -18,8 +18,11 @@ chardev-obj-$(CONFIG_WIN32) += char-win.o chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o common-obj-y += msmouse.o wctablet.o testdev.o -common-obj-$(CONFIG_BRLAPI) += baum.o + +ifeq ($(CONFIG_BRLAPI),y) +common-obj-m += baum.o baum.o-cflags := $(SDL_CFLAGS) baum.o-libs := $(BRLAPI_LIBS) +endif common-obj-$(CONFIG_SPICE) += spice.o diff --git a/chardev/char.c b/chardev/char.c index e3051295ac..df697f3ce9 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -527,7 +527,7 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp) const ChardevClass *cc; char *typename = g_strdup_printf("chardev-%s", driver); - oc = object_class_by_name(typename); + oc = module_object_class_by_name(typename); g_free(typename); if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) { @@ -1575,6 +1575,10 @@ for opt do ;; --enable-vhost-user) vhost_user="yes" ;; + --disable-vhost-vdpa) vhost_vdpa="no" + ;; + --enable-vhost-vdpa) vhost_vdpa="yes" + ;; --disable-vhost-kernel) vhost_kernel="no" ;; --enable-vhost-kernel) vhost_kernel="yes" @@ -1883,6 +1887,7 @@ disabled with --disable-FEATURE, default is enabled if available: vhost-crypto vhost-user-crypto backend support vhost-kernel vhost kernel backend support vhost-user vhost-user backend support + vhost-vdpa vhost-vdpa kernel backend support spice spice rbd rados block device (rbd) libiscsi iscsi support @@ -2394,6 +2399,10 @@ test "$vhost_user" = "" && vhost_user=yes if test "$vhost_user" = "yes" && test "$mingw32" = "yes"; then error_exit "vhost-user isn't available on win32" fi +test "$vhost_vdpa" = "" && vhost_vdpa=$linux +if test "$vhost_vdpa" = "yes" && test "$linux" != "yes"; then + error_exit "vhost-vdpa is only available on Linux" +fi test "$vhost_kernel" = "" && vhost_kernel=$linux if test "$vhost_kernel" = "yes" && test "$linux" != "yes"; then error_exit "vhost-kernel is only available on Linux" @@ -2422,6 +2431,11 @@ test "$vhost_user_fs" = "" && vhost_user_fs=$vhost_user if test "$vhost_user_fs" = "yes" && test "$vhost_user" = "no"; then error_exit "--enable-vhost-user-fs requires --enable-vhost-user" fi +#vhost-vdpa backends +test "$vhost_net_vdpa" = "" && vhost_net_vdpa=$vhost_vdpa +if test "$vhost_net_vdpa" = "yes" && test "$vhost_vdpa" = "no"; then + error_exit "--enable-vhost-net-vdpa requires --enable-vhost-vdpa" +fi # OR the vhost-kernel and vhost-user values for simplicity if test "$vhost_net" = ""; then @@ -3194,6 +3208,13 @@ if ! check_include "ifaddrs.h" ; then have_ifaddrs_h=no fi +######################################### +# libdrm check +have_drm_h=no +if check_include "libdrm/drm.h" ; then + have_drm_h=yes +fi + ########################################## # VTE probe @@ -5134,10 +5155,14 @@ extern int openpty(int *am, int *as, char *name, void *termp, void *winp); int main(void) { return openpty(0, 0, 0, 0, 0); } EOF -if ! compile_prog "" "" ; then +have_openpty="no" +if compile_prog "" "" ; then + have_openpty="yes" +else if compile_prog "" "-lutil" ; then libs_softmmu="-lutil $libs_softmmu" libs_tools="-lutil $libs_tools" + have_openpty="yes" fi fi @@ -6936,6 +6961,7 @@ echo "vhost-scsi support $vhost_scsi" echo "vhost-vsock support $vhost_vsock" echo "vhost-user support $vhost_user" echo "vhost-user-fs support $vhost_user_fs" +echo "vhost-vdpa support $vhost_vdpa" echo "Trace backends $trace_backends" if have_backend "simple"; then echo "Trace output file $trace_file-<pid>" @@ -7377,9 +7403,15 @@ fi if test "$have_ifaddrs_h" = "yes" ; then echo "HAVE_IFADDRS_H=y" >> $config_host_mak fi +if test "$have_drm_h" = "yes" ; then + echo "HAVE_DRM_H=y" >> $config_host_mak +fi if test "$have_broken_size_max" = "yes" ; then echo "HAVE_BROKEN_SIZE_MAX=y" >> $config_host_mak fi +if test "$have_openpty" = "yes" ; then + echo "HAVE_OPENPTY=y" >> $config_host_mak +fi # Work around a system header bug with some kernel/XFS header # versions where they both try to define 'struct fsxattr': @@ -7437,6 +7469,9 @@ fi if test "$vhost_net_user" = "yes" ; then echo "CONFIG_VHOST_NET_USER=y" >> $config_host_mak fi +if test "$vhost_net_vdpa" = "yes" ; then + echo "CONFIG_VHOST_NET_VDPA=y" >> $config_host_mak +fi if test "$vhost_crypto" = "yes" ; then echo "CONFIG_VHOST_CRYPTO=y" >> $config_host_mak fi @@ -7452,6 +7487,9 @@ fi if test "$vhost_user" = "yes" ; then echo "CONFIG_VHOST_USER=y" >> $config_host_mak fi +if test "$vhost_vdpa" = "yes" ; then + echo "CONFIG_VHOST_VDPA=y" >> $config_host_mak +fi if test "$vhost_user_fs" = "yes" ; then echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak fi diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 4861db810c..564caa1094 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -32,6 +32,7 @@ #include "qemu/uuid.h" #include "qemu/coroutine.h" +#include "qemu/bitmap.h" /* * Reference for the LUKS format implemented here is @@ -70,6 +71,9 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot; #define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL +#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS 2000 +#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40 + static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = { 'L', 'U', 'K', 'S', 0xBA, 0xBE }; @@ -219,6 +223,9 @@ struct QCryptoBlockLUKS { /* Hash algorithm used in pbkdf2 function */ QCryptoHashAlgorithm hash_alg; + + /* Name of the secret that was used to open the image */ + char *secret; }; @@ -720,7 +727,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block, Error **errp) { QCryptoBlockLUKS *luks = block->opaque; - QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx]; + QCryptoBlockLUKSKeySlot *slot; g_autofree uint8_t *splitkey = NULL; size_t splitkeylen; g_autofree uint8_t *slotkey = NULL; @@ -730,6 +737,8 @@ qcrypto_block_luks_store_key(QCryptoBlock *block, uint64_t iters; int ret = -1; + assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); + slot = &luks->header.key_slots[slot_idx]; if (qcrypto_random_bytes(slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, errp) < 0) { @@ -890,7 +899,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block, Error **errp) { QCryptoBlockLUKS *luks = block->opaque; - const QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx]; + const QCryptoBlockLUKSKeySlot *slot; g_autofree uint8_t *splitkey = NULL; size_t splitkeylen; g_autofree uint8_t *possiblekey = NULL; @@ -900,6 +909,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block, g_autoptr(QCryptoIVGen) ivgen = NULL; size_t niv; + assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); + slot = &luks->header.key_slots[slot_idx]; if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) { return 0; } @@ -1069,6 +1080,126 @@ qcrypto_block_luks_find_key(QCryptoBlock *block, return -1; } +/* + * Returns true if a slot i is marked as active + * (contains encrypted copy of the master key) + */ +static bool +qcrypto_block_luks_slot_active(const QCryptoBlockLUKS *luks, + unsigned int slot_idx) +{ + uint32_t val; + + assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); + val = luks->header.key_slots[slot_idx].active; + return val == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED; +} + +/* + * Returns the number of slots that are marked as active + * (slots that contain encrypted copy of the master key) + */ +static unsigned int +qcrypto_block_luks_count_active_slots(const QCryptoBlockLUKS *luks) +{ + size_t i = 0; + unsigned int ret = 0; + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + if (qcrypto_block_luks_slot_active(luks, i)) { + ret++; + } + } + return ret; +} + +/* + * Finds first key slot which is not active + * Returns the key slot index, or -1 if it doesn't exist + */ +static int +qcrypto_block_luks_find_free_keyslot(const QCryptoBlockLUKS *luks) +{ + size_t i; + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + if (!qcrypto_block_luks_slot_active(luks, i)) { + return i; + } + } + return -1; +} + +/* + * Erases an keyslot given its index + * Returns: + * 0 if the keyslot was erased successfully + * -1 if a error occurred while erasing the keyslot + * + */ +static int +qcrypto_block_luks_erase_key(QCryptoBlock *block, + unsigned int slot_idx, + QCryptoBlockWriteFunc writefunc, + void *opaque, + Error **errp) +{ + QCryptoBlockLUKS *luks = block->opaque; + QCryptoBlockLUKSKeySlot *slot; + g_autofree uint8_t *garbagesplitkey = NULL; + size_t splitkeylen; + size_t i; + Error *local_err = NULL; + int ret; + + assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); + slot = &luks->header.key_slots[slot_idx]; + + splitkeylen = luks->header.master_key_len * slot->stripes; + assert(splitkeylen > 0); + + garbagesplitkey = g_new0(uint8_t, splitkeylen); + + /* Reset the key slot header */ + memset(slot->salt, 0, QCRYPTO_BLOCK_LUKS_SALT_LEN); + slot->iterations = 0; + slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED; + + ret = qcrypto_block_luks_store_header(block, writefunc, + opaque, &local_err); + + if (ret < 0) { + error_propagate(errp, local_err); + } + /* + * Now try to erase the key material, even if the header + * update failed + */ + for (i = 0; i < QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS; i++) { + if (qcrypto_random_bytes(garbagesplitkey, + splitkeylen, &local_err) < 0) { + /* + * If we failed to get the random data, still write + * at least zeros to the key slot at least once + */ + error_propagate(errp, local_err); + + if (i > 0) { + return -1; + } + } + if (writefunc(block, + slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + garbagesplitkey, + splitkeylen, + opaque, + &local_err) != splitkeylen) { + error_propagate(errp, local_err); + return -1; + } + } + return ret; +} static int qcrypto_block_luks_open(QCryptoBlock *block, @@ -1099,6 +1230,7 @@ qcrypto_block_luks_open(QCryptoBlock *block, luks = g_new0(QCryptoBlockLUKS, 1); block->opaque = luks; + luks->secret = g_strdup(options->u.luks.key_secret); if (qcrypto_block_luks_load_header(block, readfunc, opaque, errp) < 0) { goto fail; @@ -1164,6 +1296,7 @@ qcrypto_block_luks_open(QCryptoBlock *block, fail: qcrypto_block_free_cipher(block); qcrypto_ivgen_free(block->ivgen); + g_free(luks->secret); g_free(luks); return -1; } @@ -1204,7 +1337,7 @@ qcrypto_block_luks_create(QCryptoBlock *block, memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts)); if (!luks_opts.has_iter_time) { - luks_opts.iter_time = 2000; + luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS; } if (!luks_opts.has_cipher_alg) { luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256; @@ -1244,6 +1377,8 @@ qcrypto_block_luks_create(QCryptoBlock *block, optprefix ? optprefix : ""); goto error; } + luks->secret = g_strdup(options->u.luks.key_secret); + password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp); if (!password) { goto error; @@ -1471,10 +1606,278 @@ qcrypto_block_luks_create(QCryptoBlock *block, qcrypto_block_free_cipher(block); qcrypto_ivgen_free(block->ivgen); + g_free(luks->secret); g_free(luks); return -1; } +static int +qcrypto_block_luks_amend_add_keyslot(QCryptoBlock *block, + QCryptoBlockReadFunc readfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + QCryptoBlockAmendOptionsLUKS *opts_luks, + bool force, + Error **errp) +{ + QCryptoBlockLUKS *luks = block->opaque; + uint64_t iter_time = opts_luks->has_iter_time ? + opts_luks->iter_time : + QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS; + int keyslot; + g_autofree char *old_password = NULL; + g_autofree char *new_password = NULL; + g_autofree uint8_t *master_key = NULL; + + char *secret = opts_luks->has_secret ? opts_luks->secret : luks->secret; + + if (!opts_luks->has_new_secret) { + error_setg(errp, "'new-secret' is required to activate a keyslot"); + return -1; + } + if (opts_luks->has_old_secret) { + error_setg(errp, + "'old-secret' must not be given when activating keyslots"); + return -1; + } + + if (opts_luks->has_keyslot) { + keyslot = opts_luks->keyslot; + if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) { + error_setg(errp, + "Invalid keyslot %u specified, must be between 0 and %u", + keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1); + return -1; + } + } else { + keyslot = qcrypto_block_luks_find_free_keyslot(luks); + if (keyslot == -1) { + error_setg(errp, + "Can't add a keyslot - all keyslots are in use"); + return -1; + } + } + + if (!force && qcrypto_block_luks_slot_active(luks, keyslot)) { + error_setg(errp, + "Refusing to overwrite active keyslot %i - " + "please erase it first", + keyslot); + return -1; + } + + /* Locate the password that will be used to retrieve the master key */ + old_password = qcrypto_secret_lookup_as_utf8(secret, errp); + if (!old_password) { + return -1; + } + + /* Retrieve the master key */ + master_key = g_new0(uint8_t, luks->header.master_key_len); + + if (qcrypto_block_luks_find_key(block, old_password, master_key, + readfunc, opaque, errp) < 0) { + error_append_hint(errp, "Failed to retrieve the master key"); + return -1; + } + + /* Locate the new password*/ + new_password = qcrypto_secret_lookup_as_utf8(opts_luks->new_secret, errp); + if (!new_password) { + return -1; + } + + /* Now set the new keyslots */ + if (qcrypto_block_luks_store_key(block, keyslot, new_password, master_key, + iter_time, writefunc, opaque, errp)) { + error_append_hint(errp, "Failed to write to keyslot %i", keyslot); + return -1; + } + return 0; +} + +static int +qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block, + QCryptoBlockReadFunc readfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + QCryptoBlockAmendOptionsLUKS *opts_luks, + bool force, + Error **errp) +{ + QCryptoBlockLUKS *luks = block->opaque; + g_autofree uint8_t *tmpkey = NULL; + g_autofree char *old_password = NULL; + + if (opts_luks->has_new_secret) { + error_setg(errp, + "'new-secret' must not be given when erasing keyslots"); + return -1; + } + if (opts_luks->has_iter_time) { + error_setg(errp, + "'iter-time' must not be given when erasing keyslots"); + return -1; + } + if (opts_luks->has_secret) { + error_setg(errp, + "'secret' must not be given when erasing keyslots"); + return -1; + } + + /* Load the old password if given */ + if (opts_luks->has_old_secret) { + old_password = qcrypto_secret_lookup_as_utf8(opts_luks->old_secret, + errp); + if (!old_password) { + return -1; + } + + /* + * Allocate a temporary key buffer that we will need when + * checking if slot matches the given old password + */ + tmpkey = g_new0(uint8_t, luks->header.master_key_len); + } + + /* Erase an explicitly given keyslot */ + if (opts_luks->has_keyslot) { + int keyslot = opts_luks->keyslot; + + if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) { + error_setg(errp, + "Invalid keyslot %i specified, must be between 0 and %i", + keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1); + return -1; + } + + if (opts_luks->has_old_secret) { + int rv = qcrypto_block_luks_load_key(block, + keyslot, + old_password, + tmpkey, + readfunc, + opaque, + errp); + if (rv == -1) { + return -1; + } else if (rv == 0) { + error_setg(errp, + "Given keyslot %i doesn't contain the given " + "old password for erase operation", + keyslot); + return -1; + } + } + + if (!force && !qcrypto_block_luks_slot_active(luks, keyslot)) { + error_setg(errp, + "Given keyslot %i is already erased (inactive) ", + keyslot); + return -1; + } + + if (!force && qcrypto_block_luks_count_active_slots(luks) == 1) { + error_setg(errp, + "Attempt to erase the only active keyslot %i " + "which will erase all the data in the image " + "irreversibly - refusing operation", + keyslot); + return -1; + } + + if (qcrypto_block_luks_erase_key(block, keyslot, + writefunc, opaque, errp)) { + error_append_hint(errp, "Failed to erase keyslot %i", keyslot); + return -1; + } + + /* Erase all keyslots that match the given old password */ + } else if (opts_luks->has_old_secret) { + + unsigned long slots_to_erase_bitmap = 0; + size_t i; + int slot_count; + + assert(QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS <= + sizeof(slots_to_erase_bitmap) * 8); + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + int rv = qcrypto_block_luks_load_key(block, + i, + old_password, + tmpkey, + readfunc, + opaque, + errp); + if (rv == -1) { + return -1; + } else if (rv == 1) { + bitmap_set(&slots_to_erase_bitmap, i, 1); + } + } + + slot_count = bitmap_count_one(&slots_to_erase_bitmap, + QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); + if (slot_count == 0) { + error_setg(errp, + "No keyslots match given (old) password for erase operation"); + return -1; + } + + if (!force && + slot_count == qcrypto_block_luks_count_active_slots(luks)) { + error_setg(errp, + "All the active keyslots match the (old) password that " + "was given and erasing them will erase all the data in " + "the image irreversibly - refusing operation"); + return -1; + } + + /* Now apply the update */ + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + if (!test_bit(i, &slots_to_erase_bitmap)) { + continue; + } + if (qcrypto_block_luks_erase_key(block, i, writefunc, + opaque, errp)) { + error_append_hint(errp, "Failed to erase keyslot %zu", i); + return -1; + } + } + } else { + error_setg(errp, + "To erase keyslot(s), either explicit keyslot index " + "or the password currently contained in them must be given"); + return -1; + } + return 0; +} + +static int +qcrypto_block_luks_amend_options(QCryptoBlock *block, + QCryptoBlockReadFunc readfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + QCryptoBlockAmendOptions *options, + bool force, + Error **errp) +{ + QCryptoBlockAmendOptionsLUKS *opts_luks = &options->u.luks; + + switch (opts_luks->state) { + case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE: + return qcrypto_block_luks_amend_add_keyslot(block, readfunc, + writefunc, opaque, + opts_luks, force, errp); + case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE: + return qcrypto_block_luks_amend_erase_keyslots(block, readfunc, + writefunc, opaque, + opts_luks, force, errp); + default: + g_assert_not_reached(); + } +} static int qcrypto_block_luks_get_info(QCryptoBlock *block, QCryptoBlockInfo *info, @@ -1523,7 +1926,11 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block, static void qcrypto_block_luks_cleanup(QCryptoBlock *block) { - g_free(block->opaque); + QCryptoBlockLUKS *luks = block->opaque; + if (luks) { + g_free(luks->secret); + g_free(luks); + } } @@ -1560,6 +1967,7 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block, const QCryptoBlockDriver qcrypto_block_driver_luks = { .open = qcrypto_block_luks_open, .create = qcrypto_block_luks_create, + .amend = qcrypto_block_luks_amend_options, .get_info = qcrypto_block_luks_get_info, .cleanup = qcrypto_block_luks_cleanup, .decrypt = qcrypto_block_luks_decrypt, diff --git a/crypto/block.c b/crypto/block.c index 6f42b32f1e..eb057948b5 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -150,6 +150,35 @@ qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts, return crypto != NULL; } +int qcrypto_block_amend_options(QCryptoBlock *block, + QCryptoBlockReadFunc readfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + QCryptoBlockAmendOptions *options, + bool force, + Error **errp) +{ + if (options->format != block->format) { + error_setg(errp, + "Cannot amend encryption format"); + return -1; + } + + if (!block->driver->amend) { + error_setg(errp, + "Crypto format %s doesn't support format options amendment", + QCryptoBlockFormat_str(block->format)); + return -1; + } + + return block->driver->amend(block, + readfunc, + writefunc, + opaque, + options, + force, + errp); +} QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block, Error **errp) diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h index 71c59cb542..3c7ccea504 100644 --- a/crypto/blockpriv.h +++ b/crypto/blockpriv.h @@ -62,6 +62,14 @@ struct QCryptoBlockDriver { void *opaque, Error **errp); + int (*amend)(QCryptoBlock *block, + QCryptoBlockReadFunc readfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + QCryptoBlockAmendOptions *options, + bool force, + Error **errp); + int (*get_info)(QCryptoBlock *block, QCryptoBlockInfo *info, Error **errp); diff --git a/docs/interop/index.rst b/docs/interop/index.rst index 049387ac6d..006f986420 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -20,3 +20,4 @@ Contents: qemu-ga vhost-user vhost-user-gpu + vhost-vdpa diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 688b7c6900..10e3e3475e 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -816,6 +816,7 @@ Protocol features #define VHOST_USER_PROTOCOL_F_RESET_DEVICE 13 #define VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS 14 #define VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS 15 + #define VHOST_USER_PROTOCOL_F_STATUS 16 Master message types -------------------- @@ -1307,6 +1308,29 @@ Master message types ``VHOST_USER_ADD_MEM_REG`` message, this message is used to set and update the memory tables of the slave device. +``VHOST_USER_SET_STATUS`` + :id: 39 + :equivalent ioctl: VHOST_VDPA_SET_STATUS + :slave payload: N/A + :master payload: ``u64`` + + When the ``VHOST_USER_PROTOCOL_F_STATUS`` protocol feature has been + successfully negotiated, this message is submitted by the master to + notify the backend with updated device status as defined in the Virtio + specification. + +``VHOST_USER_GET_STATUS`` + :id: 40 + :equivalent ioctl: VHOST_VDPA_GET_STATUS + :slave payload: ``u64`` + :master payload: N/A + + When the ``VHOST_USER_PROTOCOL_F_STATUS`` protocol feature has been + successfully negotiated, this message is submitted by the master to + query the backend for its device status as defined in the Virtio + specification. + + Slave message types ------------------- diff --git a/docs/interop/vhost-vdpa.rst b/docs/interop/vhost-vdpa.rst new file mode 100644 index 0000000000..0c70ba01bc --- /dev/null +++ b/docs/interop/vhost-vdpa.rst @@ -0,0 +1,17 @@ +===================== +Vhost-vdpa Protocol +===================== + +Introduction +============= +vDPA(Virtual data path acceleration) device is a device that uses +a datapath which complies with the virtio specifications with vendor +specific control path. vDPA devices can be both physically located on +the hardware or emulated by software. + +This document describes the vDPA support in qemu + +Here is the kernel commit here +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4c8cf31885f69e86be0b5b9e6677a26797365e1d + +TODO : More information will add later diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 843ae71fc6..58a9aeb851 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -82,6 +82,15 @@ should specify an ``audiodev=`` property. Additionally, when using vnc, you should specify an ``audiodev=`` propery if you plan to transmit audio through the VNC protocol. +Creating sound card devices using ``-soundhw`` (since 5.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Sound card devices should be created using ``-device`` instead. The +names are the same for most devices. The exceptions are ``hda`` which +needs two devices (``-device intel-hda -device hda-duplex``) and +``pcspk`` which can be activated using ``-machine +pcspk-audiodev=<name>``. + ``-mon ...,control=readline,pretty=on|off`` (since 4.1) ''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -404,6 +413,17 @@ The above, converted to the current supported format:: json:{"file.driver":"rbd", "file.pool":"rbd", "file.image":"name"} +linux-user mode CPUs +-------------------- + +``tilegx`` CPUs (since 5.1.0) +''''''''''''''''''''''''''''' + +The ``tilegx`` guest CPU support (which was only implemented in +linux-user mode) is deprecated and will be removed in a future version +of QEMU. Support for this CPU was removed from the upstream Linux +kernel in 2018, and has also been dropped from glibc. + Related binaries ---------------- diff --git a/docs/system/target-i386-desc.rst.inc b/docs/system/target-i386-desc.rst.inc index 47a169e0ae..7d1fffacbe 100644 --- a/docs/system/target-i386-desc.rst.inc +++ b/docs/system/target-i386-desc.rst.inc @@ -31,6 +31,8 @@ The QEMU PC System emulator simulates the following peripherals: - CS4231A compatible sound card +- PC speaker + - PCI UHCI, OHCI, EHCI or XHCI USB controller and a virtual USB-1.1 hub. @@ -49,7 +51,7 @@ must be told to not have parallel ports to have working GUS. .. parsed-literal:: - |qemu_system_x86| dos.img -soundhw gus -parallel none + |qemu_system_x86| dos.img -device gus -parallel none Alternatively: @@ -60,3 +62,12 @@ Alternatively: Or some other unclaimed IRQ. CS4231A is the chip used in Windows Sound System and GUSMAX products + +The PC speaker audio device can be configured using the pcspk-audiodev +machine property, i.e. + +.. parsed-literal:: + + |qemu_system_x86| some.img \ + -audiodev <backend>,id=<name> \ + -machine pcspk-audiodev=<name> diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index 7f0737488a..e33f5575e3 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -253,11 +253,14 @@ Command description: .. program:: qemu-img-commands -.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] -o OPTIONS FILENAME +.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] [--force] -o OPTIONS FILENAME Amends the image format specific *OPTIONS* for the image file *FILENAME*. Not all file formats support this operation. + --force allows some unsafe operations. Currently for -f luks, it allows to + erase the last encryption key, and to overwrite an active encryption key. + .. option:: bench [-c COUNT] [-d DEPTH] [-f FMT] [--flush-interval=FLUSH_INTERVAL] [-i AIO] [-n] [--no-drain] [-o OFFSET] [--pattern=PATTERN] [-q] [-s BUFFER_SIZE] [-S STEP_SIZE] [-t CACHE] [-w] [-U] FILENAME Run a simple sequential I/O benchmark on the specified image. If ``-w`` is diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst index 378594c422..824e713491 100644 --- a/docs/tools/virtiofsd.rst +++ b/docs/tools/virtiofsd.rst @@ -54,6 +54,11 @@ Options * flock|no_flock - Enable/disable flock. The default is ``no_flock``. + * modcaps=CAPLIST + Modify the list of capabilities allowed; CAPLIST is a colon separated + list of capabilities, each preceded by either + or -, e.g. + ''+sys_admin:-chown''. + * log_level=LEVEL - Print only log messages matching LEVEL or more severe. LEVEL is one of ``err``, ``warn``, ``info``, or ``debug``. The default is ``info``. @@ -4115,4 +4115,56 @@ void mtree_print_dispatch(AddressSpaceDispatch *d, MemoryRegion *root) } } +/* + * If positive, discarding RAM is disabled. If negative, discarding RAM is + * required to work and cannot be disabled. + */ +static int ram_block_discard_disabled; + +int ram_block_discard_disable(bool state) +{ + int old; + + if (!state) { + atomic_dec(&ram_block_discard_disabled); + return 0; + } + + do { + old = atomic_read(&ram_block_discard_disabled); + if (old < 0) { + return -EBUSY; + } + } while (atomic_cmpxchg(&ram_block_discard_disabled, old, old + 1) != old); + return 0; +} + +int ram_block_discard_require(bool state) +{ + int old; + + if (!state) { + atomic_inc(&ram_block_discard_disabled); + return 0; + } + + do { + old = atomic_read(&ram_block_discard_disabled); + if (old > 0) { + return -EBUSY; + } + } while (atomic_cmpxchg(&ram_block_discard_disabled, old, old - 1) != old); + return 0; +} + +bool ram_block_discard_is_disabled(void) +{ + return atomic_read(&ram_block_discard_disabled) > 0; +} + +bool ram_block_discard_is_required(void) +{ + return atomic_read(&ram_block_discard_disabled) < 0; +} + #endif diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 4cbe5e4e57..14b7ea4eb6 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -43,4 +43,6 @@ devices-dirs-y += smbios/ endif common-obj-y += $(devices-dirs-y) +common-obj-m += display/ +common-obj-m += usb/ obj-y += $(devices-dirs-y) diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 6446034711..51b2f256ec 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -428,6 +428,9 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) }; object_property_set_uint(OBJECT(&s->eth[i]), + s->phy_num[i], + "phy-num", &error_abort); + object_property_set_uint(OBJECT(&s->eth[i]), FSL_IMX6UL_ETH_NUM_TX_RINGS, "tx-ring-num", &error_abort); qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]); @@ -607,10 +610,17 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_OCRAM_ALIAS_ADDR, &s->ocram_alias); } +static Property fsl_imx6ul_properties[] = { + DEFINE_PROP_UINT32("fec1-phy-num", FslIMX6ULState, phy_num[0], 0), + DEFINE_PROP_UINT32("fec2-phy-num", FslIMX6ULState, phy_num[1], 1), + DEFINE_PROP_END_OF_LIST(), +}; + static void fsl_imx6ul_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, fsl_imx6ul_properties); dc->realize = fsl_imx6ul_realize; dc->desc = "i.MX6UL SOC"; /* Reason: Uses serial_hds and nd_table in realize() directly */ diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index 2f845cedfc..9033d3f8f3 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -40,6 +40,8 @@ static void mcimx6ul_evk_init(MachineState *machine) s = FSL_IMX6UL(object_new(TYPE_FSL_IMX6UL)); object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); + object_property_set_uint(OBJECT(s), 2, "fec1-phy-num", &error_fatal); + object_property_set_uint(OBJECT(s), 1, "fec2-phy-num", &error_fatal); qdev_realize(DEVICE(s), NULL, &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_MMDC_ADDR, diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index 105c5e63f2..ceee6aa48d 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/log.h" #include "cpu.h" #include "hw/arm/pxa.h" #include "hw/sysbus.h" @@ -166,7 +167,9 @@ static uint64_t pxa2xx_pic_mem_read(void *opaque, hwaddr offset, case ICHP: /* Highest Priority register */ return pxa2xx_pic_highest(s); default: - printf("%s: Bad register offset " REG_FMT "\n", __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "pxa2xx_pic_mem_read: bad register offset 0x%" HWADDR_PRIx + "\n", offset); return 0; } } @@ -199,7 +202,9 @@ static void pxa2xx_pic_mem_write(void *opaque, hwaddr offset, s->priority[32 + ((offset - IPR32) >> 2)] = value & 0x8000003f; break; default: - printf("%s: Bad register offset " REG_FMT "\n", __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "pxa2xx_pic_mem_write: bad register offset 0x%" + HWADDR_PRIx "\n", offset); return; } pxa2xx_pic_update(opaque); diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index fc18212e68..f020aff974 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -23,36 +23,66 @@ #include "hw/ssi/ssi.h" #include "hw/block/flash.h" #include "qemu/timer.h" +#include "qemu/log.h" #include "hw/arm/sharpsl.h" #include "ui/console.h" #include "hw/audio/wm8750.h" #include "audio/audio.h" #include "hw/boards.h" #include "hw/sysbus.h" +#include "hw/misc/max111x.h" #include "migration/vmstate.h" #include "exec/address-spaces.h" #include "cpu.h" -#undef REG_FMT -#define REG_FMT "0x%02lx" +enum spitz_model_e { spitz, akita, borzoi, terrier }; + +typedef struct { + MachineClass parent; + enum spitz_model_e model; + int arm_id; +} SpitzMachineClass; + +typedef struct { + MachineState parent; + PXA2xxState *mpu; + DeviceState *mux; + DeviceState *lcdtg; + DeviceState *ads7846; + DeviceState *max1111; + DeviceState *scp0; + DeviceState *scp1; + DeviceState *misc_gpio; +} SpitzMachineState; + +#define TYPE_SPITZ_MACHINE "spitz-common" +#define SPITZ_MACHINE(obj) \ + OBJECT_CHECK(SpitzMachineState, obj, TYPE_SPITZ_MACHINE) +#define SPITZ_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SpitzMachineClass, obj, TYPE_SPITZ_MACHINE) +#define SPITZ_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SpitzMachineClass, klass, TYPE_SPITZ_MACHINE) + +#define zaurus_printf(format, ...) \ + fprintf(stderr, "%s: " format, __func__, ##__VA_ARGS__) /* Spitz Flash */ -#define FLASH_BASE 0x0c000000 -#define FLASH_ECCLPLB 0x00 /* Line parity 7 - 0 bit */ -#define FLASH_ECCLPUB 0x04 /* Line parity 15 - 8 bit */ -#define FLASH_ECCCP 0x08 /* Column parity 5 - 0 bit */ -#define FLASH_ECCCNTR 0x0c /* ECC byte counter */ -#define FLASH_ECCCLRR 0x10 /* Clear ECC */ -#define FLASH_FLASHIO 0x14 /* Flash I/O */ -#define FLASH_FLASHCTL 0x18 /* Flash Control */ - -#define FLASHCTL_CE0 (1 << 0) -#define FLASHCTL_CLE (1 << 1) -#define FLASHCTL_ALE (1 << 2) -#define FLASHCTL_WP (1 << 3) -#define FLASHCTL_CE1 (1 << 4) -#define FLASHCTL_RYBY (1 << 5) -#define FLASHCTL_NCE (FLASHCTL_CE0 | FLASHCTL_CE1) +#define FLASH_BASE 0x0c000000 +#define FLASH_ECCLPLB 0x00 /* Line parity 7 - 0 bit */ +#define FLASH_ECCLPUB 0x04 /* Line parity 15 - 8 bit */ +#define FLASH_ECCCP 0x08 /* Column parity 5 - 0 bit */ +#define FLASH_ECCCNTR 0x0c /* ECC byte counter */ +#define FLASH_ECCCLRR 0x10 /* Clear ECC */ +#define FLASH_FLASHIO 0x14 /* Flash I/O */ +#define FLASH_FLASHCTL 0x18 /* Flash Control */ + +#define FLASHCTL_CE0 (1 << 0) +#define FLASHCTL_CLE (1 << 1) +#define FLASHCTL_ALE (1 << 2) +#define FLASHCTL_WP (1 << 3) +#define FLASHCTL_CE1 (1 << 4) +#define FLASHCTL_RYBY (1 << 5) +#define FLASHCTL_NCE (FLASHCTL_CE0 | FLASHCTL_CE1) #define TYPE_SL_NAND "sl-nand" #define SL_NAND(obj) OBJECT_CHECK(SLNANDState, (obj), TYPE_SL_NAND) @@ -74,12 +104,12 @@ static uint64_t sl_read(void *opaque, hwaddr addr, unsigned size) int ryby; switch (addr) { -#define BSHR(byte, from, to) ((s->ecc.lp[byte] >> (from - to)) & (1 << to)) +#define BSHR(byte, from, to) ((s->ecc.lp[byte] >> (from - to)) & (1 << to)) case FLASH_ECCLPLB: return BSHR(0, 4, 0) | BSHR(0, 5, 2) | BSHR(0, 6, 4) | BSHR(0, 7, 6) | BSHR(1, 4, 1) | BSHR(1, 5, 3) | BSHR(1, 6, 5) | BSHR(1, 7, 7); -#define BSHL(byte, from, to) ((s->ecc.lp[byte] << (to - from)) & (1 << to)) +#define BSHL(byte, from, to) ((s->ecc.lp[byte] << (to - from)) & (1 << to)) case FLASH_ECCLPUB: return BSHL(0, 0, 0) | BSHL(0, 1, 2) | BSHL(0, 2, 4) | BSHL(0, 3, 6) | BSHL(1, 0, 1) | BSHL(1, 1, 3) | BSHL(1, 2, 5) | BSHL(1, 3, 7); @@ -105,7 +135,9 @@ static uint64_t sl_read(void *opaque, hwaddr addr, unsigned size) return ecc_digest(&s->ecc, nand_getio(s->nand)); default: - zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); + qemu_log_mask(LOG_GUEST_ERROR, + "sl_read: bad register offset 0x%02" HWADDR_PRIx "\n", + addr); } return 0; } @@ -136,7 +168,9 @@ static void sl_write(void *opaque, hwaddr addr, break; default: - zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); + qemu_log_mask(LOG_GUEST_ERROR, + "sl_write: bad register offset 0x%02" HWADDR_PRIx "\n", + addr); } } @@ -191,8 +225,8 @@ static void sl_nand_realize(DeviceState *dev, Error **errp) /* Spitz Keyboard */ -#define SPITZ_KEY_STROBE_NUM 11 -#define SPITZ_KEY_SENSE_NUM 7 +#define SPITZ_KEY_STROBE_NUM 11 +#define SPITZ_KEY_SENSE_NUM 7 static const int spitz_gpio_key_sense[SPITZ_KEY_SENSE_NUM] = { 12, 17, 91, 34, 36, 38, 39 @@ -214,11 +248,11 @@ static int spitz_keymap[SPITZ_KEY_SENSE_NUM + 1][SPITZ_KEY_STROBE_NUM] = { { 0x52, 0x43, 0x01, 0x47, 0x49, -1 , -1 , -1 , -1 , -1 , -1 }, }; -#define SPITZ_GPIO_AK_INT 13 /* Remote control */ -#define SPITZ_GPIO_SYNC 16 /* Sync button */ -#define SPITZ_GPIO_ON_KEY 95 /* Power button */ -#define SPITZ_GPIO_SWA 97 /* Lid */ -#define SPITZ_GPIO_SWB 96 /* Tablet mode */ +#define SPITZ_GPIO_AK_INT 13 /* Remote control */ +#define SPITZ_GPIO_SYNC 16 /* Sync button */ +#define SPITZ_GPIO_ON_KEY 95 /* Power button */ +#define SPITZ_GPIO_SWA 97 /* Lid */ +#define SPITZ_GPIO_SWB 96 /* Tablet mode */ /* The special buttons are mapped to unused keys */ static const int spitz_gpiomap[5] = { @@ -300,7 +334,7 @@ static void spitz_keyboard_keydown(SpitzKeyboardState *s, int keycode) #define SPITZ_MOD_CTRL (1 << 8) #define SPITZ_MOD_FN (1 << 9) -#define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c +#define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c static void spitz_keyboard_handler(void *opaque, int keycode) { @@ -308,25 +342,25 @@ static void spitz_keyboard_handler(void *opaque, int keycode) uint16_t code; int mapcode; switch (keycode) { - case 0x2a: /* Left Shift */ + case 0x2a: /* Left Shift */ s->modifiers |= 1; break; case 0xaa: s->modifiers &= ~1; break; - case 0x36: /* Right Shift */ + case 0x36: /* Right Shift */ s->modifiers |= 2; break; case 0xb6: s->modifiers &= ~2; break; - case 0x1d: /* Control */ + case 0x1d: /* Control */ s->modifiers |= 4; break; case 0x9d: s->modifiers &= ~4; break; - case 0x38: /* Alt */ + case 0x38: /* Alt */ s->modifiers |= 8; break; case 0xb8: @@ -536,14 +570,17 @@ static void spitz_keyboard_realize(DeviceState *dev, Error **errp) /* LCD backlight controller */ -#define LCDTG_RESCTL 0x00 -#define LCDTG_PHACTRL 0x01 -#define LCDTG_DUTYCTRL 0x02 -#define LCDTG_POWERREG0 0x03 -#define LCDTG_POWERREG1 0x04 -#define LCDTG_GPOR3 0x05 -#define LCDTG_PICTRL 0x06 -#define LCDTG_POLCTRL 0x07 +#define LCDTG_RESCTL 0x00 +#define LCDTG_PHACTRL 0x01 +#define LCDTG_DUTYCTRL 0x02 +#define LCDTG_POWERREG0 0x03 +#define LCDTG_POWERREG1 0x04 +#define LCDTG_GPOR3 0x05 +#define LCDTG_PICTRL 0x06 +#define LCDTG_POLCTRL 0x07 + +#define TYPE_SPITZ_LCDTG "spitz-lcdtg" +#define SPITZ_LCDTG(obj) OBJECT_CHECK(SpitzLCDTG, (obj), TYPE_SPITZ_LCDTG) typedef struct { SSISlave ssidev; @@ -559,12 +596,9 @@ static void spitz_bl_update(SpitzLCDTG *s) zaurus_printf("LCD Backlight now off\n"); } -/* FIXME: Implement GPIO properly and remove this hack. */ -static SpitzLCDTG *spitz_lcdtg; - static inline void spitz_bl_bit5(void *opaque, int line, int level) { - SpitzLCDTG *s = spitz_lcdtg; + SpitzLCDTG *s = opaque; int prev = s->bl_intensity; if (level) @@ -578,14 +612,14 @@ static inline void spitz_bl_bit5(void *opaque, int line, int level) static inline void spitz_bl_power(void *opaque, int line, int level) { - SpitzLCDTG *s = spitz_lcdtg; + SpitzLCDTG *s = opaque; s->bl_power = !!level; spitz_bl_update(s); } static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value) { - SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev); + SpitzLCDTG *s = SPITZ_LCDTG(dev); int addr; addr = value >> 5; value &= 0x1f; @@ -612,25 +646,29 @@ static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value) return 0; } -static void spitz_lcdtg_realize(SSISlave *dev, Error **errp) +static void spitz_lcdtg_realize(SSISlave *ssi, Error **errp) { - SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev); + SpitzLCDTG *s = SPITZ_LCDTG(ssi); + DeviceState *dev = DEVICE(s); - spitz_lcdtg = s; s->bl_power = 0; s->bl_intensity = 0x20; + + qdev_init_gpio_in_named(dev, spitz_bl_bit5, "bl_bit5", 1); + qdev_init_gpio_in_named(dev, spitz_bl_power, "bl_power", 1); } /* SSP devices */ -#define CORGI_SSP_PORT 2 +#define CORGI_SSP_PORT 2 -#define SPITZ_GPIO_LCDCON_CS 53 -#define SPITZ_GPIO_ADS7846_CS 14 -#define SPITZ_GPIO_MAX1111_CS 20 -#define SPITZ_GPIO_TP_INT 11 +#define SPITZ_GPIO_LCDCON_CS 53 +#define SPITZ_GPIO_ADS7846_CS 14 +#define SPITZ_GPIO_MAX1111_CS 20 +#define SPITZ_GPIO_TP_INT 11 -static DeviceState *max1111; +#define TYPE_CORGI_SSP "corgi-ssp" +#define CORGI_SSP(obj) OBJECT_CHECK(CorgiSSPState, (obj), TYPE_CORGI_SSP) /* "Demux" the signal based on current chipselect */ typedef struct { @@ -641,7 +679,7 @@ typedef struct { static uint32_t corgi_ssp_transfer(SSISlave *dev, uint32_t value) { - CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, dev); + CorgiSSPState *s = CORGI_SSP(dev); int i; for (i = 0; i < 3; i++) { @@ -659,29 +697,18 @@ static void corgi_ssp_gpio_cs(void *opaque, int line, int level) s->enable[line] = !level; } -#define MAX1111_BATT_VOLT 1 -#define MAX1111_BATT_TEMP 2 -#define MAX1111_ACIN_VOLT 3 - -#define SPITZ_BATTERY_TEMP 0xe0 /* About 2.9V */ -#define SPITZ_BATTERY_VOLT 0xd0 /* About 4.0V */ -#define SPITZ_CHARGEON_ACIN 0x80 /* About 5.0V */ +#define MAX1111_BATT_VOLT 1 +#define MAX1111_BATT_TEMP 2 +#define MAX1111_ACIN_VOLT 3 -static void spitz_adc_temp_on(void *opaque, int line, int level) -{ - if (!max1111) - return; - - if (level) - max111x_set_input(max1111, MAX1111_BATT_TEMP, SPITZ_BATTERY_TEMP); - else - max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); -} +#define SPITZ_BATTERY_TEMP 0xe0 /* About 2.9V */ +#define SPITZ_BATTERY_VOLT 0xd0 /* About 4.0V */ +#define SPITZ_CHARGEON_ACIN 0x80 /* About 5.0V */ static void corgi_ssp_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); - CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, d); + CorgiSSPState *s = CORGI_SSP(d); qdev_init_gpio_in(dev, corgi_ssp_gpio_cs, 3); s->bus[0] = ssi_create_bus(dev, "ssi0"); @@ -689,34 +716,36 @@ static void corgi_ssp_realize(SSISlave *d, Error **errp) s->bus[2] = ssi_create_bus(dev, "ssi2"); } -static void spitz_ssp_attach(PXA2xxState *cpu) +static void spitz_ssp_attach(SpitzMachineState *sms) { - DeviceState *mux; - DeviceState *dev; void *bus; - mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp"); - - bus = qdev_get_child_bus(mux, "ssi0"); - ssi_create_slave(bus, "spitz-lcdtg"); - - bus = qdev_get_child_bus(mux, "ssi1"); - dev = ssi_create_slave(bus, "ads7846"); - qdev_connect_gpio_out(dev, 0, - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_TP_INT)); - - bus = qdev_get_child_bus(mux, "ssi2"); - max1111 = ssi_create_slave(bus, "max1111"); - max111x_set_input(max1111, MAX1111_BATT_VOLT, SPITZ_BATTERY_VOLT); - max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); - max111x_set_input(max1111, MAX1111_ACIN_VOLT, SPITZ_CHARGEON_ACIN); - - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_LCDCON_CS, - qdev_get_gpio_in(mux, 0)); - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ADS7846_CS, - qdev_get_gpio_in(mux, 1)); - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_MAX1111_CS, - qdev_get_gpio_in(mux, 2)); + sms->mux = ssi_create_slave(sms->mpu->ssp[CORGI_SSP_PORT - 1], + TYPE_CORGI_SSP); + + bus = qdev_get_child_bus(sms->mux, "ssi0"); + sms->lcdtg = ssi_create_slave(bus, TYPE_SPITZ_LCDTG); + + bus = qdev_get_child_bus(sms->mux, "ssi1"); + sms->ads7846 = ssi_create_slave(bus, "ads7846"); + qdev_connect_gpio_out(sms->ads7846, 0, + qdev_get_gpio_in(sms->mpu->gpio, SPITZ_GPIO_TP_INT)); + + bus = qdev_get_child_bus(sms->mux, "ssi2"); + sms->max1111 = qdev_new(TYPE_MAX_1111); + qdev_prop_set_uint8(sms->max1111, "input1" /* BATT_VOLT */, + SPITZ_BATTERY_VOLT); + qdev_prop_set_uint8(sms->max1111, "input2" /* BATT_TEMP */, 0); + qdev_prop_set_uint8(sms->max1111, "input3" /* ACIN_VOLT */, + SPITZ_CHARGEON_ACIN); + ssi_realize_and_unref(sms->max1111, bus, &error_fatal); + + qdev_connect_gpio_out(sms->mpu->gpio, SPITZ_GPIO_LCDCON_CS, + qdev_get_gpio_in(sms->mux, 0)); + qdev_connect_gpio_out(sms->mpu->gpio, SPITZ_GPIO_ADS7846_CS, + qdev_get_gpio_in(sms->mux, 1)); + qdev_connect_gpio_out(sms->mpu->gpio, SPITZ_GPIO_MAX1111_CS, + qdev_get_gpio_in(sms->mux, 2)); } /* CF Microdrive */ @@ -735,11 +764,11 @@ static void spitz_microdrive_attach(PXA2xxState *cpu, int slot) /* Wm8750 and Max7310 on I2C */ -#define AKITA_MAX_ADDR 0x18 -#define SPITZ_WM_ADDRL 0x1b -#define SPITZ_WM_ADDRH 0x1a +#define AKITA_MAX_ADDR 0x18 +#define SPITZ_WM_ADDRL 0x1b +#define SPITZ_WM_ADDRH 0x1a -#define SPITZ_GPIO_WM 5 +#define SPITZ_GPIO_WM 5 static void spitz_wm8750_addr(void *opaque, int line, int level) { @@ -779,75 +808,119 @@ static void spitz_akita_i2c_setup(PXA2xxState *cpu) /* Other peripherals */ -static void spitz_out_switch(void *opaque, int line, int level) +/* + * Encapsulation of some miscellaneous GPIO line behaviour for the Spitz boards. + * + * QEMU interface: + * + named GPIO inputs "green-led", "orange-led", "charging", "discharging": + * these currently just print messages that the line has been signalled + * + named GPIO input "adc-temp-on": set to cause the battery-temperature + * value to be passed to the max111x ADC + * + named GPIO output "adc-temp": the ADC value, to be wired up to the max111x + */ +#define TYPE_SPITZ_MISC_GPIO "spitz-misc-gpio" +#define SPITZ_MISC_GPIO(obj) \ + OBJECT_CHECK(SpitzMiscGPIOState, (obj), TYPE_SPITZ_MISC_GPIO) + +typedef struct SpitzMiscGPIOState { + SysBusDevice parent_obj; + + qemu_irq adc_value; +} SpitzMiscGPIOState; + +static void spitz_misc_charging(void *opaque, int n, int level) { - switch (line) { - case 0: - zaurus_printf("Charging %s.\n", level ? "off" : "on"); - break; - case 1: - zaurus_printf("Discharging %s.\n", level ? "on" : "off"); - break; - case 2: - zaurus_printf("Green LED %s.\n", level ? "on" : "off"); - break; - case 3: - zaurus_printf("Orange LED %s.\n", level ? "on" : "off"); - break; - case 4: - spitz_bl_bit5(opaque, line, level); - break; - case 5: - spitz_bl_power(opaque, line, level); - break; - case 6: - spitz_adc_temp_on(opaque, line, level); - break; - } + zaurus_printf("Charging %s.\n", level ? "off" : "on"); } -#define SPITZ_SCP_LED_GREEN 1 -#define SPITZ_SCP_JK_B 2 -#define SPITZ_SCP_CHRG_ON 3 -#define SPITZ_SCP_MUTE_L 4 -#define SPITZ_SCP_MUTE_R 5 -#define SPITZ_SCP_CF_POWER 6 -#define SPITZ_SCP_LED_ORANGE 7 -#define SPITZ_SCP_JK_A 8 -#define SPITZ_SCP_ADC_TEMP_ON 9 -#define SPITZ_SCP2_IR_ON 1 -#define SPITZ_SCP2_AKIN_PULLUP 2 -#define SPITZ_SCP2_BACKLIGHT_CONT 7 -#define SPITZ_SCP2_BACKLIGHT_ON 8 -#define SPITZ_SCP2_MIC_BIAS 9 - -static void spitz_scoop_gpio_setup(PXA2xxState *cpu, - DeviceState *scp0, DeviceState *scp1) +static void spitz_misc_discharging(void *opaque, int n, int level) { - qemu_irq *outsignals = qemu_allocate_irqs(spitz_out_switch, cpu, 8); + zaurus_printf("Discharging %s.\n", level ? "off" : "on"); +} - qdev_connect_gpio_out(scp0, SPITZ_SCP_CHRG_ON, outsignals[0]); - qdev_connect_gpio_out(scp0, SPITZ_SCP_JK_B, outsignals[1]); - qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_GREEN, outsignals[2]); - qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_ORANGE, outsignals[3]); +static void spitz_misc_green_led(void *opaque, int n, int level) +{ + zaurus_printf("Green LED %s.\n", level ? "off" : "on"); +} - if (scp1) { - qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_CONT, outsignals[4]); - qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_ON, outsignals[5]); - } +static void spitz_misc_orange_led(void *opaque, int n, int level) +{ + zaurus_printf("Orange LED %s.\n", level ? "off" : "on"); +} + +static void spitz_misc_adc_temp(void *opaque, int n, int level) +{ + SpitzMiscGPIOState *s = SPITZ_MISC_GPIO(opaque); + int batt_temp = level ? SPITZ_BATTERY_TEMP : 0; - qdev_connect_gpio_out(scp0, SPITZ_SCP_ADC_TEMP_ON, outsignals[6]); + qemu_set_irq(s->adc_value, batt_temp); } -#define SPITZ_GPIO_HSYNC 22 -#define SPITZ_GPIO_SD_DETECT 9 -#define SPITZ_GPIO_SD_WP 81 -#define SPITZ_GPIO_ON_RESET 89 -#define SPITZ_GPIO_BAT_COVER 90 -#define SPITZ_GPIO_CF1_IRQ 105 -#define SPITZ_GPIO_CF1_CD 94 -#define SPITZ_GPIO_CF2_IRQ 106 -#define SPITZ_GPIO_CF2_CD 93 +static void spitz_misc_gpio_init(Object *obj) +{ + SpitzMiscGPIOState *s = SPITZ_MISC_GPIO(obj); + DeviceState *dev = DEVICE(obj); + + qdev_init_gpio_in_named(dev, spitz_misc_charging, "charging", 1); + qdev_init_gpio_in_named(dev, spitz_misc_discharging, "discharging", 1); + qdev_init_gpio_in_named(dev, spitz_misc_green_led, "green-led", 1); + qdev_init_gpio_in_named(dev, spitz_misc_orange_led, "orange-led", 1); + qdev_init_gpio_in_named(dev, spitz_misc_adc_temp, "adc-temp-on", 1); + + qdev_init_gpio_out_named(dev, &s->adc_value, "adc-temp", 1); +} + +#define SPITZ_SCP_LED_GREEN 1 +#define SPITZ_SCP_JK_B 2 +#define SPITZ_SCP_CHRG_ON 3 +#define SPITZ_SCP_MUTE_L 4 +#define SPITZ_SCP_MUTE_R 5 +#define SPITZ_SCP_CF_POWER 6 +#define SPITZ_SCP_LED_ORANGE 7 +#define SPITZ_SCP_JK_A 8 +#define SPITZ_SCP_ADC_TEMP_ON 9 +#define SPITZ_SCP2_IR_ON 1 +#define SPITZ_SCP2_AKIN_PULLUP 2 +#define SPITZ_SCP2_BACKLIGHT_CONT 7 +#define SPITZ_SCP2_BACKLIGHT_ON 8 +#define SPITZ_SCP2_MIC_BIAS 9 + +static void spitz_scoop_gpio_setup(SpitzMachineState *sms) +{ + DeviceState *miscdev = sysbus_create_simple(TYPE_SPITZ_MISC_GPIO, -1, NULL); + + sms->misc_gpio = miscdev; + + qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_CHRG_ON, + qdev_get_gpio_in_named(miscdev, "charging", 0)); + qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_JK_B, + qdev_get_gpio_in_named(miscdev, "discharging", 0)); + qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_LED_GREEN, + qdev_get_gpio_in_named(miscdev, "green-led", 0)); + qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_LED_ORANGE, + qdev_get_gpio_in_named(miscdev, "orange-led", 0)); + qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_ADC_TEMP_ON, + qdev_get_gpio_in_named(miscdev, "adc-temp-on", 0)); + qdev_connect_gpio_out_named(miscdev, "adc-temp", 0, + qdev_get_gpio_in(sms->max1111, MAX1111_BATT_TEMP)); + + if (sms->scp1) { + qdev_connect_gpio_out(sms->scp1, SPITZ_SCP2_BACKLIGHT_CONT, + qdev_get_gpio_in_named(sms->lcdtg, "bl_bit5", 0)); + qdev_connect_gpio_out(sms->scp1, SPITZ_SCP2_BACKLIGHT_ON, + qdev_get_gpio_in_named(sms->lcdtg, "bl_power", 0)); + } +} + +#define SPITZ_GPIO_HSYNC 22 +#define SPITZ_GPIO_SD_DETECT 9 +#define SPITZ_GPIO_SD_WP 81 +#define SPITZ_GPIO_ON_RESET 89 +#define SPITZ_GPIO_BAT_COVER 90 +#define SPITZ_GPIO_CF1_IRQ 105 +#define SPITZ_GPIO_CF1_CD 94 +#define SPITZ_GPIO_CF2_IRQ 106 +#define SPITZ_GPIO_CF2_CD 93 static int spitz_hsync; @@ -905,27 +978,27 @@ static void spitz_gpio_setup(PXA2xxState *cpu, int slots) } /* Board init. */ -enum spitz_model_e { spitz, akita, borzoi, terrier }; - -#define SPITZ_RAM 0x04000000 -#define SPITZ_ROM 0x00800000 +#define SPITZ_RAM 0x04000000 +#define SPITZ_ROM 0x00800000 static struct arm_boot_info spitz_binfo = { .loader_start = PXA2XX_SDRAM_BASE, .ram_size = 0x04000000, }; -static void spitz_common_init(MachineState *machine, - enum spitz_model_e model, int arm_id) +static void spitz_common_init(MachineState *machine) { + SpitzMachineClass *smc = SPITZ_MACHINE_GET_CLASS(machine); + SpitzMachineState *sms = SPITZ_MACHINE(machine); + enum spitz_model_e model = smc->model; PXA2xxState *mpu; - DeviceState *scp0, *scp1 = NULL; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *rom = g_new(MemoryRegion, 1); /* Setup CPU & memory */ mpu = pxa270_init(address_space_mem, spitz_binfo.ram_size, machine->cpu_type); + sms->mpu = mpu; sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M); @@ -935,14 +1008,16 @@ static void spitz_common_init(MachineState *machine, /* Setup peripherals */ spitz_keyboard_register(mpu); - spitz_ssp_attach(mpu); + spitz_ssp_attach(sms); - scp0 = sysbus_create_simple("scoop", 0x10800000, NULL); + sms->scp0 = sysbus_create_simple("scoop", 0x10800000, NULL); if (model != akita) { - scp1 = sysbus_create_simple("scoop", 0x08800040, NULL); + sms->scp1 = sysbus_create_simple("scoop", 0x08800040, NULL); + } else { + sms->scp1 = NULL; } - spitz_scoop_gpio_setup(mpu, scp0, scp1); + spitz_scoop_gpio_setup(sms); spitz_gpio_setup(mpu, (model == akita) ? 1 : 2); @@ -958,100 +1033,100 @@ static void spitz_common_init(MachineState *machine, /* A 4.0 GB microdrive is permanently sitting in CF slot 0. */ spitz_microdrive_attach(mpu, 0); - spitz_binfo.board_id = arm_id; + spitz_binfo.board_id = smc->arm_id; arm_load_kernel(mpu->cpu, machine, &spitz_binfo); sl_bootparam_write(SL_PXA_PARAM_BASE); } -static void spitz_init(MachineState *machine) -{ - spitz_common_init(machine, spitz, 0x2c9); -} - -static void borzoi_init(MachineState *machine) +static void spitz_common_class_init(ObjectClass *oc, void *data) { - spitz_common_init(machine, borzoi, 0x33f); -} + MachineClass *mc = MACHINE_CLASS(oc); -static void akita_init(MachineState *machine) -{ - spitz_common_init(machine, akita, 0x2e8); + mc->block_default_type = IF_IDE; + mc->ignore_memory_transaction_failures = true; + mc->init = spitz_common_init; } -static void terrier_init(MachineState *machine) -{ - spitz_common_init(machine, terrier, 0x33f); -} +static const TypeInfo spitz_common_info = { + .name = TYPE_SPITZ_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(SpitzMachineState), + .class_size = sizeof(SpitzMachineClass), + .class_init = spitz_common_class_init, +}; static void akitapda_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + SpitzMachineClass *smc = SPITZ_MACHINE_CLASS(oc); mc->desc = "Sharp SL-C1000 (Akita) PDA (PXA270)"; - mc->init = akita_init; - mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0"); + smc->model = akita; + smc->arm_id = 0x2e8; } static const TypeInfo akitapda_type = { .name = MACHINE_TYPE_NAME("akita"), - .parent = TYPE_MACHINE, + .parent = TYPE_SPITZ_MACHINE, .class_init = akitapda_class_init, }; static void spitzpda_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + SpitzMachineClass *smc = SPITZ_MACHINE_CLASS(oc); mc->desc = "Sharp SL-C3000 (Spitz) PDA (PXA270)"; - mc->init = spitz_init; - mc->block_default_type = IF_IDE; - mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0"); + smc->model = spitz; + smc->arm_id = 0x2c9; } static const TypeInfo spitzpda_type = { .name = MACHINE_TYPE_NAME("spitz"), - .parent = TYPE_MACHINE, + .parent = TYPE_SPITZ_MACHINE, .class_init = spitzpda_class_init, }; static void borzoipda_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + SpitzMachineClass *smc = SPITZ_MACHINE_CLASS(oc); mc->desc = "Sharp SL-C3100 (Borzoi) PDA (PXA270)"; - mc->init = borzoi_init; - mc->block_default_type = IF_IDE; - mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0"); + smc->model = borzoi; + smc->arm_id = 0x33f; } static const TypeInfo borzoipda_type = { .name = MACHINE_TYPE_NAME("borzoi"), - .parent = TYPE_MACHINE, + .parent = TYPE_SPITZ_MACHINE, .class_init = borzoipda_class_init, }; static void terrierpda_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + SpitzMachineClass *smc = SPITZ_MACHINE_CLASS(oc); mc->desc = "Sharp SL-C3200 (Terrier) PDA (PXA270)"; - mc->init = terrier_init; - mc->block_default_type = IF_IDE; - mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c5"); + smc->model = terrier; + smc->arm_id = 0x33f; } static const TypeInfo terrierpda_type = { .name = MACHINE_TYPE_NAME("terrier"), - .parent = TYPE_MACHINE, + .parent = TYPE_SPITZ_MACHINE, .class_init = terrierpda_class_init, }; static void spitz_machine_init(void) { + type_register_static(&spitz_common_info); type_register_static(&akitapda_type); type_register_static(&spitzpda_type); type_register_static(&borzoipda_type); @@ -1152,7 +1227,7 @@ static void corgi_ssp_class_init(ObjectClass *klass, void *data) } static const TypeInfo corgi_ssp_info = { - .name = "corgi-ssp", + .name = TYPE_CORGI_SSP, .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(CorgiSSPState), .class_init = corgi_ssp_class_init, @@ -1181,18 +1256,30 @@ static void spitz_lcdtg_class_init(ObjectClass *klass, void *data) } static const TypeInfo spitz_lcdtg_info = { - .name = "spitz-lcdtg", + .name = TYPE_SPITZ_LCDTG, .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(SpitzLCDTG), .class_init = spitz_lcdtg_class_init, }; +static const TypeInfo spitz_misc_gpio_info = { + .name = TYPE_SPITZ_MISC_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SpitzMiscGPIOState), + .instance_init = spitz_misc_gpio_init, + /* + * No class_init required: device has no internal state so does not + * need to set up reset or vmstate, and does not have a realize method. + */ +}; + static void spitz_register_types(void) { type_register_static(&corgi_ssp_info); type_register_static(&spitz_lcdtg_info); type_register_static(&spitz_keyboard_info); type_register_static(&sl_nand_info); + type_register_static(&spitz_misc_gpio_info); } type_init(spitz_register_types) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 1384a2cf2a..91f0df7b13 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -749,6 +749,7 @@ static void build_fadt_rev5(GArray *table_data, BIOSLinker *linker, static void build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); Aml *scope, *dsdt; MachineState *ms = MACHINE(vms); const MemMapEntry *memmap = vms->memmap; @@ -767,7 +768,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_dsdt_add_cpus(scope, vms->smp_cpus); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], (irqmap[VIRT_UART] + ARM_SPI_BASE)); - acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); + if (vmc->acpi_expose_flash) { + acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); + } acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]); acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO], (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index af3050bc4b..7d9f7157da 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -600,6 +600,7 @@ static void create_its(VirtMachineState *vms) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_GIC_ITS].base); fdt_add_its_gic_node(vms); + vms->msi_controller = VIRT_MSI_CTRL_ITS; } static void create_v2m(VirtMachineState *vms) @@ -620,6 +621,7 @@ static void create_v2m(VirtMachineState *vms) } fdt_add_v2m_gic_node(vms); + vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } static void create_gic(VirtMachineState *vms) @@ -2198,8 +2200,36 @@ out: static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { virt_memory_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { + hwaddr db_start = 0, db_end = 0; + char *resv_prop_str; + + switch (vms->msi_controller) { + case VIRT_MSI_CTRL_NONE: + return; + case VIRT_MSI_CTRL_ITS: + /* GITS_TRANSLATER page */ + db_start = base_memmap[VIRT_GIC_ITS].base + 0x10000; + db_end = base_memmap[VIRT_GIC_ITS].base + + base_memmap[VIRT_GIC_ITS].size - 1; + break; + case VIRT_MSI_CTRL_GICV2M: + /* MSI_SETSPI_NS page */ + db_start = base_memmap[VIRT_GIC_V2M].base; + db_end = db_start + base_memmap[VIRT_GIC_V2M].size - 1; + break; + } + resv_prop_str = g_strdup_printf("0x%"PRIx64":0x%"PRIx64":%u", + db_start, db_end, + VIRTIO_IOMMU_RESV_MEM_T_MSI); + + qdev_prop_set_uint32(dev, "len-reserved-regions", 1); + qdev_prop_set_string(dev, "reserved-regions[0]", resv_prop_str); + g_free(resv_prop_str); } } @@ -2371,6 +2401,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) hc->unplug = virt_machine_device_unplug_cb; mc->nvdimm_supported = true; mc->auto_enable_numa_with_memhp = true; + mc->auto_enable_numa_with_memdev = true; mc->default_ram_id = "mach-virt.ram"; object_class_property_add(oc, "acpi", "OnOffAuto", @@ -2480,9 +2511,13 @@ DEFINE_VIRT_MACHINE_AS_LATEST(5, 1) static void virt_machine_5_0_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + virt_machine_5_1_options(mc); compat_props_add(mc->compat_props, hw_compat_5_0, hw_compat_5_0_len); mc->numa_mem_supported = true; + vmc->acpi_expose_flash = true; + mc->auto_enable_numa_with_memdev = false; } DEFINE_VIRT_MACHINE(5, 0) diff --git a/hw/arm/z2.c b/hw/arm/z2.c index a0f4095990..e1f22f5868 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -111,9 +111,12 @@ typedef struct { int pos; } ZipitLCD; +#define TYPE_ZIPIT_LCD "zipit-lcd" +#define ZIPIT_LCD(obj) OBJECT_CHECK(ZipitLCD, (obj), TYPE_ZIPIT_LCD) + static uint32_t zipit_lcd_transfer(SSISlave *dev, uint32_t value) { - ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); + ZipitLCD *z = ZIPIT_LCD(dev); uint16_t val; if (z->selected) { z->buf[z->pos] = value & 0xff; @@ -153,7 +156,7 @@ static void z2_lcd_cs(void *opaque, int line, int level) static void zipit_lcd_realize(SSISlave *dev, Error **errp) { - ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); + ZipitLCD *z = ZIPIT_LCD(dev); z->selected = 0; z->enabled = 0; z->pos = 0; @@ -185,7 +188,7 @@ static void zipit_lcd_class_init(ObjectClass *klass, void *data) } static const TypeInfo zipit_lcd_info = { - .name = "zipit-lcd", + .name = TYPE_ZIPIT_LCD, .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(ZipitLCD), .class_init = zipit_lcd_class_init, @@ -325,7 +328,7 @@ static void z2_init(MachineState *machine) type_register_static(&zipit_lcd_info); type_register_static(&aer915_info); - z2_lcd = ssi_create_slave(mpu->ssp[1], "zipit-lcd"); + z2_lcd = ssi_create_slave(mpu->ssp[1], TYPE_ZIPIT_LCD); bus = pxa2xx_i2c_bus(mpu->i2c[0]); i2c_create_slave(bus, TYPE_AER915, 0x55); wm = i2c_create_slave(bus, TYPE_WM8750, 0x1b); diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 8a9b9924c4..38522cf0ba 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1393,12 +1393,6 @@ static void ac97_exit(PCIDevice *dev) AUD_remove_card(&s->card); } -static int ac97_init (PCIBus *bus) -{ - pci_create_simple(bus, -1, TYPE_AC97); - return 0; -} - static Property ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(AC97LinkState, card), DEFINE_PROP_END_OF_LIST (), @@ -1436,7 +1430,8 @@ static const TypeInfo ac97_info = { static void ac97_register_types (void) { type_register_static (&ac97_info); - pci_register_soundhw("ac97", "Intel 82801AA AC97 Audio", ac97_init); + deprecated_register_soundhw("ac97", "Intel 82801AA AC97 Audio", + 0, TYPE_AC97); } type_init (ac97_register_types) diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index 7c3b67dcfb..65dff5b6fc 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -319,16 +319,10 @@ static const TypeInfo adlib_info = { .class_init = adlib_class_initfn, }; -static int Adlib_init (ISABus *bus) -{ - isa_create_simple (bus, TYPE_ADLIB); - return 0; -} - static void adlib_register_types (void) { type_register_static (&adlib_info); - isa_register_soundhw("adlib", ADLIB_DESC, Adlib_init); + deprecated_register_soundhw("adlib", ADLIB_DESC, 1, TYPE_ADLIB); } type_init (adlib_register_types) diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index ffdbb58d6a..59705a8d47 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -683,12 +683,6 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp) AUD_register_card ("cs4231a", &s->card); } -static int cs4231a_init (ISABus *bus) -{ - isa_create_simple (bus, TYPE_CS4231A); - return 0; -} - static Property cs4231a_properties[] = { DEFINE_AUDIO_PROPERTIES(CSState, card), DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534), @@ -720,7 +714,7 @@ static const TypeInfo cs4231a_info = { static void cs4231a_register_types (void) { type_register_static (&cs4231a_info); - isa_register_soundhw("cs4231a", "CS4231A", cs4231a_init); + deprecated_register_soundhw("cs4231a", "CS4231A", 1, TYPE_CS4231A); } type_init (cs4231a_register_types) diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 5f8a83ff56..4255463a49 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -884,12 +884,6 @@ static void es1370_exit(PCIDevice *dev) AUD_remove_card(&s->card); } -static int es1370_init (PCIBus *bus) -{ - pci_create_simple (bus, -1, TYPE_ES1370); - return 0; -} - static Property es1370_properties[] = { DEFINE_AUDIO_PROPERTIES(ES1370State, card), DEFINE_PROP_END_OF_LIST(), @@ -928,7 +922,8 @@ static const TypeInfo es1370_info = { static void es1370_register_types (void) { type_register_static (&es1370_info); - pci_register_soundhw("es1370", "ENSONIQ AudioPCI ES1370", es1370_init); + deprecated_register_soundhw("es1370", "ENSONIQ AudioPCI ES1370", + 0, TYPE_ES1370); } type_init (es1370_register_types) diff --git a/hw/audio/gus.c b/hw/audio/gus.c index c8df2bde6b..7e4a8cadad 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -286,12 +286,6 @@ static void gus_realizefn (DeviceState *dev, Error **errp) AUD_set_active_out (s->voice, 1); } -static int GUS_init (ISABus *bus) -{ - isa_create_simple (bus, TYPE_GUS); - return 0; -} - static Property gus_properties[] = { DEFINE_AUDIO_PROPERTIES(GUSState, card), DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), @@ -322,7 +316,7 @@ static const TypeInfo gus_info = { static void gus_register_types (void) { type_register_static (&gus_info); - isa_register_soundhw("gus", "Gravis Ultrasound GF1", GUS_init); + deprecated_register_soundhw("gus", "Gravis Ultrasound GF1", 1, TYPE_GUS); } type_init (gus_register_types) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index f673b8317a..f6cea49686 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -25,6 +25,7 @@ #include "qemu/bitops.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include "hw/audio/soundhw.h" #include "intel-hda.h" #include "migration/vmstate.h" @@ -1307,6 +1308,8 @@ static int intel_hda_and_codec_init(PCIBus *bus) BusState *hdabus; DeviceState *codec; + warn_report("'-soundhw hda' is deprecated, " + "please use '-device intel-hda -device hda-duplex' instead"); controller = DEVICE(pci_create_simple(bus, -1, "intel-hda")); hdabus = QLIST_FIRST(&controller->child_bus); codec = qdev_new("hda-duplex"); diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index c37a387861..ea539e7605 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -28,6 +28,7 @@ #include "audio/audio.h" #include "qemu/module.h" #include "qemu/timer.h" +#include "qemu/error-report.h" #include "hw/timer/i8254.h" #include "migration/vmstate.h" #include "hw/audio/pcspk.h" @@ -112,11 +113,15 @@ static void pcspk_callback(void *opaque, int free) } } -static int pcspk_audio_init(ISABus *bus) +static int pcspk_audio_init(PCSpkState *s) { - PCSpkState *s = pcspk_state; struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUDIO_FORMAT_U8, 0}; + if (s->voice) { + /* already initialized */ + return 0; + } + AUD_register_card(s_spk, &s->card); s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as); @@ -185,6 +190,10 @@ static void pcspk_realizefn(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &s->ioport, s->iobase); + if (s->card.state) { + pcspk_audio_init(s); + } + pcspk_state = s; } @@ -210,7 +219,7 @@ static const VMStateDescription vmstate_spk = { static Property pcspk_properties[] = { DEFINE_AUDIO_PROPERTIES(PCSpkState, card), - DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, -1), + DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, 0x61), DEFINE_PROP_BOOL("migrate", PCSpkState, migrate, true), DEFINE_PROP_END_OF_LIST(), }; @@ -236,9 +245,18 @@ static const TypeInfo pcspk_info = { .class_init = pcspk_class_initfn, }; +static int pcspk_audio_init_soundhw(ISABus *bus) +{ + PCSpkState *s = pcspk_state; + + warn_report("'-soundhw pcspk' is deprecated, " + "please set a backend using '-machine pcspk-audiodev=<name>' instead"); + return pcspk_audio_init(s); +} + static void pcspk_register(void) { type_register_static(&pcspk_info); - isa_register_soundhw("pcspk", "PC speaker", pcspk_audio_init); + isa_register_soundhw("pcspk", "PC speaker", pcspk_audio_init_soundhw); } type_init(pcspk_register) diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index df6f755a37..2d9e50f99b 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1415,12 +1415,6 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) AUD_register_card ("sb16", &s->card); } -static int SB16_init (ISABus *bus) -{ - isa_create_simple (bus, TYPE_SB16); - return 0; -} - static Property sb16_properties[] = { DEFINE_AUDIO_PROPERTIES(SB16State, card), DEFINE_PROP_UINT32 ("version", SB16State, ver, 0x0405), /* 4.5 */ @@ -1453,7 +1447,8 @@ static const TypeInfo sb16_info = { static void sb16_register_types (void) { type_register_static (&sb16_info); - isa_register_soundhw("sb16", "Creative Sound Blaster 16", SB16_init); + deprecated_register_soundhw("sb16", "Creative Sound Blaster 16", + 1, TYPE_SB16); } type_init (sb16_register_types) diff --git a/hw/audio/soundhw.c b/hw/audio/soundhw.c index c750473c8f..173b674ff5 100644 --- a/hw/audio/soundhw.c +++ b/hw/audio/soundhw.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/option.h" #include "qemu/help_option.h" #include "qemu/error-report.h" #include "qom/object.h" @@ -32,6 +33,7 @@ struct soundhw { const char *name; const char *descr; + const char *typename; int enabled; int isa; union { @@ -65,6 +67,17 @@ void pci_register_soundhw(const char *name, const char *descr, soundhw_count++; } +void deprecated_register_soundhw(const char *name, const char *descr, + int isa, const char *typename) +{ + assert(soundhw_count < ARRAY_SIZE(soundhw) - 1); + soundhw[soundhw_count].name = name; + soundhw[soundhw_count].descr = descr; + soundhw[soundhw_count].isa = isa; + soundhw[soundhw_count].typename = typename; + soundhw_count++; +} + void select_soundhw(const char *optarg) { struct soundhw *c; @@ -136,7 +149,16 @@ void soundhw_init(void) for (c = soundhw; c->name; ++c) { if (c->enabled) { - if (c->isa) { + if (c->typename) { + warn_report("'-soundhw %s' is deprecated, " + "please use '-device %s' instead", + c->name, c->typename); + if (c->isa) { + isa_create_simple(isa_bus, c->typename); + } else { + pci_create_simple(pci_bus, -1, c->typename); + } + } else if (c->isa) { if (!isa_bus) { error_report("ISA bus not available for %s", c->name); exit(1); diff --git a/hw/core/numa.c b/hw/core/numa.c index 2725886d06..6a20ce7cf1 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -688,8 +688,9 @@ void numa_complete_configuration(MachineState *ms) NodeInfo *numa_info = ms->numa_state->nodes; /* - * If memory hotplug is enabled (slots > 0) but without '-numa' - * options explicitly on CLI, guestes will break. + * If memory hotplug is enabled (slot > 0) or memory devices are enabled + * (ms->maxram_size > ram_size) but without '-numa' options explicitly on + * CLI, guests will break. * * Windows: won't enable memory hotplug without SRAT table at all * @@ -704,9 +705,9 @@ void numa_complete_configuration(MachineState *ms) * assume there is just one node with whole RAM. */ if (ms->numa_state->num_nodes == 0 && - ((ms->ram_slots > 0 && - mc->auto_enable_numa_with_memhp) || - mc->auto_enable_numa)) { + ((ms->ram_slots && mc->auto_enable_numa_with_memhp) || + (ms->maxram_size > ms->ram_size && mc->auto_enable_numa_with_memdev) || + mc->auto_enable_numa)) { NumaNodeOptions node = { }; parse_numa_node(ms, &node, &error_abort); numa_info[0].node_mem = ram_size; @@ -824,6 +825,7 @@ static void numa_stat_memory_devices(NumaNodeMem node_mem[]) MemoryDeviceInfoList *info; PCDIMMDeviceInfo *pcdimm_info; VirtioPMEMDeviceInfo *vpi; + VirtioMEMDeviceInfo *vmi; for (info = info_list; info; info = info->next) { MemoryDeviceInfo *value = info->value; @@ -844,6 +846,11 @@ static void numa_stat_memory_devices(NumaNodeMem node_mem[]) node_mem[0].node_mem += vpi->size; node_mem[0].node_plugged_mem += vpi->size; break; + case MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM: + vmi = value->u.virtio_mem.data; + node_mem[vmi->node].node_mem += vmi->size; + node_mem[vmi->node].node_plugged_mem += vmi->size; + break; default: g_assert_not_reached(); } diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 71f8aca7c6..ca7771f307 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -15,6 +15,7 @@ #include "chardev/char.h" #include "qemu/uuid.h" #include "qemu/units.h" +#include "qemu/cutils.h" void qdev_prop_set_after_realize(DeviceState *dev, const char *name, Error **errp) @@ -578,6 +579,94 @@ const PropertyInfo qdev_prop_macaddr = { .set = set_mac, }; +/* --- Reserved Region --- */ + +/* + * Accepted syntax: + * <low address>:<high address>:<type> + * where low/high addresses are uint64_t in hexadecimal + * and type is a non-negative decimal integer + */ +static void get_reserved_region(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + ReservedRegion *rr = qdev_get_prop_ptr(dev, prop); + char buffer[64]; + char *p = buffer; + int rc; + + rc = snprintf(buffer, sizeof(buffer), "0x%"PRIx64":0x%"PRIx64":%u", + rr->low, rr->high, rr->type); + assert(rc < sizeof(buffer)); + + visit_type_str(v, name, &p, errp); +} + +static void set_reserved_region(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + ReservedRegion *rr = qdev_get_prop_ptr(dev, prop); + Error *local_err = NULL; + const char *endptr; + char *str; + int ret; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_str(v, name, &str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + ret = qemu_strtou64(str, &endptr, 16, &rr->low); + if (ret) { + error_setg(errp, "start address of '%s'" + " must be a hexadecimal integer", name); + goto out; + } + if (*endptr != ':') { + goto separator_error; + } + + ret = qemu_strtou64(endptr + 1, &endptr, 16, &rr->high); + if (ret) { + error_setg(errp, "end address of '%s'" + " must be a hexadecimal integer", name); + goto out; + } + if (*endptr != ':') { + goto separator_error; + } + + ret = qemu_strtoui(endptr + 1, &endptr, 10, &rr->type); + if (ret) { + error_setg(errp, "type of '%s'" + " must be a non-negative decimal integer", name); + } + goto out; + +separator_error: + error_setg(errp, "reserved region fields must be separated with ':'"); +out: + g_free(str); + return; +} + +const PropertyInfo qdev_prop_reserved_region = { + .name = "reserved_region", + .description = "Reserved Region, example: 0xFEE00000:0xFEEFFFFF:0", + .get = get_reserved_region, + .set = set_reserved_region, +}; + /* --- on/off/auto --- */ const PropertyInfo qdev_prop_on_off_auto = { diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 2131c7f951..9de16eae05 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -137,6 +137,9 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus) */ DeviceState *qdev_new(const char *name) { + if (!object_class_by_name(name)) { + module_load_qom_one(name); + } return DEVICE(object_new(name)); } @@ -147,10 +150,9 @@ DeviceState *qdev_new(const char *name) */ DeviceState *qdev_try_new(const char *name) { - if (!object_class_by_name(name)) { + if (!module_object_class_by_name(name)) { return NULL; } - return DEVICE(object_new(name)); } diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 77a7d622bd..e907f3182b 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -44,18 +44,24 @@ common-obj-$(CONFIG_ARTIST) += artist.o obj-$(CONFIG_VGA) += vga.o -common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o - -obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d.o -obj-$(CONFIG_VHOST_USER_GPU) += vhost-user-gpu.o -obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) += virtio-gpu-pci.o -obj-$(call land,$(CONFIG_VHOST_USER_GPU),$(CONFIG_VIRTIO_PCI)) += vhost-user-gpu-pci.o -obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o -obj-$(CONFIG_VHOST_USER_VGA) += vhost-user-vga.o -virtio-gpu.o-cflags := $(VIRGL_CFLAGS) -virtio-gpu.o-libs += $(VIRGL_LIBS) -virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS) -virtio-gpu-3d.o-libs += $(VIRGL_LIBS) +ifeq ($(CONFIG_QXL),y) +common-obj-m += qxl.mo +qxl.mo-objs = qxl.o qxl-logger.o qxl-render.o +endif + +ifeq ($(CONFIG_VIRTIO_GPU),y) +common-obj-m += virtio-gpu.mo +virtio-gpu-obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d.o +virtio-gpu-obj-$(CONFIG_VHOST_USER_GPU) += vhost-user-gpu.o +virtio-gpu-obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) += virtio-gpu-pci.o +virtio-gpu-obj-$(call land,$(CONFIG_VHOST_USER_GPU),$(CONFIG_VIRTIO_PCI)) += vhost-user-gpu-pci.o +virtio-gpu-obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o +virtio-gpu-obj-$(CONFIG_VHOST_USER_VGA) += vhost-user-vga.o +virtio-gpu.mo-objs := $(virtio-gpu-obj-y) +virtio-gpu.mo-cflags := $(VIRGL_CFLAGS) +virtio-gpu.mo-libs := $(VIRGL_LIBS) +endif + common-obj-$(CONFIG_DPCD) += dpcd.o common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dp.o diff --git a/hw/display/ads7846.c b/hw/display/ads7846.c index 9228b40b1a..56bf82fe07 100644 --- a/hw/display/ads7846.c +++ b/hw/display/ads7846.c @@ -29,6 +29,9 @@ typedef struct { int output; } ADS7846State; +#define TYPE_ADS7846 "ads7846" +#define ADS7846(obj) OBJECT_CHECK(ADS7846State, (obj), TYPE_ADS7846) + /* Control-byte bitfields */ #define CB_PD0 (1 << 0) #define CB_PD1 (1 << 1) @@ -61,7 +64,7 @@ static void ads7846_int_update(ADS7846State *s) static uint32_t ads7846_transfer(SSISlave *dev, uint32_t value) { - ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev); + ADS7846State *s = ADS7846(dev); switch (s->cycle ++) { case 0: @@ -139,7 +142,7 @@ static const VMStateDescription vmstate_ads7846 = { static void ads7846_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); - ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, d); + ADS7846State *s = ADS7846(d); qdev_init_gpio_out(dev, &s->interrupt, 1); @@ -166,7 +169,7 @@ static void ads7846_class_init(ObjectClass *klass, void *data) } static const TypeInfo ads7846_info = { - .name = "ads7846", + .name = TYPE_ADS7846, .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(ADS7846State), .class_init = ads7846_class_init, diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index c6263808a2..7c0e5eef2d 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -282,6 +282,10 @@ static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) newconf.base = s->vcram_base | (value & 0xc0000000); newconf.base += BCM2835_FB_OFFSET; + /* Copy fields which we don't want to change from the existing config */ + newconf.pixo = s->config.pixo; + newconf.alpha = s->config.alpha; + bcm2835_fb_validate_config(&newconf); pitch = bcm2835_fb_get_pitch(&newconf); diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index c3bdb18742..32d27f008a 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -66,9 +66,13 @@ typedef struct { uint8_t framebuffer[128 * 80 / 2]; } ssd0323_state; +#define TYPE_SSD0323 "ssd0323" +#define SSD0323(obj) OBJECT_CHECK(ssd0323_state, (obj), TYPE_SSD0323) + + static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data) { - ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev); + ssd0323_state *s = SSD0323(dev); switch (s->mode) { case SSD0323_DATA: @@ -346,7 +350,7 @@ static const GraphicHwOps ssd0323_ops = { static void ssd0323_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); - ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, d); + ssd0323_state *s = SSD0323(d); s->col_end = 63; s->row_end = 79; @@ -368,7 +372,7 @@ static void ssd0323_class_init(ObjectClass *klass, void *data) } static const TypeInfo ssd0323_info = { - .name = "ssd0323", + .name = TYPE_SSD0323, .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(ssd0323_state), .class_init = ssd0323_class_init, diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c index 9a12c68342..258e926493 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -22,9 +22,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" - -#undef REG_FMT -#define REG_FMT "0x%02lx" +#include "qemu/log.h" /* SCOOP devices */ @@ -104,7 +102,9 @@ static uint64_t scoop_read(void *opaque, hwaddr addr, case SCOOP_GPRR: return s->gpio_level; default: - zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); + qemu_log_mask(LOG_GUEST_ERROR, + "scoop_read: bad register offset 0x%02" HWADDR_PRIx "\n", + addr); } return 0; @@ -150,7 +150,9 @@ static void scoop_write(void *opaque, hwaddr addr, scoop_gpio_handler_update(s); break; default: - zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); + qemu_log_mask(LOG_GUEST_ERROR, + "scoop_write: bad register offset 0x%02" HWADDR_PRIx "\n", + addr); } } diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index c93f32f657..03e347b207 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -35,6 +35,7 @@ config PC select ACPI_PCI select ACPI_VMGENID select VIRTIO_PMEM_SUPPORTED + select VIRTIO_MEM_SUPPORTED config PC_PCI bool diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index df7ad254ac..c56398e991 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3758,7 +3758,7 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) /* Currently only address widths supported are 39 and 48 bits */ if ((s->aw_bits != VTD_HOST_AW_39BIT) && (s->aw_bits != VTD_HOST_AW_48BIT)) { - error_setg(errp, "Supported values for x-aw-bits are: %d, %d", + error_setg(errp, "Supported values for aw-bits are: %d, %d", VTD_HOST_AW_39BIT, VTD_HOST_AW_48BIT); return false; } diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 5e931975a0..81d0888930 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -464,6 +464,7 @@ static void microvm_class_init(ObjectClass *oc, void *data) mc->max_cpus = 288; mc->has_hotpluggable_cpus = false; mc->auto_enable_numa_with_memhp = false; + mc->auto_enable_numa_with_memdev = false; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; mc->nvdimm_supported = false; mc->default_ram_id = "microvm.ram"; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 4af9679d03..d7f27bc16b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -88,6 +88,7 @@ #include "hw/net/ne2000-isa.h" #include "standard-headers/asm-x86/bootparam.h" #include "hw/virtio/virtio-pmem-pci.h" +#include "hw/virtio/virtio-mem-pci.h" #include "hw/mem/memory-device.h" #include "sysemu/replay.h" #include "qapi/qmp/qerror.h" @@ -1155,11 +1156,10 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) g_free(a20_line); } -void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, +void pc_basic_device_init(struct PCMachineState *pcms, + ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, bool create_fdctrl, - bool no_vmport, - bool has_pit, uint32_t hpet_irqs) { int i; @@ -1210,7 +1210,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, qemu_register_boot_set(pc_boot_set, *rtc_state); - if (!xen_enabled() && has_pit) { + if (!xen_enabled() && pcms->pit_enabled) { if (kvm_pit_in_kernel()) { pit = kvm_pit_init(isa_bus, 0x40); } else { @@ -1220,13 +1220,13 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, /* connect PIT to output control line of the HPET */ qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(DEVICE(pit), 0)); } - pcspk_init(isa_bus, pit); + pcspk_init(pcms->pcspk, isa_bus, pit); } i8257_dma_init(isa_bus, 0); /* Super I/O */ - pc_superio_init(isa_bus, create_fdctrl, no_vmport); + pc_superio_init(isa_bus, create_fdctrl, pcms->vmport != ON_OFF_AUTO_ON); } void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) @@ -1637,19 +1637,20 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, numa_cpu_pre_plug(cpu_slot, dev, errp); } -static void pc_virtio_pmem_pci_pre_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_virtio_md_pci_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev); Error *local_err = NULL; - if (!hotplug_dev2) { + if (!hotplug_dev2 && dev->hotplugged) { /* * Without a bus hotplug handler, we cannot control the plug/unplug - * order. This should never be the case on x86, however better add - * a safety net. + * order. We should never reach this point when hotplugging on x86, + * however, better add a safety net. */ - error_setg(errp, "virtio-pmem-pci not supported on this bus."); + error_setg(errp, "hotplug of virtio based memory devices not supported" + " on this bus."); return; } /* @@ -1658,14 +1659,14 @@ static void pc_virtio_pmem_pci_pre_plug(HotplugHandler *hotplug_dev, */ memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), NULL, &local_err); - if (!local_err) { + if (!local_err && hotplug_dev2) { hotplug_handler_pre_plug(hotplug_dev2, dev, &local_err); } error_propagate(errp, local_err); } -static void pc_virtio_pmem_pci_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_virtio_md_pci_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev); Error *local_err = NULL; @@ -1676,24 +1677,26 @@ static void pc_virtio_pmem_pci_plug(HotplugHandler *hotplug_dev, * device bits. */ memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); - hotplug_handler_plug(hotplug_dev2, dev, &local_err); - if (local_err) { - memory_device_unplug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); + if (hotplug_dev2) { + hotplug_handler_plug(hotplug_dev2, dev, &local_err); + if (local_err) { + memory_device_unplug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); + } } error_propagate(errp, local_err); } -static void pc_virtio_pmem_pci_unplug_request(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_virtio_md_pci_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - /* We don't support virtio pmem hot unplug */ - error_setg(errp, "virtio pmem device unplug not supported."); + /* We don't support hot unplug of virtio based memory devices */ + error_setg(errp, "virtio based memory devices cannot be unplugged."); } -static void pc_virtio_pmem_pci_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_virtio_md_pci_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - /* We don't support virtio pmem hot unplug */ + /* We don't support hot unplug of virtio based memory devices */ } static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, @@ -1703,8 +1706,9 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, pc_memory_pre_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_pre_plug(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) { - pc_virtio_pmem_pci_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { + pc_virtio_md_pci_pre_plug(hotplug_dev, dev, errp); } } @@ -1715,8 +1719,9 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, pc_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_plug(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) { - pc_virtio_pmem_pci_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { + pc_virtio_md_pci_plug(hotplug_dev, dev, errp); } } @@ -1727,8 +1732,9 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, pc_memory_unplug_request(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_unplug_request_cb(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) { - pc_virtio_pmem_pci_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { + pc_virtio_md_pci_unplug_request(hotplug_dev, dev, errp); } else { error_setg(errp, "acpi: device unplug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -1742,8 +1748,9 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, pc_memory_unplug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_unplug_cb(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) { - pc_virtio_pmem_pci_unplug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { + pc_virtio_md_pci_unplug(hotplug_dev, dev, errp); } else { error_setg(errp, "acpi: device unplug for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -1755,7 +1762,8 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || object_dynamic_cast(OBJECT(dev), TYPE_CPU) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) { + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { return HOTPLUG_HANDLER(machine); } @@ -1892,6 +1900,9 @@ static void pc_machine_initfn(Object *obj) pcms->pit_enabled = true; pc_system_flash_create(pcms); + pcms->pcspk = isa_new(TYPE_PC_SPEAKER); + object_property_add_alias(OBJECT(pcms), "pcspk-audiodev", + OBJECT(pcms->pcspk), "audiodev"); } static void pc_machine_reset(MachineState *machine) @@ -1966,6 +1977,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->auto_enable_numa_with_memhp = true; + mc->auto_enable_numa_with_memdev = true; mc->has_hotpluggable_cpus = true; mc->default_boot_order = "cad"; mc->hot_add_cpu = pc_hot_add_cpu; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 1d832b2878..2bb42a8141 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -235,8 +235,7 @@ static void pc_init1(MachineState *machine, } /* init basic PC hardware */ - pc_basic_device_init(isa_bus, x86ms->gsi, &rtc_state, true, - (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit_enabled, + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, &rtc_state, true, 0x4); pc_nic_init(pcmc, isa_bus, pci_bus); @@ -444,6 +443,7 @@ static void pc_i440fx_5_0_machine_options(MachineClass *m) m->numa_mem_supported = true; compat_props_add(m->compat_props, hw_compat_5_0, hw_compat_5_0_len); compat_props_add(m->compat_props, pc_compat_5_0, pc_compat_5_0_len); + m->auto_enable_numa_with_memdev = false; } DEFINE_I440FX_MACHINE(v5_0, "pc-i440fx-5.0", NULL, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 047ea8db28..33163ed18d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -275,8 +275,7 @@ static void pc_q35_init(MachineState *machine) } /* init basic PC hardware */ - pc_basic_device_init(isa_bus, x86ms->gsi, &rtc_state, !mc->no_floppy, - (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit_enabled, + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, &rtc_state, !mc->no_floppy, 0xff0104); /* connect pm stuff to lpc */ @@ -372,6 +371,7 @@ static void pc_q35_5_0_machine_options(MachineClass *m) m->numa_mem_supported = true; compat_props_add(m->compat_props, hw_compat_5_0, hw_compat_5_0_len); compat_props_add(m->compat_props, pc_compat_5_0, pc_compat_5_0_len); + m->auto_enable_numa_with_memhp = false; } DEFINE_Q35_MACHINE(v5_0, "pc-q35-5.0", NULL, diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index d9e6c7fa00..75a2da2881 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -102,7 +102,7 @@ static void i82378_realize(PCIDevice *pci, Error **errp) pit = i8254_pit_init(isabus, 0x40, 0, NULL); /* speaker */ - pcspk_init(isabus, pit); + pcspk_init(isa_new(TYPE_PC_SPEAKER), isabus, pit); /* 2 82C37 (dma) */ isa_create_simple(isabus, "i82374"); diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index a2fef04f8e..94a37a1a46 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -10,7 +10,6 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "cpu.h" -#include "hw/hw.h" #include "hw/irq.h" #include "hw/m68k/mcf.h" #include "qemu/timer.h" @@ -69,10 +68,16 @@ static void m5206_timer_recalibrate(m5206_timer_state *s) if (mode == 2) prescale *= 16; - if (mode == 3 || mode == 0) - hw_error("m5206_timer: mode %d not implemented\n", mode); - if ((s->tmr & TMR_FRR) == 0) - hw_error("m5206_timer: free running mode not implemented\n"); + if (mode == 3 || mode == 0) { + qemu_log_mask(LOG_UNIMP, "m5206_timer: mode %d not implemented\n", + mode); + goto exit; + } + if ((s->tmr & TMR_FRR) == 0) { + qemu_log_mask(LOG_UNIMP, + "m5206_timer: free running mode not implemented\n"); + goto exit; + } /* Assume 66MHz system clock. */ ptimer_set_freq(s->timer, 66000000 / prescale); @@ -391,7 +396,9 @@ static uint32_t m5206_mbar_readb(void *opaque, hwaddr offset) m5206_mbar_state *s = (m5206_mbar_state *)opaque; offset &= 0x3ff; if (offset >= 0x200) { - hw_error("Bad MBAR read offset 0x%x", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX, + offset); + return 0; } if (m5206_mbar_width[offset >> 2] > 1) { uint16_t val; @@ -410,7 +417,9 @@ static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset) int width; offset &= 0x3ff; if (offset >= 0x200) { - hw_error("Bad MBAR read offset 0x%x", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX, + offset); + return 0; } width = m5206_mbar_width[offset >> 2]; if (width > 2) { @@ -434,7 +443,9 @@ static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset) int width; offset &= 0x3ff; if (offset >= 0x200) { - hw_error("Bad MBAR read offset 0x%x", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX, + offset); + return 0; } width = m5206_mbar_width[offset >> 2]; if (width < 4) { @@ -458,7 +469,9 @@ static void m5206_mbar_writeb(void *opaque, hwaddr offset, int width; offset &= 0x3ff; if (offset >= 0x200) { - hw_error("Bad MBAR write offset 0x%x", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX, + offset); + return; } width = m5206_mbar_width[offset >> 2]; if (width > 1) { @@ -482,7 +495,9 @@ static void m5206_mbar_writew(void *opaque, hwaddr offset, int width; offset &= 0x3ff; if (offset >= 0x200) { - hw_error("Bad MBAR write offset 0x%x", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX, + offset); + return; } width = m5206_mbar_width[offset >> 2]; if (width > 2) { @@ -510,7 +525,9 @@ static void m5206_mbar_writel(void *opaque, hwaddr offset, int width; offset &= 0x3ff; if (offset >= 0x200) { - hw_error("Bad MBAR write offset 0x%x", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX, + offset); + return; } width = m5206_mbar_width[offset >> 2]; if (width < 4) { diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index c3b0da60cc..0002bff695 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -250,7 +250,7 @@ static void mips_jazz_init(MachineState *machine, isa_bus_irqs(isa_bus, i8259); i8257_dma_init(isa_bus, 0); pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); - pcspk_init(isa_bus, pit); + pcspk_init(isa_new(TYPE_PC_SPEAKER), isa_bus, pit); /* Video card */ switch (jazz_model) { diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c index 2b87bdee5b..7e6723f343 100644 --- a/hw/misc/max111x.c +++ b/hw/misc/max111x.c @@ -11,29 +11,11 @@ */ #include "qemu/osdep.h" +#include "hw/misc/max111x.h" #include "hw/irq.h" -#include "hw/ssi/ssi.h" #include "migration/vmstate.h" #include "qemu/module.h" - -typedef struct { - SSISlave parent_obj; - - qemu_irq interrupt; - uint8_t tb1, rb2, rb3; - int cycle; - - uint8_t input[8]; - int inputs, com; -} MAX111xState; - -#define TYPE_MAX_111X "max111x" - -#define MAX_111X(obj) \ - OBJECT_CHECK(MAX111xState, (obj), TYPE_MAX_111X) - -#define TYPE_MAX_1110 "max1110" -#define TYPE_MAX_1111 "max1111" +#include "hw/qdev-properties.h" /* Control-byte bitfields */ #define CB_PD0 (1 << 0) @@ -127,27 +109,24 @@ static const VMStateDescription vmstate_max111x = { } }; +static void max111x_input_set(void *opaque, int line, int value) +{ + MAX111xState *s = MAX_111X(opaque); + + assert(line >= 0 && line < s->inputs); + s->input[line] = value; +} + static int max111x_init(SSISlave *d, int inputs) { DeviceState *dev = DEVICE(d); MAX111xState *s = MAX_111X(dev); qdev_init_gpio_out(dev, &s->interrupt, 1); + qdev_init_gpio_in(dev, max111x_input_set, inputs); s->inputs = inputs; - /* TODO: add a user interface for setting these */ - s->input[0] = 0xf0; - s->input[1] = 0xe0; - s->input[2] = 0xd0; - s->input[3] = 0xc0; - s->input[4] = 0xb0; - s->input[5] = 0xa0; - s->input[6] = 0x90; - s->input[7] = 0x80; - s->com = 0; - vmstate_register(VMSTATE_IF(dev), VMSTATE_INSTANCE_ID_ANY, - &vmstate_max111x, s); return 0; } @@ -161,18 +140,51 @@ static void max1111_realize(SSISlave *dev, Error **errp) max111x_init(dev, 4); } -void max111x_set_input(DeviceState *dev, int line, uint8_t value) +static void max111x_reset(DeviceState *dev) { MAX111xState *s = MAX_111X(dev); - assert(line >= 0 && line < s->inputs); - s->input[line] = value; + int i; + + for (i = 0; i < s->inputs; i++) { + s->input[i] = s->reset_input[i]; + } + s->com = 0; + s->tb1 = 0; + s->rb2 = 0; + s->rb3 = 0; + s->cycle = 0; } +static Property max1110_properties[] = { + /* Reset values for ADC inputs */ + DEFINE_PROP_UINT8("input0", MAX111xState, reset_input[0], 0xf0), + DEFINE_PROP_UINT8("input1", MAX111xState, reset_input[1], 0xe0), + DEFINE_PROP_UINT8("input2", MAX111xState, reset_input[2], 0xd0), + DEFINE_PROP_UINT8("input3", MAX111xState, reset_input[3], 0xc0), + DEFINE_PROP_END_OF_LIST(), +}; + +static Property max1111_properties[] = { + /* Reset values for ADC inputs */ + DEFINE_PROP_UINT8("input0", MAX111xState, reset_input[0], 0xf0), + DEFINE_PROP_UINT8("input1", MAX111xState, reset_input[1], 0xe0), + DEFINE_PROP_UINT8("input2", MAX111xState, reset_input[2], 0xd0), + DEFINE_PROP_UINT8("input3", MAX111xState, reset_input[3], 0xc0), + DEFINE_PROP_UINT8("input4", MAX111xState, reset_input[4], 0xb0), + DEFINE_PROP_UINT8("input5", MAX111xState, reset_input[5], 0xa0), + DEFINE_PROP_UINT8("input6", MAX111xState, reset_input[6], 0x90), + DEFINE_PROP_UINT8("input7", MAX111xState, reset_input[7], 0x80), + DEFINE_PROP_END_OF_LIST(), +}; + static void max111x_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); k->transfer = max111x_transfer; + dc->reset = max111x_reset; + dc->vmsd = &vmstate_max111x; } static const TypeInfo max111x_info = { @@ -186,8 +198,10 @@ static const TypeInfo max111x_info = { static void max1110_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); k->realize = max1110_realize; + device_class_set_props(dc, max1110_properties); } static const TypeInfo max1110_info = { @@ -199,8 +213,10 @@ static const TypeInfo max1110_info = { static void max1111_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); k->realize = max1111_realize; + device_class_set_props(dc, max1111_properties); } static const TypeInfo max1111_info = { diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index eefedc252d..2c14804041 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -280,12 +280,16 @@ static void imx_phy_reset(IMXFECState *s) static uint32_t imx_phy_read(IMXFECState *s, int reg) { uint32_t val; + uint32_t phy = reg / 32; - if (reg > 31) { - /* we only advertise one phy */ + if (phy != s->phy_num) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad phy num %u\n", + TYPE_IMX_FEC, __func__, phy); return 0; } + reg %= 32; + switch (reg) { case 0: /* Basic Control */ val = s->phy_control; @@ -331,20 +335,25 @@ static uint32_t imx_phy_read(IMXFECState *s, int reg) break; } - trace_imx_phy_read(val, reg); + trace_imx_phy_read(val, phy, reg); return val; } static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) { - trace_imx_phy_write(val, reg); + uint32_t phy = reg / 32; - if (reg > 31) { - /* we only advertise one phy */ + if (phy != s->phy_num) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad phy num %u\n", + TYPE_IMX_FEC, __func__, phy); return; } + reg %= 32; + + trace_imx_phy_write(val, phy, reg); + switch (reg) { case 0: /* Basic Control */ if (val & 0x8000) { @@ -926,7 +935,7 @@ static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value, extract32(value, 18, 10))); } else { - /* This a write operation */ + /* This is a write operation */ imx_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16)); } /* raise the interrupt as the PHY operation is done */ @@ -1315,6 +1324,7 @@ static void imx_eth_realize(DeviceState *dev, Error **errp) static Property imx_eth_properties[] = { DEFINE_NIC_PROPERTIES(IMXFECState, conf), DEFINE_PROP_UINT32("tx-ring-num", IMXFECState, tx_ring_num, 1), + DEFINE_PROP_UINT32("phy-num", IMXFECState, phy_num, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/trace-events b/hw/net/trace-events index e6875c4c0f..5db45456d9 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -413,8 +413,8 @@ i82596_set_multicast(uint16_t count) "Added %d multicast entries" i82596_channel_attention(void *s) "%p: Received CHANNEL ATTENTION" # imx_fec.c -imx_phy_read(uint32_t val, int reg) "0x%04"PRIx32" <= reg[%d]" -imx_phy_write(uint32_t val, int reg) "0x%04"PRIx32" => reg[%d]" +imx_phy_read(uint32_t val, int phy, int reg) "0x%04"PRIx32" <= phy[%d].reg[%d]" +imx_phy_write(uint32_t val, int phy, int reg) "0x%04"PRIx32" => phy[%d].reg[%d]" imx_phy_update_link(const char *s) "%s" imx_phy_reset(void) "" imx_fec_read_bd(uint64_t addr, int flags, int len, int data) "tx_bd 0x%"PRIx64" flags 0x%04x len %d data 0x%08x" diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index aac0e98228..a7f4252630 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -52,6 +52,17 @@ uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) return features; } +int vhost_net_get_config(struct vhost_net *net, uint8_t *config, + uint32_t config_len) +{ + return 0; +} +int vhost_net_set_config(struct vhost_net *net, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags) +{ + return 0; +} + void vhost_net_ack_features(struct vhost_net *net, uint64_t features) { } diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 6b82803fa7..24d555e764 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -17,6 +17,7 @@ #include "net/net.h" #include "net/tap.h" #include "net/vhost-user.h" +#include "net/vhost-vdpa.h" #include "standard-headers/linux/vhost_types.h" #include "hw/virtio/virtio-net.h" @@ -33,12 +34,6 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/virtio-bus.h" -struct vhost_net { - struct vhost_dev dev; - struct vhost_virtqueue vqs[2]; - int backend; - NetClientState *nc; -}; /* Features supported by host kernel. */ static const int kernel_feature_bits[] = { @@ -96,6 +91,11 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net) case NET_CLIENT_DRIVER_VHOST_USER: feature_bits = user_feature_bits; break; +#ifdef CONFIG_VHOST_NET_VDPA + case NET_CLIENT_DRIVER_VHOST_VDPA: + feature_bits = vdpa_feature_bits; + break; +#endif default: error_report("Feature bits not defined for this type: %d", net->nc->info->type); @@ -110,6 +110,16 @@ uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) return vhost_get_features(&net->dev, vhost_net_get_feature_bits(net), features); } +int vhost_net_get_config(struct vhost_net *net, uint8_t *config, + uint32_t config_len) +{ + return vhost_dev_get_config(&net->dev, config, config_len); +} +int vhost_net_set_config(struct vhost_net *net, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags) +{ + return vhost_dev_set_config(&net->dev, data, offset, size, flags); +} void vhost_net_ack_features(struct vhost_net *net, uint64_t features) { @@ -306,7 +316,9 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); + struct vhost_net *net; int r, e, i; + NetClientState *peer; if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); @@ -314,9 +326,9 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } for (i = 0; i < total_queues; i++) { - struct vhost_net *net; - net = get_vhost_net(ncs[i].peer); + peer = qemu_get_peer(ncs, i); + net = get_vhost_net(peer); vhost_net_set_vq_index(net, i * 2); /* Suppress the masking guest notifiers on vhost user @@ -335,15 +347,16 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } for (i = 0; i < total_queues; i++) { - r = vhost_net_start_one(get_vhost_net(ncs[i].peer), dev); + peer = qemu_get_peer(ncs, i); + r = vhost_net_start_one(get_vhost_net(peer), dev); if (r < 0) { goto err_start; } - if (ncs[i].peer->vring_enable) { + if (peer->vring_enable) { /* restore vring enable state */ - r = vhost_set_vring_enable(ncs[i].peer, ncs[i].peer->vring_enable); + r = vhost_set_vring_enable(peer, peer->vring_enable); if (r < 0) { goto err_start; @@ -355,7 +368,8 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, err_start: while (--i >= 0) { - vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); + peer = qemu_get_peer(ncs , i); + vhost_net_stop_one(get_vhost_net(peer), dev); } e = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); if (e < 0) { @@ -430,6 +444,12 @@ VHostNetState *get_vhost_net(NetClientState *nc) assert(vhost_net); break; #endif +#ifdef CONFIG_VHOST_NET_VDPA + case NET_CLIENT_DRIVER_VHOST_VDPA: + vhost_net = vhost_vdpa_get_vhost_net(nc); + assert(vhost_net); + break; +#endif default: break; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 9bb5578e5d..1596cb1397 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -43,6 +43,7 @@ #include "monitor/qdev.h" #include "hw/pci/pci.h" #include "net_rx_pkt.h" +#include "hw/virtio/vhost.h" #define VIRTIO_NET_VM_VERSION 11 @@ -125,6 +126,8 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) VirtIONet *n = VIRTIO_NET(vdev); struct virtio_net_config netcfg; + int ret = 0; + memset(&netcfg, 0 , sizeof(struct virtio_net_config)); virtio_stw_p(vdev, &netcfg.status, n->status); virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu); @@ -138,6 +141,15 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) virtio_stl_p(vdev, &netcfg.supported_hash_types, VIRTIO_NET_RSS_SUPPORTED_HASHES); memcpy(config, &netcfg, n->config_size); + + NetClientState *nc = qemu_get_queue(n->nic); + if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) { + ret = vhost_net_get_config(get_vhost_net(nc->peer), (uint8_t *)&netcfg, + n->config_size); + if (ret != -1) { + memcpy(config, &netcfg, n->config_size); + } + } } static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) @@ -153,6 +165,13 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) memcpy(n->mac, netcfg.mac, ETH_ALEN); qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); } + + NetClientState *nc = qemu_get_queue(n->nic); + if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) { + vhost_net_set_config(get_vhost_net(nc->peer), (uint8_t *)&netcfg, + 0, n->config_size, + VHOST_SET_CONFIG_TYPE_MASTER); + } } static bool virtio_net_started(VirtIONet *n, uint8_t status) diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c index b11ffa0edc..669c21adc2 100644 --- a/hw/riscv/sifive_clint.c +++ b/hw/riscv/sifive_clint.c @@ -181,7 +181,7 @@ static const MemoryRegionOps sifive_clint_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, - .max_access_size = 4 + .max_access_size = 8 } }; diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index 4f216c5585..c20c192034 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -166,6 +166,9 @@ static void sifive_plic_update(SiFivePLICState *plic) static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid) { int i, j; + uint32_t max_irq = 0; + uint32_t max_prio = plic->target_priority[addrid]; + for (i = 0; i < plic->bitfield_words; i++) { uint32_t pending_enabled_not_claimed = (plic->pending[i] & ~plic->claimed[i]) & @@ -177,14 +180,18 @@ static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid) int irq = (i << 5) + j; uint32_t prio = plic->source_priority[irq]; int enabled = pending_enabled_not_claimed & (1 << j); - if (enabled && prio > plic->target_priority[addrid]) { - sifive_plic_set_pending(plic, irq, false); - sifive_plic_set_claimed(plic, irq, true); - return irq; + if (enabled && prio > max_prio) { + max_irq = irq; + max_prio = prio; } } } - return 0; + + if (max_irq) { + sifive_plic_set_pending(plic, max_irq, false); + sifive_plic_set_claimed(plic, max_irq, true); + } + return max_irq; } static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) @@ -248,8 +255,8 @@ static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) plic->addr_config[addrid].hartid, mode_to_char(plic->addr_config[addrid].mode), value); - sifive_plic_print_state(plic); } + sifive_plic_update(plic); return value; } } @@ -280,6 +287,7 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, qemu_log("plic: write priority: irq=%d priority=%d\n", irq, plic->source_priority[irq]); } + sifive_plic_update(plic); return; } else if (addr >= plic->pending_base && /* 1 bit per source */ addr < plic->pending_base + (plic->num_sources >> 3)) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 142e52a8ff..736965c928 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -637,22 +637,24 @@ static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) { - uint8_t ind_old, ind_new; + uint8_t expected, actual; hwaddr len = 1; - uint8_t *ind_addr; + /* avoid multiple fetches */ + uint8_t volatile *ind_addr; ind_addr = cpu_physical_memory_map(ind_loc, &len, true); if (!ind_addr) { s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0); return -1; } + actual = *ind_addr; do { - ind_old = *ind_addr; - ind_new = ind_old | to_be_set; - } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); - cpu_physical_memory_unmap(ind_addr, len, 1, len); + expected = actual; + actual = atomic_cmpxchg(ind_addr, expected, expected | to_be_set); + } while (actual != expected); + cpu_physical_memory_unmap((void *)ind_addr, len, 1, len); - return ind_old; + return actual; } static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index b111406d56..023fd25f2b 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -43,7 +43,6 @@ #include "hw/qdev-properties.h" #include "hw/s390x/tod.h" #include "sysemu/sysemu.h" -#include "sysemu/balloon.h" #include "hw/s390x/pv.h" #include "migration/blocker.h" @@ -329,7 +328,7 @@ static void s390_machine_unprotect(S390CcwMachineState *ms) ms->pv = false; migrate_del_blocker(pv_mig_blocker); error_free_or_abort(&pv_mig_blocker); - qemu_balloon_inhibit(false); + ram_block_discard_disable(false); } static int s390_machine_protect(S390CcwMachineState *ms) @@ -338,17 +337,22 @@ static int s390_machine_protect(S390CcwMachineState *ms) int rc; /* - * Ballooning on protected VMs needs support in the guest for - * sharing and unsharing balloon pages. Block ballooning for - * now, until we have a solution to make at least Linux guests - * either support it or fail gracefully. + * Discarding of memory in RAM blocks does not work as expected with + * protected VMs. Sharing and unsharing pages would be required. Disable + * it for now, until until we have a solution to make at least Linux + * guests either support it (e.g., virtio-balloon) or fail gracefully. */ - qemu_balloon_inhibit(true); + rc = ram_block_discard_disable(true); + if (rc) { + error_report("protected VMs: cannot disable RAM discard"); + return rc; + } + error_setg(&pv_mig_blocker, "protected VMs are currently not migrateable."); rc = migrate_add_blocker(pv_mig_blocker, &local_err); if (rc) { - qemu_balloon_inhibit(false); + ram_block_discard_disable(false); error_report_err(local_err); error_free_or_abort(&pv_mig_blocker); return rc; @@ -357,7 +361,7 @@ static int s390_machine_protect(S390CcwMachineState *ms) /* Create SE VM */ rc = s390_pv_vm_enable(); if (rc) { - qemu_balloon_inhibit(false); + ram_block_discard_disable(false); migrate_del_blocker(pv_mig_blocker); error_free_or_abort(&pv_mig_blocker); return rc; diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index c1f4bb1d33..3c988a000b 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -786,9 +786,10 @@ static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, uint8_t to_be_set) { - uint8_t ind_old, ind_new; + uint8_t expected, actual; hwaddr len = 1; - uint8_t *ind_addr; + /* avoid multiple fetches */ + uint8_t volatile *ind_addr; ind_addr = cpu_physical_memory_map(ind_loc, &len, true); if (!ind_addr) { @@ -796,14 +797,15 @@ static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, __func__, sch->cssid, sch->ssid, sch->schid); return -1; } + actual = *ind_addr; do { - ind_old = *ind_addr; - ind_new = ind_old | to_be_set; - } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); - trace_virtio_ccw_set_ind(ind_loc, ind_old, ind_new); - cpu_physical_memory_unmap(ind_addr, len, 1, len); + expected = actual; + actual = atomic_cmpxchg(ind_addr, expected, expected | to_be_set); + } while (actual != expected); + trace_virtio_ccw_set_ind(ind_loc, actual, actual | to_be_set); + cpu_physical_memory_unmap((void *)ind_addr, len, 1, len); - return ind_old; + return actual; } static void virtio_ccw_notify(DeviceState *d, uint16_t vector) diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 25cec2ddea..25cdf4c966 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -74,7 +74,7 @@ typedef struct { static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) { - ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev); + ssi_sd_state *s = SSI_SD(dev); /* Special case: allow CMD12 (STOP TRANSMISSION) while reading data. */ if (s->mode == SSI_SD_DATA_READ && val == 0x4d) { @@ -241,7 +241,7 @@ static const VMStateDescription vmstate_ssi_sd = { static void ssi_sd_realize(SSISlave *d, Error **errp) { - ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d); + ssi_sd_state *s = SSI_SD(d); DeviceState *carddev; DriveInfo *dinfo; Error *err = NULL; diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index 67b48c31cd..a35d7ebb26 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -90,11 +90,16 @@ static const TypeInfo ssi_slave_info = { .abstract = true, }; +bool ssi_realize_and_unref(DeviceState *dev, SSIBus *bus, Error **errp) +{ + return qdev_realize_and_unref(dev, &bus->parent_obj, errp); +} + DeviceState *ssi_create_slave(SSIBus *bus, const char *name) { DeviceState *dev = qdev_new(name); - qdev_realize_and_unref(dev, &bus->parent_obj, &error_fatal); + ssi_realize_and_unref(dev, bus, &error_fatal); return dev; } diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index fa5c3fa1b8..e342ff59fa 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -29,11 +29,13 @@ common-obj-$(CONFIG_USB_NETWORK) += dev-network.o ifeq ($(CONFIG_USB_SMARTCARD),y) common-obj-y += dev-smartcard-reader.o -common-obj-$(CONFIG_SMARTCARD) += smartcard.mo +ifeq ($(CONFIG_SMARTCARD),y) +common-obj-m += smartcard.mo smartcard.mo-objs := ccid-card-passthru.o ccid-card-emulated.o smartcard.mo-cflags := $(SMARTCARD_CFLAGS) smartcard.mo-libs := $(SMARTCARD_LIBS) endif +endif ifeq ($(CONFIG_POSIX),y) common-obj-$(CONFIG_USB_STORAGE_MTP) += dev-mtp.o @@ -41,9 +43,12 @@ endif # usb redirection ifeq ($(CONFIG_USB),y) -common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o -redirect.o-cflags = $(USB_REDIR_CFLAGS) -redirect.o-libs = $(USB_REDIR_LIBS) +ifeq ($(CONFIG_USB_REDIR),y) +common-obj-m += redirect.mo +redirect.mo-objs = redirect.o quirks.o +redirect.mo-cflags = $(USB_REDIR_CFLAGS) +redirect.mo-libs = $(USB_REDIR_LIBS) +endif endif # usb pass-through diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 95564c17ed..b9330a8e6f 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -105,12 +105,12 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) vapdev->vdev.dev = dev; /* - * vfio-ap devices operate in a way compatible with - * memory ballooning, as no pages are pinned in the host. + * vfio-ap devices operate in a way compatible with discarding of + * memory in RAM blocks, as no pages are pinned in the host. * This needs to be set before vfio_get_device() for vfio common to - * handle the balloon inhibitor. + * handle ram_block_discard_disable(). */ - vapdev->vdev.balloon_allowed = true; + vapdev->vdev.ram_block_discard_allowed = true; ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, errp); if (ret) { diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 06e69d7066..ff7f369779 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -574,12 +574,13 @@ static void vfio_ccw_get_device(VFIOGroup *group, VFIOCCWDevice *vcdev, /* * All vfio-ccw devices are believed to operate in a way compatible with - * memory ballooning, ie. pages pinned in the host are in the current - * working set of the guest driver and therefore never overlap with pages - * available to the guest balloon driver. This needs to be set before - * vfio_get_device() for vfio common to handle the balloon inhibitor. + * discarding of memory in RAM blocks, ie. pages pinned in the host are + * in the current working set of the guest driver and therefore never + * overlap e.g., with pages available to the guest balloon driver. This + * needs to be set before vfio_get_device() for vfio common to handle + * ram_block_discard_disable(). */ - vcdev->vdev.balloon_allowed = true; + vcdev->vdev.ram_block_discard_allowed = true; if (vfio_get_device(group, vcdev->cdev.mdevid, &vcdev->vdev, errp)) { goto out_err; diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 0b3593b3c0..33357140b8 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -33,7 +33,6 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/range.h" -#include "sysemu/balloon.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" #include "trace.h" @@ -1215,31 +1214,36 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, space = vfio_get_address_space(as); /* - * VFIO is currently incompatible with memory ballooning insofar as the + * VFIO is currently incompatible with discarding of RAM insofar as the * madvise to purge (zap) the page from QEMU's address space does not * interact with the memory API and therefore leaves stale virtual to * physical mappings in the IOMMU if the page was previously pinned. We - * therefore add a balloon inhibit for each group added to a container, + * therefore set discarding broken for each group added to a container, * whether the container is used individually or shared. This provides * us with options to allow devices within a group to opt-in and allow - * ballooning, so long as it is done consistently for a group (for instance + * discarding, so long as it is done consistently for a group (for instance * if the device is an mdev device where it is known that the host vendor * driver will never pin pages outside of the working set of the guest - * driver, which would thus not be ballooning candidates). + * driver, which would thus not be discarding candidates). * * The first opportunity to induce pinning occurs here where we attempt to * attach the group to existing containers within the AddressSpace. If any - * pages are already zapped from the virtual address space, such as from a - * previous ballooning opt-in, new pinning will cause valid mappings to be + * pages are already zapped from the virtual address space, such as from + * previous discards, new pinning will cause valid mappings to be * re-established. Likewise, when the overall MemoryListener for a new * container is registered, a replay of mappings within the AddressSpace * will occur, re-establishing any previously zapped pages as well. * - * NB. Balloon inhibiting does not currently block operation of the - * balloon driver or revoke previously pinned pages, it only prevents - * calling madvise to modify the virtual mapping of ballooned pages. + * Especially virtio-balloon is currently only prevented from discarding + * new memory, it will not yet set ram_block_discard_set_required() and + * therefore, neither stops us here or deals with the sudden memory + * consumption of inflated memory. */ - qemu_balloon_inhibit(true); + ret = ram_block_discard_disable(true); + if (ret) { + error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); + return ret; + } QLIST_FOREACH(container, &space->containers, next) { if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { @@ -1405,7 +1409,7 @@ close_fd_exit: close(fd); put_space_exit: - qemu_balloon_inhibit(false); + ram_block_discard_disable(false); vfio_put_address_space(space); return ret; @@ -1526,8 +1530,8 @@ void vfio_put_group(VFIOGroup *group) return; } - if (!group->balloon_allowed) { - qemu_balloon_inhibit(false); + if (!group->ram_block_discard_allowed) { + ram_block_discard_disable(false); } vfio_kvm_device_del_group(group); vfio_disconnect_container(group); @@ -1565,22 +1569,23 @@ int vfio_get_device(VFIOGroup *group, const char *name, } /* - * Clear the balloon inhibitor for this group if the driver knows the - * device operates compatibly with ballooning. Setting must be consistent - * per group, but since compatibility is really only possible with mdev - * currently, we expect singleton groups. + * Set discarding of RAM as not broken for this group if the driver knows + * the device operates compatibly with discarding. Setting must be + * consistent per group, but since compatibility is really only possible + * with mdev currently, we expect singleton groups. */ - if (vbasedev->balloon_allowed != group->balloon_allowed) { + if (vbasedev->ram_block_discard_allowed != + group->ram_block_discard_allowed) { if (!QLIST_EMPTY(&group->device_list)) { - error_setg(errp, - "Inconsistent device balloon setting within group"); + error_setg(errp, "Inconsistent setting of support for discarding " + "RAM (e.g., balloon) within group"); close(fd); return -1; } - if (!group->balloon_allowed) { - group->balloon_allowed = true; - qemu_balloon_inhibit(false); + if (!group->ram_block_discard_allowed) { + group->ram_block_discard_allowed = true; + ram_block_discard_disable(false); } } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 6838bcc4b3..d020ea9f82 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2789,7 +2789,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) } /* - * Mediated devices *might* operate compatibly with memory ballooning, but + * Mediated devices *might* operate compatibly with discarding of RAM, but * we cannot know for certain, it depends on whether the mdev vendor driver * stays in sync with the active working set of the guest driver. Prevent * the x-balloon-allowed option unless this is minimally an mdev device. @@ -2802,7 +2802,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) trace_vfio_mdev(vdev->vbasedev.name, is_mdev); - if (vdev->vbasedev.balloon_allowed && !is_mdev) { + if (vdev->vbasedev.ram_block_discard_allowed && !is_mdev) { error_setg(errp, "x-balloon-allowed only potentially compatible " "with mdev devices"); vfio_put_group(group); @@ -3156,7 +3156,7 @@ static Property vfio_pci_dev_properties[] = { VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), DEFINE_PROP_BOOL("x-balloon-allowed", VFIOPCIDevice, - vbasedev.balloon_allowed, false), + vbasedev.ram_block_discard_allowed, false), DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false), DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false), DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false), diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index 83122424fa..0eda25c4e1 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -47,3 +47,14 @@ config VIRTIO_PMEM depends on VIRTIO depends on VIRTIO_PMEM_SUPPORTED select MEM_DEVICE + +config VIRTIO_MEM_SUPPORTED + bool + +config VIRTIO_MEM + bool + default y + depends on VIRTIO + depends on LINUX + depends on VIRTIO_MEM_SUPPORTED + select MEM_DEVICE diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 13e75f171f..fc91719b4a 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -5,6 +5,7 @@ obj-y += virtio.o obj-$(CONFIG_VHOST) += vhost.o vhost-backend.o common-obj-$(call lnot,$(CONFIG_VHOST)) += vhost-stub.o obj-$(CONFIG_VHOST_USER) += vhost-user.o +obj-$(CONFIG_VHOST_VDPA) += vhost-vdpa.o common-obj-$(CONFIG_VIRTIO_RNG) += virtio-rng.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o @@ -19,6 +20,8 @@ obj-$(call land,$(CONFIG_VHOST_USER_FS),$(CONFIG_VIRTIO_PCI)) += vhost-user-fs-p obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock-common.o vhost-vsock.o obj-$(CONFIG_VHOST_USER_VSOCK) += vhost-vsock-common.o vhost-user-vsock.o +obj-$(CONFIG_VIRTIO_MEM) += virtio-mem.o +common-obj-$(call land,$(CONFIG_VIRTIO_MEM),$(CONFIG_VIRTIO_PCI)) += virtio-mem-pci.o ifeq ($(CONFIG_VIRTIO_PCI),y) obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock-pci.o diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 6427a0047d..045e89cae6 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -74,3 +74,14 @@ virtio_iommu_get_domain(uint32_t domain_id) "Alloc domain=%d" virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d" virtio_iommu_translate_out(uint64_t virt_addr, uint64_t phys_addr, uint32_t sid) "0x%"PRIx64" -> 0x%"PRIx64 " for sid=%d" virtio_iommu_report_fault(uint8_t reason, uint32_t flags, uint32_t endpoint, uint64_t addr) "FAULT reason=%d flags=%d endpoint=%d address =0x%"PRIx64 +virtio_iommu_fill_resv_property(uint32_t devid, uint8_t subtype, uint64_t start, uint64_t end) "dev= %d, type=%d start=0x%"PRIx64" end=0x%"PRIx64 + +# virtio-mem.c +virtio_mem_send_response(uint16_t type) "type=%" PRIu16 +virtio_mem_plug_request(uint64_t addr, uint16_t nb_blocks) "addr=0x%" PRIx64 " nb_blocks=%" PRIu16 +virtio_mem_unplug_request(uint64_t addr, uint16_t nb_blocks) "addr=0x%" PRIx64 " nb_blocks=%" PRIu16 +virtio_mem_unplugged_all(void) "" +virtio_mem_unplug_all_request(void) "" +virtio_mem_resized_usable_region(uint64_t old_size, uint64_t new_size) "old_size=0x%" PRIx64 "new_size=0x%" PRIx64 +virtio_mem_state_request(uint64_t addr, uint16_t nb_blocks) "addr=0x%" PRIx64 " nb_blocks=%" PRIu16 +virtio_mem_state_response(uint16_t state) "state=%" PRIu16 diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 48905383f8..782b1d67d9 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -15,6 +15,7 @@ #include "qemu/main-loop.h" #include "standard-headers/linux/vhost_types.h" +#include "hw/virtio/vhost-vdpa.h" #ifdef CONFIG_VHOST_KERNEL #include <linux/vhost.h> #include <sys/ioctl.h> @@ -286,6 +287,11 @@ int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) dev->vhost_ops = &user_ops; break; #endif +#ifdef CONFIG_VHOST_VDPA + case VHOST_BACKEND_TYPE_VDPA: + dev->vhost_ops = &vdpa_ops; + break; +#endif default: error_report("Unknown vhost backend type"); r = -1; diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c new file mode 100644 index 0000000000..a3d17fe0f9 --- /dev/null +++ b/hw/virtio/vhost-vdpa.c @@ -0,0 +1,475 @@ +/* + * vhost-vdpa + * + * Copyright(c) 2017-2018 Intel Corporation. + * Copyright(c) 2020 Red Hat, 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. + * + */ + +#include "qemu/osdep.h" +#include <linux/vhost.h> +#include <linux/vfio.h> +#include <sys/eventfd.h> +#include <sys/ioctl.h> +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-backend.h" +#include "hw/virtio/virtio-net.h" +#include "hw/virtio/vhost-vdpa.h" +#include "qemu/main-loop.h" +#include <linux/kvm.h> +#include "sysemu/kvm.h" + +static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section) +{ + return (!memory_region_is_ram(section->mr) && + !memory_region_is_iommu(section->mr)) || + /* + * Sizing an enabled 64-bit BAR can cause spurious mappings to + * addresses in the upper part of the 64-bit address space. These + * are never accessed by the CPU and beyond the address width of + * some IOMMU hardware. TODO: VDPA should tell us the IOMMU width. + */ + section->offset_within_address_space & (1ULL << 63); +} + +static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, + void *vaddr, bool readonly) +{ + struct vhost_msg_v2 msg; + int fd = v->device_fd; + int ret = 0; + + msg.type = v->msg_type; + msg.iotlb.iova = iova; + msg.iotlb.size = size; + msg.iotlb.uaddr = (uint64_t)(uintptr_t)vaddr; + msg.iotlb.perm = readonly ? VHOST_ACCESS_RO : VHOST_ACCESS_RW; + msg.iotlb.type = VHOST_IOTLB_UPDATE; + + if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { + error_report("failed to write, fd=%d, errno=%d (%s)", + fd, errno, strerror(errno)); + return -EIO ; + } + + return ret; +} + +static int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, + hwaddr size) +{ + struct vhost_msg_v2 msg; + int fd = v->device_fd; + int ret = 0; + + msg.type = v->msg_type; + msg.iotlb.iova = iova; + msg.iotlb.size = size; + msg.iotlb.type = VHOST_IOTLB_INVALIDATE; + + if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { + error_report("failed to write, fd=%d, errno=%d (%s)", + fd, errno, strerror(errno)); + return -EIO ; + } + + return ret; +} + +static void vhost_vdpa_listener_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + hwaddr iova; + Int128 llend, llsize; + void *vaddr; + int ret; + + if (vhost_vdpa_listener_skipped_section(section)) { + return; + } + + if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != + (section->offset_within_region & ~TARGET_PAGE_MASK))) { + error_report("%s received unaligned region", __func__); + return; + } + + iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + llend = int128_make64(section->offset_within_address_space); + llend = int128_add(llend, section->size); + llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + + if (int128_ge(int128_make64(iova), llend)) { + return; + } + + memory_region_ref(section->mr); + + /* Here we assume that memory_region_is_ram(section->mr)==true */ + + vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + + llsize = int128_sub(llend, int128_make64(iova)); + + ret = vhost_vdpa_dma_map(v, iova, int128_get64(llsize), + vaddr, section->readonly); + if (ret) { + error_report("vhost vdpa map fail!"); + if (memory_region_is_ram_device(section->mr)) { + /* Allow unexpected mappings not to be fatal for RAM devices */ + error_report("map ram fail!"); + return ; + } + goto fail; + } + + return; + +fail: + if (memory_region_is_ram_device(section->mr)) { + error_report("failed to vdpa_dma_map. pci p2p may not work"); + return; + + } + /* + * On the initfn path, store the first error in the container so we + * can gracefully fail. Runtime, there's not much we can do other + * than throw a hardware error. + */ + error_report("vhost-vdpa: DMA mapping failed, unable to continue"); + return; + +} + +static void vhost_vdpa_listener_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + hwaddr iova; + Int128 llend, llsize; + int ret; + bool try_unmap = true; + + if (vhost_vdpa_listener_skipped_section(section)) { + return; + } + + if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != + (section->offset_within_region & ~TARGET_PAGE_MASK))) { + error_report("%s received unaligned region", __func__); + return; + } + + iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + llend = int128_make64(section->offset_within_address_space); + llend = int128_add(llend, section->size); + llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + + if (int128_ge(int128_make64(iova), llend)) { + return; + } + + llsize = int128_sub(llend, int128_make64(iova)); + + if (try_unmap) { + ret = vhost_vdpa_dma_unmap(v, iova, int128_get64(llsize)); + if (ret) { + error_report("vhost_vdpa dma unmap error!"); + } + } + + memory_region_unref(section->mr); +} +/* + * IOTLB API is used by vhost-vpda which requires incremental updating + * of the mapping. So we can not use generic vhost memory listener which + * depends on the addnop(). + */ +static const MemoryListener vhost_vdpa_memory_listener = { + .region_add = vhost_vdpa_listener_region_add, + .region_del = vhost_vdpa_listener_region_del, +}; + +static int vhost_vdpa_call(struct vhost_dev *dev, unsigned long int request, + void *arg) +{ + struct vhost_vdpa *v = dev->opaque; + int fd = v->device_fd; + + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); + + return ioctl(fd, request, arg); +} + +static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status) +{ + uint8_t s; + + if (vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &s)) { + return; + } + + s |= status; + + vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &s); +} + +static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque) +{ + struct vhost_vdpa *v; + uint64_t features; + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); + + v = opaque; + dev->opaque = opaque ; + vhost_vdpa_call(dev, VHOST_GET_FEATURES, &features); + dev->backend_features = features; + v->listener = vhost_vdpa_memory_listener; + v->msg_type = VHOST_IOTLB_MSG_V2; + + vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER); + + return 0; +} + +static int vhost_vdpa_cleanup(struct vhost_dev *dev) +{ + struct vhost_vdpa *v; + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); + v = dev->opaque; + memory_listener_unregister(&v->listener); + + dev->opaque = NULL; + return 0; +} + +static int vhost_vdpa_memslots_limit(struct vhost_dev *dev) +{ + return INT_MAX; +} + +static int vhost_vdpa_set_mem_table(struct vhost_dev *dev, + struct vhost_memory *mem) +{ + + if (mem->padding) { + return -1; + } + + return 0; +} + +static int vhost_vdpa_set_features(struct vhost_dev *dev, + uint64_t features) +{ + int ret; + ret = vhost_vdpa_call(dev, VHOST_SET_FEATURES, &features); + uint8_t status = 0; + if (ret) { + return ret; + } + vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK); + vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &status); + + return !(status & VIRTIO_CONFIG_S_FEATURES_OK); +} + +int vhost_vdpa_get_device_id(struct vhost_dev *dev, + uint32_t *device_id) +{ + return vhost_vdpa_call(dev, VHOST_VDPA_GET_DEVICE_ID, device_id); +} + +static int vhost_vdpa_reset_device(struct vhost_dev *dev) +{ + uint8_t status = 0; + + return vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status); +} + +static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx) +{ + assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); + + return idx - dev->vq_index; +} + +static int vhost_vdpa_set_vring_ready(struct vhost_dev *dev) +{ + int i; + for (i = 0; i < dev->nvqs; ++i) { + struct vhost_vring_state state = { + .index = dev->vq_index + i, + .num = 1, + }; + vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, &state); + } + return 0; +} + +static int vhost_vdpa_set_config(struct vhost_dev *dev, const uint8_t *data, + uint32_t offset, uint32_t size, + uint32_t flags) +{ + struct vhost_vdpa_config *config; + int ret; + unsigned long config_size = offsetof(struct vhost_vdpa_config, buf); + config = g_malloc(size + config_size); + if (config == NULL) { + return -1; + } + config->off = offset; + config->len = size; + memcpy(config->buf, data, size); + ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_CONFIG, config); + g_free(config); + return ret; +} + +static int vhost_vdpa_get_config(struct vhost_dev *dev, uint8_t *config, + uint32_t config_len) +{ + struct vhost_vdpa_config *v_config; + unsigned long config_size = offsetof(struct vhost_vdpa_config, buf); + int ret; + + v_config = g_malloc(config_len + config_size); + if (v_config == NULL) { + return -1; + } + v_config->len = config_len; + v_config->off = 0; + ret = vhost_vdpa_call(dev, VHOST_VDPA_GET_CONFIG, v_config); + memcpy(config, v_config->buf, config_len); + g_free(v_config); + return ret; + } + +static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) +{ + struct vhost_vdpa *v = dev->opaque; + if (started) { + uint8_t status = 0; + memory_listener_register(&v->listener, &address_space_memory); + vhost_vdpa_set_vring_ready(dev); + vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); + vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &status); + + return !(status & VIRTIO_CONFIG_S_DRIVER_OK); + } else { + vhost_vdpa_reset_device(dev); + vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER); + memory_listener_unregister(&v->listener); + + return 0; + } +} + +static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, + struct vhost_log *log) +{ + return vhost_vdpa_call(dev, VHOST_SET_LOG_BASE, &base); +} + +static int vhost_vdpa_set_vring_addr(struct vhost_dev *dev, + struct vhost_vring_addr *addr) +{ + return vhost_vdpa_call(dev, VHOST_SET_VRING_ADDR, addr); +} + +static int vhost_vdpa_set_vring_num(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_vdpa_call(dev, VHOST_SET_VRING_NUM, ring); +} + +static int vhost_vdpa_set_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_vdpa_call(dev, VHOST_SET_VRING_BASE, ring); +} + +static int vhost_vdpa_get_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return vhost_vdpa_call(dev, VHOST_GET_VRING_BASE, ring); +} + +static int vhost_vdpa_set_vring_kick(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_vdpa_call(dev, VHOST_SET_VRING_KICK, file); +} + +static int vhost_vdpa_set_vring_call(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_vdpa_call(dev, VHOST_SET_VRING_CALL, file); +} + +static int vhost_vdpa_get_features(struct vhost_dev *dev, + uint64_t *features) +{ + return vhost_vdpa_call(dev, VHOST_GET_FEATURES, features); +} + +static int vhost_vdpa_set_owner(struct vhost_dev *dev) +{ + return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); +} + +static int vhost_vdpa_vq_get_addr(struct vhost_dev *dev, + struct vhost_vring_addr *addr, struct vhost_virtqueue *vq) +{ + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); + addr->desc_user_addr = (uint64_t)(unsigned long)vq->desc_phys; + addr->avail_user_addr = (uint64_t)(unsigned long)vq->avail_phys; + addr->used_user_addr = (uint64_t)(unsigned long)vq->used_phys; + return 0; +} + +static bool vhost_vdpa_force_iommu(struct vhost_dev *dev) +{ + return true; +} + +const VhostOps vdpa_ops = { + .backend_type = VHOST_BACKEND_TYPE_VDPA, + .vhost_backend_init = vhost_vdpa_init, + .vhost_backend_cleanup = vhost_vdpa_cleanup, + .vhost_set_log_base = vhost_vdpa_set_log_base, + .vhost_set_vring_addr = vhost_vdpa_set_vring_addr, + .vhost_set_vring_num = vhost_vdpa_set_vring_num, + .vhost_set_vring_base = vhost_vdpa_set_vring_base, + .vhost_get_vring_base = vhost_vdpa_get_vring_base, + .vhost_set_vring_kick = vhost_vdpa_set_vring_kick, + .vhost_set_vring_call = vhost_vdpa_set_vring_call, + .vhost_get_features = vhost_vdpa_get_features, + .vhost_set_owner = vhost_vdpa_set_owner, + .vhost_set_vring_endian = NULL, + .vhost_backend_memslots_limit = vhost_vdpa_memslots_limit, + .vhost_set_mem_table = vhost_vdpa_set_mem_table, + .vhost_set_features = vhost_vdpa_set_features, + .vhost_reset_device = vhost_vdpa_reset_device, + .vhost_get_vq_index = vhost_vdpa_get_vq_index, + .vhost_get_config = vhost_vdpa_get_config, + .vhost_set_config = vhost_vdpa_set_config, + .vhost_requires_shm_log = NULL, + .vhost_migration_done = NULL, + .vhost_backend_can_merge = NULL, + .vhost_net_set_mtu = NULL, + .vhost_set_iotlb_callback = NULL, + .vhost_send_device_iotlb_msg = NULL, + .vhost_dev_start = vhost_vdpa_dev_start, + .vhost_get_device_id = vhost_vdpa_get_device_id, + .vhost_vq_get_addr = vhost_vdpa_vq_get_addr, + .vhost_force_iommu = vhost_vdpa_force_iommu, +}; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 5fd25fe520..1a1384e7a6 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -773,15 +773,25 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev, struct vhost_virtqueue *vq, unsigned idx, bool enable_log) { - struct vhost_vring_addr addr = { - .index = idx, - .desc_user_addr = (uint64_t)(unsigned long)vq->desc, - .avail_user_addr = (uint64_t)(unsigned long)vq->avail, - .used_user_addr = (uint64_t)(unsigned long)vq->used, - .log_guest_addr = vq->used_phys, - .flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0, - }; - int r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr); + struct vhost_vring_addr addr; + int r; + memset(&addr, 0, sizeof(struct vhost_vring_addr)); + + if (dev->vhost_ops->vhost_vq_get_addr) { + r = dev->vhost_ops->vhost_vq_get_addr(dev, &addr, vq); + if (r < 0) { + VHOST_OPS_DEBUG("vhost_vq_get_addr failed"); + return -errno; + } + } else { + addr.desc_user_addr = (uint64_t)(unsigned long)vq->desc; + addr.avail_user_addr = (uint64_t)(unsigned long)vq->avail; + addr.used_user_addr = (uint64_t)(unsigned long)vq->used; + } + addr.index = idx; + addr.log_guest_addr = vq->used_phys; + addr.flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0; + r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr); if (r < 0) { VHOST_OPS_DEBUG("vhost_set_vring_addr failed"); return -errno; @@ -800,6 +810,11 @@ static int vhost_dev_set_features(struct vhost_dev *dev, if (!vhost_dev_has_iommu(dev)) { features &= ~(0x1ULL << VIRTIO_F_IOMMU_PLATFORM); } + if (dev->vhost_ops->vhost_force_iommu) { + if (dev->vhost_ops->vhost_force_iommu(dev) == true) { + features |= 0x1ULL << VIRTIO_F_IOMMU_PLATFORM; + } + } r = dev->vhost_ops->vhost_set_features(dev, features); if (r < 0) { VHOST_OPS_DEBUG("vhost_set_features failed"); @@ -1685,9 +1700,15 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) goto fail_log; } } - - if (vhost_dev_has_iommu(hdev)) { - hdev->vhost_ops->vhost_set_iotlb_callback(hdev, true); + if (hdev->vhost_ops->vhost_dev_start) { + r = hdev->vhost_ops->vhost_dev_start(hdev, true); + if (r) { + goto fail_log; + } + } + if (vhost_dev_has_iommu(hdev) && + hdev->vhost_ops->vhost_set_iotlb_callback) { + hdev->vhost_ops->vhost_set_iotlb_callback(hdev, true); /* Update used ring information for IOTLB to work correctly, * vhost-kernel code requires for this.*/ @@ -1722,6 +1743,9 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) /* should only be called after backend is connected */ assert(hdev->vhost_ops); + if (hdev->vhost_ops->vhost_dev_start) { + hdev->vhost_ops->vhost_dev_start(hdev, false); + } for (i = 0; i < hdev->nvqs; ++i) { vhost_virtqueue_stop(hdev, vdev, @@ -1730,7 +1754,9 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) } if (vhost_dev_has_iommu(hdev)) { - hdev->vhost_ops->vhost_set_iotlb_callback(hdev, false); + if (hdev->vhost_ops->vhost_set_iotlb_callback) { + hdev->vhost_ops->vhost_set_iotlb_callback(hdev, false); + } memory_listener_unregister(&hdev->iommu_listener); } vhost_log_put(hdev, true); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 10507b2a43..ae31f0817a 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -63,6 +63,12 @@ static bool virtio_balloon_pbp_matches(PartiallyBalloonedPage *pbp, return pbp->base_gpa == base_gpa; } +static bool virtio_balloon_inhibited(void) +{ + /* Postcopy cannot deal with concurrent discards, so it's special. */ + return ram_block_discard_is_disabled() || migration_in_incoming_postcopy(); +} + static void balloon_inflate_page(VirtIOBalloon *balloon, MemoryRegion *mr, hwaddr mr_offset, PartiallyBalloonedPage *pbp) @@ -336,7 +342,7 @@ static void virtio_balloon_handle_report(VirtIODevice *vdev, VirtQueue *vq) * accessible by another device or process, or if the guest is * expecting it to retain a non-zero value. */ - if (qemu_balloon_is_inhibited() || dev->poison_val) { + if (virtio_balloon_inhibited() || dev->poison_val) { goto skip_element; } @@ -421,7 +427,7 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) trace_virtio_balloon_handle_output(memory_region_name(section.mr), pa); - if (!qemu_balloon_is_inhibited()) { + if (!virtio_balloon_inhibited()) { if (vq == s->ivq) { balloon_inflate_page(s, section.mr, section.offset_within_region, &pbp); @@ -628,8 +634,13 @@ static void virtio_balloon_free_page_done(VirtIOBalloon *s) { VirtIODevice *vdev = VIRTIO_DEVICE(s); - s->free_page_report_status = FREE_PAGE_REPORT_S_DONE; - virtio_notify_config(vdev); + if (s->free_page_report_status != FREE_PAGE_REPORT_S_DONE) { + /* See virtio_balloon_free_page_stop() */ + qemu_mutex_lock(&s->free_page_lock); + s->free_page_report_status = FREE_PAGE_REPORT_S_DONE; + qemu_mutex_unlock(&s->free_page_lock); + virtio_notify_config(vdev); + } } static int @@ -653,17 +664,26 @@ virtio_balloon_free_page_report_notify(NotifierWithReturn *n, void *data) case PRECOPY_NOTIFY_SETUP: precopy_enable_free_page_optimization(); break; - case PRECOPY_NOTIFY_COMPLETE: - case PRECOPY_NOTIFY_CLEANUP: case PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC: virtio_balloon_free_page_stop(dev); break; case PRECOPY_NOTIFY_AFTER_BITMAP_SYNC: if (vdev->vm_running) { virtio_balloon_free_page_start(dev); - } else { - virtio_balloon_free_page_done(dev); + break; } + /* + * Set S_DONE before migrating the vmstate, so the guest will reuse + * all hinted pages once running on the destination. Fall through. + */ + case PRECOPY_NOTIFY_CLEANUP: + /* + * Especially, if something goes wrong during precopy or if migration + * is canceled, we have to properly communicate S_DONE to the VM. + */ + virtio_balloon_free_page_done(dev); + break; + case PRECOPY_NOTIFY_COMPLETE: break; default: virtio_error(vdev, "%s: %d reason unknown", __func__, pnd->reason); diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 4588361d6b..592abc9279 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -33,6 +33,9 @@ struct VirtIOIOMMUPCI { static Property virtio_iommu_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_ARRAY("reserved-regions", VirtIOIOMMUPCI, + vdev.nb_reserved_regions, vdev.reserved_regions, + qdev_prop_reserved_region, ReservedRegion), DEFINE_PROP_END_OF_LIST(), }; @@ -40,6 +43,7 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); if (!qdev_get_machine_hotplug_handler(DEVICE(vpci_dev))) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); @@ -54,6 +58,13 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) "-no-acpi\n"); return; } + for (int i = 0; i < s->nb_reserved_regions; i++) { + if (s->reserved_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_RESERVED && + s->reserved_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_MSI) { + error_setg(errp, "reserved region %d has an invalid type", i); + error_append_hint(errp, "Valid values are 0 and 1\n"); + } + } object_property_set_link(OBJECT(dev), OBJECT(pci_get_bus(&vpci_dev->pci_dev)), "primary-bus", &error_abort); diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 483883ec1d..b39e836181 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -38,6 +38,7 @@ /* Max size */ #define VIOMMU_DEFAULT_QUEUE_SIZE 256 +#define VIOMMU_PROBE_SIZE 512 typedef struct VirtIOIOMMUDomain { uint32_t id; @@ -378,6 +379,65 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s, return ret; } +static ssize_t virtio_iommu_fill_resv_mem_prop(VirtIOIOMMU *s, uint32_t ep, + uint8_t *buf, size_t free) +{ + struct virtio_iommu_probe_resv_mem prop = {}; + size_t size = sizeof(prop), length = size - sizeof(prop.head), total; + int i; + + total = size * s->nb_reserved_regions; + + if (total > free) { + return -ENOSPC; + } + + for (i = 0; i < s->nb_reserved_regions; i++) { + unsigned subtype = s->reserved_regions[i].type; + + assert(subtype == VIRTIO_IOMMU_RESV_MEM_T_RESERVED || + subtype == VIRTIO_IOMMU_RESV_MEM_T_MSI); + prop.head.type = cpu_to_le16(VIRTIO_IOMMU_PROBE_T_RESV_MEM); + prop.head.length = cpu_to_le16(length); + prop.subtype = subtype; + prop.start = cpu_to_le64(s->reserved_regions[i].low); + prop.end = cpu_to_le64(s->reserved_regions[i].high); + + memcpy(buf, &prop, size); + + trace_virtio_iommu_fill_resv_property(ep, prop.subtype, + prop.start, prop.end); + buf += size; + } + return total; +} + +/** + * virtio_iommu_probe - Fill the probe request buffer with + * the properties the device is able to return + */ +static int virtio_iommu_probe(VirtIOIOMMU *s, + struct virtio_iommu_req_probe *req, + uint8_t *buf) +{ + uint32_t ep_id = le32_to_cpu(req->endpoint); + size_t free = VIOMMU_PROBE_SIZE; + ssize_t count; + + if (!virtio_iommu_mr(s, ep_id)) { + return VIRTIO_IOMMU_S_NOENT; + } + + count = virtio_iommu_fill_resv_mem_prop(s, ep_id, buf, free); + if (count < 0) { + return VIRTIO_IOMMU_S_INVAL; + } + buf += count; + free -= count; + + return VIRTIO_IOMMU_S_OK; +} + static int virtio_iommu_iov_to_req(struct iovec *iov, unsigned int iov_cnt, void *req, size_t req_sz) @@ -407,15 +467,27 @@ virtio_iommu_handle_req(detach) virtio_iommu_handle_req(map) virtio_iommu_handle_req(unmap) +static int virtio_iommu_handle_probe(VirtIOIOMMU *s, + struct iovec *iov, + unsigned int iov_cnt, + uint8_t *buf) +{ + struct virtio_iommu_req_probe req; + int ret = virtio_iommu_iov_to_req(iov, iov_cnt, &req, sizeof(req)); + + return ret ? ret : virtio_iommu_probe(s, &req, buf); +} + static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) { VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); struct virtio_iommu_req_head head; struct virtio_iommu_req_tail tail = {}; + size_t output_size = sizeof(tail), sz; VirtQueueElement *elem; unsigned int iov_cnt; struct iovec *iov; - size_t sz; + void *buf = NULL; for (;;) { elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); @@ -452,6 +524,17 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) case VIRTIO_IOMMU_T_UNMAP: tail.status = virtio_iommu_handle_unmap(s, iov, iov_cnt); break; + case VIRTIO_IOMMU_T_PROBE: + { + struct virtio_iommu_req_tail *ptail; + + output_size = s->config.probe_size + sizeof(tail); + buf = g_malloc0(output_size); + + ptail = (struct virtio_iommu_req_tail *) + (buf + s->config.probe_size); + ptail->status = virtio_iommu_handle_probe(s, iov, iov_cnt, buf); + } default: tail.status = VIRTIO_IOMMU_S_UNSUPP; } @@ -459,12 +542,13 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) out: sz = iov_from_buf(elem->in_sg, elem->in_num, 0, - &tail, sizeof(tail)); - assert(sz == sizeof(tail)); + buf ? buf : &tail, output_size); + assert(sz == output_size); - virtqueue_push(vq, elem, sizeof(tail)); + virtqueue_push(vq, elem, sz); virtio_notify(vdev, vq); g_free(elem); + g_free(buf); } } @@ -523,6 +607,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, uint32_t sid, flags; bool bypass_allowed; bool found; + int i; interval.low = addr; interval.high = addr + 1; @@ -556,6 +641,25 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto unlock; } + for (i = 0; i < s->nb_reserved_regions; i++) { + ReservedRegion *reg = &s->reserved_regions[i]; + + if (addr >= reg->low && addr <= reg->high) { + switch (reg->type) { + case VIRTIO_IOMMU_RESV_MEM_T_MSI: + entry.perm = flag; + break; + case VIRTIO_IOMMU_RESV_MEM_T_RESERVED: + default: + virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_MAPPING, + VIRTIO_IOMMU_FAULT_F_ADDRESS, + sid, addr); + break; + } + goto unlock; + } + } + if (!ep->domain) { if (!bypass_allowed) { error_report_once("%s %02x:%02x.%01x not attached to any domain", @@ -667,6 +771,7 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) s->config.page_size_mask = TARGET_PAGE_MASK; s->config.input_range.end = -1UL; s->config.domain_range.end = 32; + s->config.probe_size = VIOMMU_PROBE_SIZE; virtio_add_feature(&s->features, VIRTIO_RING_F_EVENT_IDX); virtio_add_feature(&s->features, VIRTIO_RING_F_INDIRECT_DESC); @@ -676,6 +781,7 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP); virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS); virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MMIO); + virtio_add_feature(&s->features, VIRTIO_IOMMU_F_PROBE); qemu_mutex_init(&s->mutex); diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c new file mode 100644 index 0000000000..1a8e854123 --- /dev/null +++ b/hw/virtio/virtio-mem-pci.c @@ -0,0 +1,157 @@ +/* + * Virtio MEM PCI device + * + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * David Hildenbrand <david@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "virtio-mem-pci.h" +#include "hw/mem/memory-device.h" +#include "qapi/error.h" +#include "qapi/qapi-events-misc.h" + +static void virtio_mem_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOMEMPCI *mem_pci = VIRTIO_MEM_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&mem_pci->vdev); + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_mem_pci_set_addr(MemoryDeviceState *md, uint64_t addr, + Error **errp) +{ + object_property_set_uint(OBJECT(md), addr, VIRTIO_MEM_ADDR_PROP, errp); +} + +static uint64_t virtio_mem_pci_get_addr(const MemoryDeviceState *md) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_ADDR_PROP, + &error_abort); +} + +static MemoryRegion *virtio_mem_pci_get_memory_region(MemoryDeviceState *md, + Error **errp) +{ + VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md); + VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev); + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + return vmc->get_memory_region(vmem, errp); +} + +static uint64_t virtio_mem_pci_get_plugged_size(const MemoryDeviceState *md, + Error **errp) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_SIZE_PROP, + errp); +} + +static void virtio_mem_pci_fill_device_info(const MemoryDeviceState *md, + MemoryDeviceInfo *info) +{ + VirtioMEMDeviceInfo *vi = g_new0(VirtioMEMDeviceInfo, 1); + VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md); + VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev); + VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem); + DeviceState *dev = DEVICE(md); + + if (dev->id) { + vi->has_id = true; + vi->id = g_strdup(dev->id); + } + + /* let the real device handle everything else */ + vpc->fill_device_info(vmem, vi); + + info->u.virtio_mem.data = vi; + info->type = MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM; +} + +static void virtio_mem_pci_size_change_notify(Notifier *notifier, void *data) +{ + VirtIOMEMPCI *pci_mem = container_of(notifier, VirtIOMEMPCI, + size_change_notifier); + DeviceState *dev = DEVICE(pci_mem); + const uint64_t * const size_p = data; + const char *id = NULL; + + if (dev->id) { + id = g_strdup(dev->id); + } + + qapi_event_send_memory_device_size_change(!!id, id, *size_p); +} + +static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass); + + k->realize = virtio_mem_pci_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_MEM; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_OTHERS; + + mdc->get_addr = virtio_mem_pci_get_addr; + mdc->set_addr = virtio_mem_pci_set_addr; + mdc->get_plugged_size = virtio_mem_pci_get_plugged_size; + mdc->get_memory_region = virtio_mem_pci_get_memory_region; + mdc->fill_device_info = virtio_mem_pci_fill_device_info; +} + +static void virtio_mem_pci_instance_init(Object *obj) +{ + VirtIOMEMPCI *dev = VIRTIO_MEM_PCI(obj); + VirtIOMEMClass *vmc; + VirtIOMEM *vmem; + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_MEM); + + dev->size_change_notifier.notify = virtio_mem_pci_size_change_notify; + vmem = VIRTIO_MEM(&dev->vdev); + vmc = VIRTIO_MEM_GET_CLASS(vmem); + /* + * We never remove the notifier again, as we expect both devices to + * disappear at the same time. + */ + vmc->add_size_change_notifier(vmem, &dev->size_change_notifier); + + object_property_add_alias(obj, VIRTIO_MEM_BLOCK_SIZE_PROP, + OBJECT(&dev->vdev), VIRTIO_MEM_BLOCK_SIZE_PROP); + object_property_add_alias(obj, VIRTIO_MEM_SIZE_PROP, OBJECT(&dev->vdev), + VIRTIO_MEM_SIZE_PROP); + object_property_add_alias(obj, VIRTIO_MEM_REQUESTED_SIZE_PROP, + OBJECT(&dev->vdev), + VIRTIO_MEM_REQUESTED_SIZE_PROP); +} + +static const VirtioPCIDeviceTypeInfo virtio_mem_pci_info = { + .base_name = TYPE_VIRTIO_MEM_PCI, + .generic_name = "virtio-mem-pci", + .instance_size = sizeof(VirtIOMEMPCI), + .instance_init = virtio_mem_pci_instance_init, + .class_init = virtio_mem_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_MEMORY_DEVICE }, + { } + }, +}; + +static void virtio_mem_pci_register_types(void) +{ + virtio_pci_types_register(&virtio_mem_pci_info); +} +type_init(virtio_mem_pci_register_types) diff --git a/hw/virtio/virtio-mem-pci.h b/hw/virtio/virtio-mem-pci.h new file mode 100644 index 0000000000..b51a28b275 --- /dev/null +++ b/hw/virtio/virtio-mem-pci.h @@ -0,0 +1,34 @@ +/* + * Virtio MEM PCI device + * + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * David Hildenbrand <david@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_VIRTIO_MEM_PCI_H +#define QEMU_VIRTIO_MEM_PCI_H + +#include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-mem.h" + +typedef struct VirtIOMEMPCI VirtIOMEMPCI; + +/* + * virtio-mem-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_MEM_PCI "virtio-mem-pci-base" +#define VIRTIO_MEM_PCI(obj) \ + OBJECT_CHECK(VirtIOMEMPCI, (obj), TYPE_VIRTIO_MEM_PCI) + +struct VirtIOMEMPCI { + VirtIOPCIProxy parent_obj; + VirtIOMEM vdev; + Notifier size_change_notifier; +}; + +#endif /* QEMU_VIRTIO_MEM_PCI_H */ diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c new file mode 100644 index 0000000000..65850530e7 --- /dev/null +++ b/hw/virtio/virtio-mem.c @@ -0,0 +1,873 @@ +/* + * Virtio MEM device + * + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * David Hildenbrand <david@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/iov.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/units.h" +#include "sysemu/numa.h" +#include "sysemu/sysemu.h" +#include "sysemu/reset.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/virtio-access.h" +#include "hw/virtio/virtio-mem.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "exec/ram_addr.h" +#include "migration/misc.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "config-devices.h" +#include "trace.h" + +/* + * Use QEMU_VMALLOC_ALIGN, so no THP will have to be split when unplugging + * memory (e.g., 2MB on x86_64). + */ +#define VIRTIO_MEM_MIN_BLOCK_SIZE QEMU_VMALLOC_ALIGN +/* + * Size the usable region bigger than the requested size if possible. Esp. + * Linux guests will only add (aligned) memory blocks in case they fully + * fit into the usable region, but plug+online only a subset of the pages. + * The memory block size corresponds mostly to the section size. + * + * This allows e.g., to add 20MB with a section size of 128MB on x86_64, and + * a section size of 1GB on arm64 (as long as the start address is properly + * aligned, similar to ordinary DIMMs). + * + * We can change this at any time and maybe even make it configurable if + * necessary (as the section size can change). But it's more likely that the + * section size will rather get smaller and not bigger over time. + */ +#if defined(TARGET_X86_64) || defined(TARGET_I386) +#define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB)) +#else +#error VIRTIO_MEM_USABLE_EXTENT not defined +#endif + +static bool virtio_mem_is_busy(void) +{ + /* + * Postcopy cannot handle concurrent discards and we don't want to migrate + * pages on-demand with stale content when plugging new blocks. + * + * For precopy, we don't want unplugged blocks in our migration stream, and + * when plugging new blocks, the page content might differ between source + * and destination (observable by the guest when not initializing pages + * after plugging them) until we're running on the destination (as we didn't + * migrate these blocks when they were unplugged). + */ + return migration_in_incoming_postcopy() || !migration_is_idle(); +} + +static bool virtio_mem_test_bitmap(VirtIOMEM *vmem, uint64_t start_gpa, + uint64_t size, bool plugged) +{ + const unsigned long first_bit = (start_gpa - vmem->addr) / vmem->block_size; + const unsigned long last_bit = first_bit + (size / vmem->block_size) - 1; + unsigned long found_bit; + + /* We fake a shorter bitmap to avoid searching too far. */ + if (plugged) { + found_bit = find_next_zero_bit(vmem->bitmap, last_bit + 1, first_bit); + } else { + found_bit = find_next_bit(vmem->bitmap, last_bit + 1, first_bit); + } + return found_bit > last_bit; +} + +static void virtio_mem_set_bitmap(VirtIOMEM *vmem, uint64_t start_gpa, + uint64_t size, bool plugged) +{ + const unsigned long bit = (start_gpa - vmem->addr) / vmem->block_size; + const unsigned long nbits = size / vmem->block_size; + + if (plugged) { + bitmap_set(vmem->bitmap, bit, nbits); + } else { + bitmap_clear(vmem->bitmap, bit, nbits); + } +} + +static void virtio_mem_send_response(VirtIOMEM *vmem, VirtQueueElement *elem, + struct virtio_mem_resp *resp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(vmem); + VirtQueue *vq = vmem->vq; + + trace_virtio_mem_send_response(le16_to_cpu(resp->type)); + iov_from_buf(elem->in_sg, elem->in_num, 0, resp, sizeof(*resp)); + + virtqueue_push(vq, elem, sizeof(*resp)); + virtio_notify(vdev, vq); +} + +static void virtio_mem_send_response_simple(VirtIOMEM *vmem, + VirtQueueElement *elem, + uint16_t type) +{ + struct virtio_mem_resp resp = { + .type = cpu_to_le16(type), + }; + + virtio_mem_send_response(vmem, elem, &resp); +} + +static bool virtio_mem_valid_range(VirtIOMEM *vmem, uint64_t gpa, uint64_t size) +{ + if (!QEMU_IS_ALIGNED(gpa, vmem->block_size)) { + return false; + } + if (gpa + size < gpa || !size) { + return false; + } + if (gpa < vmem->addr || gpa >= vmem->addr + vmem->usable_region_size) { + return false; + } + if (gpa + size > vmem->addr + vmem->usable_region_size) { + return false; + } + return true; +} + +static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, + uint64_t size, bool plug) +{ + const uint64_t offset = start_gpa - vmem->addr; + int ret; + + if (virtio_mem_is_busy()) { + return -EBUSY; + } + + if (!plug) { + ret = ram_block_discard_range(vmem->memdev->mr.ram_block, offset, size); + if (ret) { + error_report("Unexpected error discarding RAM: %s", + strerror(-ret)); + return -EBUSY; + } + } + virtio_mem_set_bitmap(vmem, start_gpa, size, plug); + return 0; +} + +static int virtio_mem_state_change_request(VirtIOMEM *vmem, uint64_t gpa, + uint16_t nb_blocks, bool plug) +{ + const uint64_t size = nb_blocks * vmem->block_size; + int ret; + + if (!virtio_mem_valid_range(vmem, gpa, size)) { + return VIRTIO_MEM_RESP_ERROR; + } + + if (plug && (vmem->size + size > vmem->requested_size)) { + return VIRTIO_MEM_RESP_NACK; + } + + /* test if really all blocks are in the opposite state */ + if (!virtio_mem_test_bitmap(vmem, gpa, size, !plug)) { + return VIRTIO_MEM_RESP_ERROR; + } + + ret = virtio_mem_set_block_state(vmem, gpa, size, plug); + if (ret) { + return VIRTIO_MEM_RESP_BUSY; + } + if (plug) { + vmem->size += size; + } else { + vmem->size -= size; + } + notifier_list_notify(&vmem->size_change_notifiers, &vmem->size); + return VIRTIO_MEM_RESP_ACK; +} + +static void virtio_mem_plug_request(VirtIOMEM *vmem, VirtQueueElement *elem, + struct virtio_mem_req *req) +{ + const uint64_t gpa = le64_to_cpu(req->u.plug.addr); + const uint16_t nb_blocks = le16_to_cpu(req->u.plug.nb_blocks); + uint16_t type; + + trace_virtio_mem_plug_request(gpa, nb_blocks); + type = virtio_mem_state_change_request(vmem, gpa, nb_blocks, true); + virtio_mem_send_response_simple(vmem, elem, type); +} + +static void virtio_mem_unplug_request(VirtIOMEM *vmem, VirtQueueElement *elem, + struct virtio_mem_req *req) +{ + const uint64_t gpa = le64_to_cpu(req->u.unplug.addr); + const uint16_t nb_blocks = le16_to_cpu(req->u.unplug.nb_blocks); + uint16_t type; + + trace_virtio_mem_unplug_request(gpa, nb_blocks); + type = virtio_mem_state_change_request(vmem, gpa, nb_blocks, false); + virtio_mem_send_response_simple(vmem, elem, type); +} + +static void virtio_mem_resize_usable_region(VirtIOMEM *vmem, + uint64_t requested_size, + bool can_shrink) +{ + uint64_t newsize = MIN(memory_region_size(&vmem->memdev->mr), + requested_size + VIRTIO_MEM_USABLE_EXTENT); + + if (!requested_size) { + newsize = 0; + } + + if (newsize < vmem->usable_region_size && !can_shrink) { + return; + } + + trace_virtio_mem_resized_usable_region(vmem->usable_region_size, newsize); + vmem->usable_region_size = newsize; +} + +static int virtio_mem_unplug_all(VirtIOMEM *vmem) +{ + RAMBlock *rb = vmem->memdev->mr.ram_block; + int ret; + + if (virtio_mem_is_busy()) { + return -EBUSY; + } + + ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); + if (ret) { + error_report("Unexpected error discarding RAM: %s", strerror(-ret)); + return -EBUSY; + } + bitmap_clear(vmem->bitmap, 0, vmem->bitmap_size); + if (vmem->size) { + vmem->size = 0; + notifier_list_notify(&vmem->size_change_notifiers, &vmem->size); + } + trace_virtio_mem_unplugged_all(); + virtio_mem_resize_usable_region(vmem, vmem->requested_size, true); + return 0; +} + +static void virtio_mem_unplug_all_request(VirtIOMEM *vmem, + VirtQueueElement *elem) +{ + trace_virtio_mem_unplug_all_request(); + if (virtio_mem_unplug_all(vmem)) { + virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_BUSY); + } else { + virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_ACK); + } +} + +static void virtio_mem_state_request(VirtIOMEM *vmem, VirtQueueElement *elem, + struct virtio_mem_req *req) +{ + const uint16_t nb_blocks = le16_to_cpu(req->u.state.nb_blocks); + const uint64_t gpa = le64_to_cpu(req->u.state.addr); + const uint64_t size = nb_blocks * vmem->block_size; + struct virtio_mem_resp resp = { + .type = cpu_to_le16(VIRTIO_MEM_RESP_ACK), + }; + + trace_virtio_mem_state_request(gpa, nb_blocks); + if (!virtio_mem_valid_range(vmem, gpa, size)) { + virtio_mem_send_response_simple(vmem, elem, VIRTIO_MEM_RESP_ERROR); + return; + } + + if (virtio_mem_test_bitmap(vmem, gpa, size, true)) { + resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_PLUGGED); + } else if (virtio_mem_test_bitmap(vmem, gpa, size, false)) { + resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_UNPLUGGED); + } else { + resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_MIXED); + } + trace_virtio_mem_state_response(le16_to_cpu(resp.u.state.state)); + virtio_mem_send_response(vmem, elem, &resp); +} + +static void virtio_mem_handle_request(VirtIODevice *vdev, VirtQueue *vq) +{ + const int len = sizeof(struct virtio_mem_req); + VirtIOMEM *vmem = VIRTIO_MEM(vdev); + VirtQueueElement *elem; + struct virtio_mem_req req; + uint16_t type; + + while (true) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + return; + } + + if (iov_to_buf(elem->out_sg, elem->out_num, 0, &req, len) < len) { + virtio_error(vdev, "virtio-mem protocol violation: invalid request" + " size: %d", len); + g_free(elem); + return; + } + + if (iov_size(elem->in_sg, elem->in_num) < + sizeof(struct virtio_mem_resp)) { + virtio_error(vdev, "virtio-mem protocol violation: not enough space" + " for response: %zu", + iov_size(elem->in_sg, elem->in_num)); + g_free(elem); + return; + } + + type = le16_to_cpu(req.type); + switch (type) { + case VIRTIO_MEM_REQ_PLUG: + virtio_mem_plug_request(vmem, elem, &req); + break; + case VIRTIO_MEM_REQ_UNPLUG: + virtio_mem_unplug_request(vmem, elem, &req); + break; + case VIRTIO_MEM_REQ_UNPLUG_ALL: + virtio_mem_unplug_all_request(vmem, elem); + break; + case VIRTIO_MEM_REQ_STATE: + virtio_mem_state_request(vmem, elem, &req); + break; + default: + virtio_error(vdev, "virtio-mem protocol violation: unknown request" + " type: %d", type); + g_free(elem); + return; + } + + g_free(elem); + } +} + +static void virtio_mem_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + VirtIOMEM *vmem = VIRTIO_MEM(vdev); + struct virtio_mem_config *config = (void *) config_data; + + config->block_size = cpu_to_le64(vmem->block_size); + config->node_id = cpu_to_le16(vmem->node); + config->requested_size = cpu_to_le64(vmem->requested_size); + config->plugged_size = cpu_to_le64(vmem->size); + config->addr = cpu_to_le64(vmem->addr); + config->region_size = cpu_to_le64(memory_region_size(&vmem->memdev->mr)); + config->usable_region_size = cpu_to_le64(vmem->usable_region_size); +} + +static uint64_t virtio_mem_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + + if (ms->numa_state) { +#if defined(CONFIG_ACPI) + virtio_add_feature(&features, VIRTIO_MEM_F_ACPI_PXM); +#endif + } + return features; +} + +static void virtio_mem_system_reset(void *opaque) +{ + VirtIOMEM *vmem = VIRTIO_MEM(opaque); + + /* + * During usual resets, we will unplug all memory and shrink the usable + * region size. This is, however, not possible in all scenarios. Then, + * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL). + */ + virtio_mem_unplug_all(vmem); +} + +static void virtio_mem_device_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + int nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0; + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOMEM *vmem = VIRTIO_MEM(dev); + uint64_t page_size; + RAMBlock *rb; + int ret; + + if (!vmem->memdev) { + error_setg(errp, "'%s' property is not set", VIRTIO_MEM_MEMDEV_PROP); + return; + } else if (host_memory_backend_is_mapped(vmem->memdev)) { + char *path = object_get_canonical_path_component(OBJECT(vmem->memdev)); + + error_setg(errp, "'%s' property specifies a busy memdev: %s", + VIRTIO_MEM_MEMDEV_PROP, path); + g_free(path); + return; + } else if (!memory_region_is_ram(&vmem->memdev->mr) || + memory_region_is_rom(&vmem->memdev->mr) || + !vmem->memdev->mr.ram_block) { + error_setg(errp, "'%s' property specifies an unsupported memdev", + VIRTIO_MEM_MEMDEV_PROP); + return; + } + + if ((nb_numa_nodes && vmem->node >= nb_numa_nodes) || + (!nb_numa_nodes && vmem->node)) { + error_setg(errp, "'%s' property has value '%" PRIu32 "', which exceeds" + "the number of numa nodes: %d", VIRTIO_MEM_NODE_PROP, + vmem->node, nb_numa_nodes ? nb_numa_nodes : 1); + return; + } + + if (enable_mlock) { + error_setg(errp, "Incompatible with mlock"); + return; + } + + rb = vmem->memdev->mr.ram_block; + page_size = qemu_ram_pagesize(rb); + + if (vmem->block_size < page_size) { + error_setg(errp, "'%s' property has to be at least the page size (0x%" + PRIx64 ")", VIRTIO_MEM_BLOCK_SIZE_PROP, page_size); + return; + } else if (!QEMU_IS_ALIGNED(vmem->requested_size, vmem->block_size)) { + error_setg(errp, "'%s' property has to be multiples of '%s' (0x%" PRIx64 + ")", VIRTIO_MEM_REQUESTED_SIZE_PROP, + VIRTIO_MEM_BLOCK_SIZE_PROP, vmem->block_size); + return; + } else if (!QEMU_IS_ALIGNED(memory_region_size(&vmem->memdev->mr), + vmem->block_size)) { + error_setg(errp, "'%s' property memdev size has to be multiples of" + "'%s' (0x%" PRIx64 ")", VIRTIO_MEM_MEMDEV_PROP, + VIRTIO_MEM_BLOCK_SIZE_PROP, vmem->block_size); + return; + } + + if (ram_block_discard_require(true)) { + error_setg(errp, "Discarding RAM is disabled"); + return; + } + + ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); + if (ret) { + error_setg_errno(errp, -ret, "Unexpected error discarding RAM"); + ram_block_discard_require(false); + return; + } + + virtio_mem_resize_usable_region(vmem, vmem->requested_size, true); + + vmem->bitmap_size = memory_region_size(&vmem->memdev->mr) / + vmem->block_size; + vmem->bitmap = bitmap_new(vmem->bitmap_size); + + virtio_init(vdev, TYPE_VIRTIO_MEM, VIRTIO_ID_MEM, + sizeof(struct virtio_mem_config)); + vmem->vq = virtio_add_queue(vdev, 128, virtio_mem_handle_request); + + host_memory_backend_set_mapped(vmem->memdev, true); + vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem)); + qemu_register_reset(virtio_mem_system_reset, vmem); + precopy_add_notifier(&vmem->precopy_notifier); +} + +static void virtio_mem_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOMEM *vmem = VIRTIO_MEM(dev); + + precopy_remove_notifier(&vmem->precopy_notifier); + qemu_unregister_reset(virtio_mem_system_reset, vmem); + vmstate_unregister_ram(&vmem->memdev->mr, DEVICE(vmem)); + host_memory_backend_set_mapped(vmem->memdev, false); + virtio_del_queue(vdev, 0); + virtio_cleanup(vdev); + g_free(vmem->bitmap); + ram_block_discard_require(false); +} + +static int virtio_mem_restore_unplugged(VirtIOMEM *vmem) +{ + RAMBlock *rb = vmem->memdev->mr.ram_block; + unsigned long first_zero_bit, last_zero_bit; + uint64_t offset, length; + int ret; + + /* Find consecutive unplugged blocks and discard the consecutive range. */ + first_zero_bit = find_first_zero_bit(vmem->bitmap, vmem->bitmap_size); + while (first_zero_bit < vmem->bitmap_size) { + offset = first_zero_bit * vmem->block_size; + last_zero_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, + first_zero_bit + 1) - 1; + length = (last_zero_bit - first_zero_bit + 1) * vmem->block_size; + + ret = ram_block_discard_range(rb, offset, length); + if (ret) { + error_report("Unexpected error discarding RAM: %s", + strerror(-ret)); + return -EINVAL; + } + first_zero_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, + last_zero_bit + 2); + } + return 0; +} + +static int virtio_mem_post_load(void *opaque, int version_id) +{ + if (migration_in_incoming_postcopy()) { + return 0; + } + + return virtio_mem_restore_unplugged(VIRTIO_MEM(opaque)); +} + +typedef struct VirtIOMEMMigSanityChecks { + VirtIOMEM *parent; + uint64_t addr; + uint64_t region_size; + uint64_t block_size; + uint32_t node; +} VirtIOMEMMigSanityChecks; + +static int virtio_mem_mig_sanity_checks_pre_save(void *opaque) +{ + VirtIOMEMMigSanityChecks *tmp = opaque; + VirtIOMEM *vmem = tmp->parent; + + tmp->addr = vmem->addr; + tmp->region_size = memory_region_size(&vmem->memdev->mr); + tmp->block_size = vmem->block_size; + tmp->node = vmem->node; + return 0; +} + +static int virtio_mem_mig_sanity_checks_post_load(void *opaque, int version_id) +{ + VirtIOMEMMigSanityChecks *tmp = opaque; + VirtIOMEM *vmem = tmp->parent; + const uint64_t new_region_size = memory_region_size(&vmem->memdev->mr); + + if (tmp->addr != vmem->addr) { + error_report("Property '%s' changed from 0x%" PRIx64 " to 0x%" PRIx64, + VIRTIO_MEM_ADDR_PROP, tmp->addr, vmem->addr); + return -EINVAL; + } + /* + * Note: Preparation for resizeable memory regions. The maximum size + * of the memory region must not change during migration. + */ + if (tmp->region_size != new_region_size) { + error_report("Property '%s' size changed from 0x%" PRIx64 " to 0x%" + PRIx64, VIRTIO_MEM_MEMDEV_PROP, tmp->region_size, + new_region_size); + return -EINVAL; + } + if (tmp->block_size != vmem->block_size) { + error_report("Property '%s' changed from 0x%" PRIx64 " to 0x%" PRIx64, + VIRTIO_MEM_BLOCK_SIZE_PROP, tmp->block_size, + vmem->block_size); + return -EINVAL; + } + if (tmp->node != vmem->node) { + error_report("Property '%s' changed from %" PRIu32 " to %" PRIu32, + VIRTIO_MEM_NODE_PROP, tmp->node, vmem->node); + return -EINVAL; + } + return 0; +} + +static const VMStateDescription vmstate_virtio_mem_sanity_checks = { + .name = "virtio-mem-device/sanity-checks", + .pre_save = virtio_mem_mig_sanity_checks_pre_save, + .post_load = virtio_mem_mig_sanity_checks_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT64(addr, VirtIOMEMMigSanityChecks), + VMSTATE_UINT64(region_size, VirtIOMEMMigSanityChecks), + VMSTATE_UINT64(block_size, VirtIOMEMMigSanityChecks), + VMSTATE_UINT32(node, VirtIOMEMMigSanityChecks), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_virtio_mem_device = { + .name = "virtio-mem-device", + .minimum_version_id = 1, + .version_id = 1, + .post_load = virtio_mem_post_load, + .fields = (VMStateField[]) { + VMSTATE_WITH_TMP(VirtIOMEM, VirtIOMEMMigSanityChecks, + vmstate_virtio_mem_sanity_checks), + VMSTATE_UINT64(usable_region_size, VirtIOMEM), + VMSTATE_UINT64(size, VirtIOMEM), + VMSTATE_UINT64(requested_size, VirtIOMEM), + VMSTATE_BITMAP(bitmap, VirtIOMEM, 0, bitmap_size), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_virtio_mem = { + .name = "virtio-mem", + .minimum_version_id = 1, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static void virtio_mem_fill_device_info(const VirtIOMEM *vmem, + VirtioMEMDeviceInfo *vi) +{ + vi->memaddr = vmem->addr; + vi->node = vmem->node; + vi->requested_size = vmem->requested_size; + vi->size = vmem->size; + vi->max_size = memory_region_size(&vmem->memdev->mr); + vi->block_size = vmem->block_size; + vi->memdev = object_get_canonical_path(OBJECT(vmem->memdev)); +} + +static MemoryRegion *virtio_mem_get_memory_region(VirtIOMEM *vmem, Error **errp) +{ + if (!vmem->memdev) { + error_setg(errp, "'%s' property must be set", VIRTIO_MEM_MEMDEV_PROP); + return NULL; + } + + return &vmem->memdev->mr; +} + +static void virtio_mem_add_size_change_notifier(VirtIOMEM *vmem, + Notifier *notifier) +{ + notifier_list_add(&vmem->size_change_notifiers, notifier); +} + +static void virtio_mem_remove_size_change_notifier(VirtIOMEM *vmem, + Notifier *notifier) +{ + notifier_remove(notifier); +} + +static void virtio_mem_get_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const VirtIOMEM *vmem = VIRTIO_MEM(obj); + uint64_t value = vmem->size; + + visit_type_size(v, name, &value, errp); +} + +static void virtio_mem_get_requested_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + const VirtIOMEM *vmem = VIRTIO_MEM(obj); + uint64_t value = vmem->requested_size; + + visit_type_size(v, name, &value, errp); +} + +static void virtio_mem_set_requested_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + VirtIOMEM *vmem = VIRTIO_MEM(obj); + Error *err = NULL; + uint64_t value; + + visit_type_size(v, name, &value, &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* + * The block size and memory backend are not fixed until the device was + * realized. realize() will verify these properties then. + */ + if (DEVICE(obj)->realized) { + if (!QEMU_IS_ALIGNED(value, vmem->block_size)) { + error_setg(errp, "'%s' has to be multiples of '%s' (0x%" PRIx64 + ")", name, VIRTIO_MEM_BLOCK_SIZE_PROP, + vmem->block_size); + return; + } else if (value > memory_region_size(&vmem->memdev->mr)) { + error_setg(errp, "'%s' cannot exceed the memory backend size" + "(0x%" PRIx64 ")", name, + memory_region_size(&vmem->memdev->mr)); + return; + } + + if (value != vmem->requested_size) { + virtio_mem_resize_usable_region(vmem, value, false); + vmem->requested_size = value; + } + /* + * Trigger a config update so the guest gets notified. We trigger + * even if the size didn't change (especially helpful for debugging). + */ + virtio_notify_config(VIRTIO_DEVICE(vmem)); + } else { + vmem->requested_size = value; + } +} + +static void virtio_mem_get_block_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const VirtIOMEM *vmem = VIRTIO_MEM(obj); + uint64_t value = vmem->block_size; + + visit_type_size(v, name, &value, errp); +} + +static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + VirtIOMEM *vmem = VIRTIO_MEM(obj); + Error *err = NULL; + uint64_t value; + + if (DEVICE(obj)->realized) { + error_setg(errp, "'%s' cannot be changed", name); + return; + } + + visit_type_size(v, name, &value, &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (value < VIRTIO_MEM_MIN_BLOCK_SIZE) { + error_setg(errp, "'%s' property has to be at least 0x%" PRIx32, name, + VIRTIO_MEM_MIN_BLOCK_SIZE); + return; + } else if (!is_power_of_2(value)) { + error_setg(errp, "'%s' property has to be a power of two", name); + return; + } + vmem->block_size = value; +} + +static void virtio_mem_precopy_exclude_unplugged(VirtIOMEM *vmem) +{ + void * const host = qemu_ram_get_host_addr(vmem->memdev->mr.ram_block); + unsigned long first_zero_bit, last_zero_bit; + uint64_t offset, length; + + /* + * Find consecutive unplugged blocks and exclude them from migration. + * + * Note: Blocks cannot get (un)plugged during precopy, no locking needed. + */ + first_zero_bit = find_first_zero_bit(vmem->bitmap, vmem->bitmap_size); + while (first_zero_bit < vmem->bitmap_size) { + offset = first_zero_bit * vmem->block_size; + last_zero_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, + first_zero_bit + 1) - 1; + length = (last_zero_bit - first_zero_bit + 1) * vmem->block_size; + + qemu_guest_free_page_hint(host + offset, length); + first_zero_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, + last_zero_bit + 2); + } +} + +static int virtio_mem_precopy_notify(NotifierWithReturn *n, void *data) +{ + VirtIOMEM *vmem = container_of(n, VirtIOMEM, precopy_notifier); + PrecopyNotifyData *pnd = data; + + switch (pnd->reason) { + case PRECOPY_NOTIFY_SETUP: + precopy_enable_free_page_optimization(); + break; + case PRECOPY_NOTIFY_AFTER_BITMAP_SYNC: + virtio_mem_precopy_exclude_unplugged(vmem); + break; + default: + break; + } + + return 0; +} + +static void virtio_mem_instance_init(Object *obj) +{ + VirtIOMEM *vmem = VIRTIO_MEM(obj); + + vmem->block_size = VIRTIO_MEM_MIN_BLOCK_SIZE; + notifier_list_init(&vmem->size_change_notifiers); + vmem->precopy_notifier.notify = virtio_mem_precopy_notify; + + object_property_add(obj, VIRTIO_MEM_SIZE_PROP, "size", virtio_mem_get_size, + NULL, NULL, NULL); + object_property_add(obj, VIRTIO_MEM_REQUESTED_SIZE_PROP, "size", + virtio_mem_get_requested_size, + virtio_mem_set_requested_size, NULL, NULL); + object_property_add(obj, VIRTIO_MEM_BLOCK_SIZE_PROP, "size", + virtio_mem_get_block_size, virtio_mem_set_block_size, + NULL, NULL); +} + +static Property virtio_mem_properties[] = { + DEFINE_PROP_UINT64(VIRTIO_MEM_ADDR_PROP, VirtIOMEM, addr, 0), + DEFINE_PROP_UINT32(VIRTIO_MEM_NODE_PROP, VirtIOMEM, node, 0), + DEFINE_PROP_LINK(VIRTIO_MEM_MEMDEV_PROP, VirtIOMEM, memdev, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_mem_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass); + + device_class_set_props(dc, virtio_mem_properties); + dc->vmsd = &vmstate_virtio_mem; + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + vdc->realize = virtio_mem_device_realize; + vdc->unrealize = virtio_mem_device_unrealize; + vdc->get_config = virtio_mem_get_config; + vdc->get_features = virtio_mem_get_features; + vdc->vmsd = &vmstate_virtio_mem_device; + + vmc->fill_device_info = virtio_mem_fill_device_info; + vmc->get_memory_region = virtio_mem_get_memory_region; + vmc->add_size_change_notifier = virtio_mem_add_size_change_notifier; + vmc->remove_size_change_notifier = virtio_mem_remove_size_change_notifier; +} + +static const TypeInfo virtio_mem_info = { + .name = TYPE_VIRTIO_MEM, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOMEM), + .instance_init = virtio_mem_instance_init, + .class_init = virtio_mem_class_init, + .class_size = sizeof(VirtIOMEMClass), +}; + +static void virtio_register_types(void) +{ + type_register_static(&virtio_mem_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 7bc8c1c056..8554cf2a03 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1107,6 +1107,18 @@ static AddressSpace *virtio_pci_get_dma_as(DeviceState *d) return pci_get_address_space(dev); } +static bool virtio_pci_queue_enabled(DeviceState *d, int n) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(d); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + + if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { + return proxy->vqs[vdev->queue_sel].enabled; + } + + return virtio_queue_enabled(vdev, n); +} + static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy, struct virtio_pci_cap *cap) { @@ -2064,6 +2076,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->ioeventfd_enabled = virtio_pci_ioeventfd_enabled; k->ioeventfd_assign = virtio_pci_ioeventfd_assign; k->get_dma_as = virtio_pci_get_dma_as; + k->queue_enabled = virtio_pci_queue_enabled; } static const TypeInfo virtio_pci_bus_info = { diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index cc9c9dc162..5bd2a2f621 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3286,6 +3286,12 @@ hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n) bool virtio_queue_enabled(VirtIODevice *vdev, int n) { + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + + if (k->queue_enabled) { + return k->queue_enabled(qbus->parent, n); + } return virtio_queue_get_desc_addr(vdev, n) != 0; } diff --git a/include/block/block.h b/include/block/block.h index e8fc814996..bca3bb831c 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -22,11 +22,6 @@ typedef struct BlockDriverInfo { int64_t vm_state_offset; bool is_dirty; /* - * True if unallocated blocks read back as zeroes. This is equivalent - * to the LBPRZ flag in the SCSI logical block provisioning page. - */ - bool unallocated_blocks_are_zero; - /* * True if this block driver only supports compressed writes */ bool needs_compressed_writes; @@ -450,6 +445,7 @@ typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, int64_t total_work_size, void *opaque); int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + bool force, Error **errp); /* check if a named node can be replaced when doing drive-mirror */ @@ -488,7 +484,6 @@ int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); int bdrv_has_zero_init_1(BlockDriverState *bs); int bdrv_has_zero_init(BlockDriverState *bs); -bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, diff --git a/include/block/block_int.h b/include/block/block_int.h index 791de6a59c..3d6cf88592 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -123,7 +123,17 @@ struct BlockDriver { */ bool bdrv_needs_filename; - /* Set if a driver can support backing files */ + /* + * Set if a driver can support backing files. This also implies the + * following semantics: + * + * - Return status 0 of .bdrv_co_block_status means that corresponding + * blocks are not allocated in this layer of backing-chain + * - For such (unallocated) blocks, read will: + * - fill buffer with zeros if there is no backing file + * - read from the backing file otherwise, where the block layer + * takes care of reading zeros beyond EOF if backing file is short + */ bool supports_backing; /* For handling image reopen for split or non-split files */ @@ -141,12 +151,27 @@ struct BlockDriver { int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, Error **errp); void (*bdrv_close)(BlockDriverState *bs); + + int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts, Error **errp); int coroutine_fn (*bdrv_co_create_opts)(BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp); + + int coroutine_fn (*bdrv_co_amend)(BlockDriverState *bs, + BlockdevAmendOptions *opts, + bool force, + Error **errp); + + int (*bdrv_amend_options)(BlockDriverState *bs, + QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque, + bool force, + Error **errp); + int (*bdrv_make_empty)(BlockDriverState *bs); /* @@ -420,6 +445,10 @@ struct BlockDriver { /* List of options for creating images, terminated by name == NULL */ QemuOptsList *create_opts; + + /* List of options for image amend */ + QemuOptsList *amend_opts; + /* * If this driver supports reopening images this contains a * NULL-terminated list of the runtime options that can be @@ -437,11 +466,6 @@ struct BlockDriver { BdrvCheckResult *result, BdrvCheckMode fix); - int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque, - Error **errp); - void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ diff --git a/include/crypto/block.h b/include/crypto/block.h index c77ccaf9c0..d274819791 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -144,6 +144,28 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, void *opaque, Error **errp); +/** + * qcrypto_block_amend_options: + * @block: the block encryption object + * + * @readfunc: callback for reading data from the volume header + * @writefunc: callback for writing data to the volume header + * @opaque: data to pass to @readfunc and @writefunc + * @options: the new/amended encryption options + * @force: hint for the driver to allow unsafe operation + * @errp: error pointer + * + * Changes the crypto options of the encryption format + * + */ +int qcrypto_block_amend_options(QCryptoBlock *block, + QCryptoBlockReadFunc readfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + QCryptoBlockAmendOptions *options, + bool force, + Error **errp); + /** * qcrypto_block_calculate_payload_offset: diff --git a/include/exec/memory.h b/include/exec/memory.h index 7207025bd4..307e527835 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -51,6 +51,12 @@ extern bool global_dirty_log; typedef struct MemoryRegionOps MemoryRegionOps; +struct ReservedRegion { + hwaddr low; + hwaddr high; + unsigned type; +}; + typedef struct IOMMUTLBEntry IOMMUTLBEntry; /* See address_space_translate: bit 0 is read, bit 1 is write. */ @@ -2472,6 +2478,47 @@ static inline MemOp devend_memop(enum device_endian end) } #endif +/* + * Inhibit technologies that require discarding of pages in RAM blocks, e.g., + * to manage the actual amount of memory consumed by the VM (then, the memory + * provided by RAM blocks might be bigger than the desired memory consumption). + * This *must* be set if: + * - Discarding parts of a RAM blocks does not result in the change being + * reflected in the VM and the pages getting freed. + * - All memory in RAM blocks is pinned or duplicated, invaldiating any previous + * discards blindly. + * - Discarding parts of a RAM blocks will result in integrity issues (e.g., + * encrypted VMs). + * Technologies that only temporarily pin the current working set of a + * driver are fine, because we don't expect such pages to be discarded + * (esp. based on guest action like balloon inflation). + * + * This is *not* to be used to protect from concurrent discards (esp., + * postcopy). + * + * Returns 0 if successful. Returns -EBUSY if a technology that relies on + * discards to work reliably is active. + */ +int ram_block_discard_disable(bool state); + +/* + * Inhibit technologies that disable discarding of pages in RAM blocks. + * + * Returns 0 if successful. Returns -EBUSY if discards are already set to + * broken. + */ +int ram_block_discard_require(bool state); + +/* + * Test if discarding of memory in ram blocks is disabled. + */ +bool ram_block_discard_is_disabled(void); + +/* + * Test if discarding of memory in ram blocks is required to work reliably. + */ +bool ram_block_discard_is_required(void); + #endif #endif diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 7b5c24e928..3ef729a23c 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -442,8 +442,7 @@ static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, static inline uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, ram_addr_t start, - ram_addr_t length, - uint64_t *real_dirty_pages) + ram_addr_t length) { ram_addr_t addr; unsigned long word = BIT_WORD((start + rb->offset) >> TARGET_PAGE_BITS); @@ -469,7 +468,6 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, if (src[idx][offset]) { unsigned long bits = atomic_xchg(&src[idx][offset], 0); unsigned long new_dirty; - *real_dirty_pages += ctpopl(bits); new_dirty = ~dest[k]; dest[k] |= bits; new_dirty &= bits; @@ -502,7 +500,6 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, start + addr + offset, TARGET_PAGE_SIZE, DIRTY_MEMORY_MIGRATION)) { - *real_dirty_pages += 1; long k = (start + addr) >> TARGET_PAGE_BITS; if (!test_and_set_bit(k, dest)) { num_dirty++; diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index eae2c27f99..7992475c9f 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -73,6 +73,7 @@ void thunk_register_struct_direct(int id, const char *name, const StructEntry *se1); const argtype *thunk_convert(void *dst, const void *src, const argtype *type_ptr, int to_host); +const argtype *thunk_print(void *arg, const argtype *type_ptr); extern StructEntry *struct_entries; diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index ff4e2605b1..f1a19df066 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -794,7 +794,31 @@ static inline bool floatx80_unordered_quiet(floatx80 a, floatx80 b, *----------------------------------------------------------------------------*/ static inline bool floatx80_invalid_encoding(floatx80 a) { +#if defined(TARGET_M68K) + /*------------------------------------------------------------------------- + | With m68k, the explicit integer bit can be zero in the case of: + | - zeros (exp == 0, mantissa == 0) + | - denormalized numbers (exp == 0, mantissa != 0) + | - unnormalized numbers (exp != 0, exp < 0x7FFF) + | - infinities (exp == 0x7FFF, mantissa == 0) + | - not-a-numbers (exp == 0x7FFF, mantissa != 0) + | + | For infinities and NaNs, the explicit integer bit can be either one or + | zero. + | + | The IEEE 754 standard does not define a zero integer bit. Such a number + | is an unnormalized number. Hardware does not directly support + | denormalized and unnormalized numbers, but implicitly supports them by + | trapping them as unimplemented data types, allowing efficient conversion + | in software. + | + | See "M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL", + | "1.6 FLOATING-POINT DATA TYPES" + *------------------------------------------------------------------------*/ + return false; +#else return (a.low & (1ULL << 63)) == 0 && (a.high & 0x7FFF) != 0; +#endif } #define floatx80_zero make_floatx80(0x0000, 0x0000000000000000LL) diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index 37c89cc5f9..fcbaf3dc86 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -87,6 +87,8 @@ typedef struct FslIMX6ULState { MemoryRegion caam; MemoryRegion ocram; MemoryRegion ocram_alias; + + uint32_t phy_num[FSL_IMX6UL_NUM_ETHS]; } FslIMX6ULState; enum FslIMX6ULMemoryMap { diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h index f6dfb5c0cf..8843e5f910 100644 --- a/include/hw/arm/pxa.h +++ b/include/hw/arm/pxa.h @@ -184,7 +184,6 @@ struct PXA2xxI2SState { }; # define PA_FMT "0x%08lx" -# define REG_FMT "0x" TARGET_FMT_plx PXA2xxState *pxa270_init(MemoryRegion *address_space, unsigned int sdram_size, const char *revision); diff --git a/include/hw/arm/sharpsl.h b/include/hw/arm/sharpsl.h index 89e168fbff..e986b28c52 100644 --- a/include/hw/arm/sharpsl.h +++ b/include/hw/arm/sharpsl.h @@ -9,9 +9,6 @@ #include "exec/hwaddr.h" -#define zaurus_printf(format, ...) \ - fprintf(stderr, "%s: " format, __func__, ##__VA_ARGS__) - /* zaurus.c */ #define SL_PXA_PARAM_BASE 0xa0000a00 diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 31878ddc72..54bcf17afd 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -96,6 +96,12 @@ typedef enum VirtIOMMUType { VIRT_IOMMU_VIRTIO, } VirtIOMMUType; +typedef enum VirtMSIControllerType { + VIRT_MSI_CTRL_NONE, + VIRT_MSI_CTRL_GICV2M, + VIRT_MSI_CTRL_ITS, +} VirtMSIControllerType; + typedef enum VirtGICType { VIRT_GIC_VERSION_MAX, VIRT_GIC_VERSION_HOST, @@ -119,6 +125,7 @@ typedef struct { bool no_highmem_ecam; bool no_ged; /* Machines < 4.2 has no support for ACPI GED device */ bool kvm_no_adjvtime; + bool acpi_expose_flash; } VirtMachineClass; typedef struct { @@ -136,6 +143,7 @@ typedef struct { OnOffAuto acpi; VirtGICType gic_version; VirtIOMMUType iommu; + VirtMSIControllerType msi_controller; uint16_t virtio_iommu_bdf; struct arm_boot_info bootinfo; MemMapEntry *memmap; diff --git a/include/hw/audio/pcspk.h b/include/hw/audio/pcspk.h index 7e7f5f49dc..06cba00b83 100644 --- a/include/hw/audio/pcspk.h +++ b/include/hw/audio/pcspk.h @@ -31,18 +31,10 @@ #define TYPE_PC_SPEAKER "isa-pcspk" -static inline ISADevice *pcspk_init(ISABus *bus, ISADevice *pit) +static inline void pcspk_init(ISADevice *isadev, ISABus *bus, ISADevice *pit) { - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_new(TYPE_PC_SPEAKER); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "iobase", 0x61); - object_property_set_link(OBJECT(dev), OBJECT(pit), "pit", NULL); + object_property_set_link(OBJECT(isadev), OBJECT(pit), "pit", NULL); isa_realize_and_unref(isadev, bus, &error_fatal); - - return isadev; } #endif /* HW_PCSPK_H */ diff --git a/include/hw/audio/soundhw.h b/include/hw/audio/soundhw.h index c8eef82418..f09a297854 100644 --- a/include/hw/audio/soundhw.h +++ b/include/hw/audio/soundhw.h @@ -6,6 +6,8 @@ void isa_register_soundhw(const char *name, const char *descr, void pci_register_soundhw(const char *name, const char *descr, int (*init_pci)(PCIBus *bus)); +void deprecated_register_soundhw(const char *name, const char *descr, + int isa, const char *typename); void soundhw_init(void); void select_soundhw(const char *optarg); diff --git a/include/hw/boards.h b/include/hw/boards.h index 18815d9be2..426ce5f625 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -207,6 +207,7 @@ struct MachineClass { const char **valid_cpu_types; strList *allowed_dynamic_sysbus_devices; bool auto_enable_numa_with_memhp; + bool auto_enable_numa_with_memdev; void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes, int nb_nodes, ram_addr_t size); bool ignore_boot_device_suffixes; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index dce1273c7d..a802e69974 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -33,6 +33,7 @@ struct PCMachineState { PCIBus *bus; I2CBus *smbus; PFlashCFI01 *flash[2]; + ISADevice *pcspk; /* Configuration options: */ uint64_t max_ram_below_4g; @@ -160,11 +161,10 @@ void pc_memory_init(PCMachineState *pcms, MemoryRegion **ram_memory); uint64_t pc_pci_hole64_start(void); DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus); -void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, +void pc_basic_device_init(struct PCMachineState *pcms, + ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, bool create_fdctrl, - bool no_vmport, - bool has_pit, uint32_t hpet_irqs); void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd); void pc_cmos_init(PCMachineState *pcms, diff --git a/include/hw/misc/max111x.h b/include/hw/misc/max111x.h new file mode 100644 index 0000000000..af7f1017ef --- /dev/null +++ b/include/hw/misc/max111x.h @@ -0,0 +1,56 @@ +/* + * Maxim MAX1110/1111 ADC chip emulation. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski <balrog@zabor.org> + * + * This code is licensed under the GNU GPLv2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef HW_MISC_MAX111X_H +#define HW_MISC_MAX111X_H + +#include "hw/ssi/ssi.h" + +/* + * This is a model of the Maxim MAX1110/1111 ADC chip, which for QEMU + * is an SSI slave device. It has either 4 (max1110) or 8 (max1111) + * 8-bit ADC channels. + * + * QEMU interface: + * + GPIO inputs 0..3 (for max1110) or 0..7 (for max1111): set the value + * of each ADC input, as an unsigned 8-bit value + * + GPIO output 0: interrupt line + * + Properties "input0" to "input3" (max1110) or "input0" to "input7" + * (max1111): initial reset values for ADC inputs. + * + * Known bugs: + * + the interrupt line is not correctly implemented, and will never + * be lowered once it has been asserted. + */ +typedef struct { + SSISlave parent_obj; + + qemu_irq interrupt; + /* Values of inputs at system reset (settable by QOM property) */ + uint8_t reset_input[8]; + + uint8_t tb1, rb2, rb3; + int cycle; + + uint8_t input[8]; + int inputs, com; +} MAX111xState; + +#define TYPE_MAX_111X "max111x" + +#define MAX_111X(obj) \ + OBJECT_CHECK(MAX111xState, (obj), TYPE_MAX_111X) + +#define TYPE_MAX_1110 "max1110" +#define TYPE_MAX_1111 "max1111" + +#endif diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h index 7b3faa4019..9f03034b89 100644 --- a/include/hw/net/imx_fec.h +++ b/include/hw/net/imx_fec.h @@ -268,6 +268,7 @@ typedef struct IMXFECState { uint32_t phy_advertise; uint32_t phy_int; uint32_t phy_int_mask; + uint32_t phy_num; bool is_fec; diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index a4e9c33416..c1bf7d5356 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -87,6 +87,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012 #define PCI_DEVICE_ID_VIRTIO_PMEM 0x1013 #define PCI_DEVICE_ID_VIRTIO_IOMMU 0x1014 +#define PCI_DEVICE_ID_VIRTIO_MEM 0x1015 #define PCI_VENDOR_ID_REDHAT 0x1b36 #define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 49c6cd2460..944e3f2e0c 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -19,6 +19,7 @@ extern const PropertyInfo qdev_prop_string; extern const PropertyInfo qdev_prop_chr; extern const PropertyInfo qdev_prop_tpm; extern const PropertyInfo qdev_prop_macaddr; +extern const PropertyInfo qdev_prop_reserved_region; extern const PropertyInfo qdev_prop_on_off_auto; extern const PropertyInfo qdev_prop_multifd_compression; extern const PropertyInfo qdev_prop_losttickpolicy; @@ -184,6 +185,8 @@ extern const PropertyInfo qdev_prop_pcie_link_width; DEFINE_PROP(_n, _s, _f, qdev_prop_drive_iothread, BlockBackend *) #define DEFINE_PROP_MACADDR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr) +#define DEFINE_PROP_RESERVED_REGION(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_reserved_region, ReservedRegion) #define DEFINE_PROP_ON_OFF_AUTO(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_on_off_auto, OnOffAuto) #define DEFINE_PROP_MULTIFD_COMPRESSION(_n, _s, _f, _d) \ diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h index 93f2b8b0be..eac168aa1d 100644 --- a/include/hw/ssi/ssi.h +++ b/include/hw/ssi/ssi.h @@ -66,8 +66,6 @@ struct SSISlave { bool cs; }; -#define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev) - extern const VMStateDescription vmstate_ssi_slave; #define VMSTATE_SSI_SLAVE(_field, _state) { \ @@ -79,13 +77,36 @@ extern const VMStateDescription vmstate_ssi_slave; } DeviceState *ssi_create_slave(SSIBus *bus, const char *name); +/** + * ssi_realize_and_unref: realize and unref an SSI slave device + * @dev: SSI slave device to realize + * @bus: SSI bus to put it on + * @errp: error pointer + * + * Call 'realize' on @dev, put it on the specified @bus, and drop the + * reference to it. Errors are reported via @errp and by returning + * false. + * + * This function is useful if you have created @dev via qdev_new() + * (which takes a reference to the device it returns to you), so that + * you can set properties on it before realizing it. If you don't need + * to set properties then ssi_create_slave() is probably better (as it + * does the create, init and realize in one step). + * + * If you are embedding the SSI slave into another QOM device and + * initialized it via some variant on object_initialize_child() then + * do not use this function, because that family of functions arrange + * for the only reference to the child device to be held by the parent + * via the child<> property, and so the reference-count-drop done here + * would be incorrect. (Instead you would want ssi_realize(), which + * doesn't currently exist but would be trivial to create if we had + * any code that wanted it.) + */ +bool ssi_realize_and_unref(DeviceState *dev, SSIBus *bus, Error **errp); /* Master interface. */ SSIBus *ssi_create_bus(DeviceState *parent, const char *name); uint32_t ssi_transfer(SSIBus *bus, uint32_t val); -/* max111x.c */ -void max111x_set_input(DeviceState *dev, int line, uint8_t value); - #endif diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index fd564209ac..c78f3ff559 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -108,7 +108,7 @@ typedef struct VFIODevice { bool reset_works; bool needs_reset; bool no_mmap; - bool balloon_allowed; + bool ram_block_discard_allowed; VFIODeviceOps *ops; unsigned int num_irqs; unsigned int num_regions; @@ -128,7 +128,7 @@ typedef struct VFIOGroup { QLIST_HEAD(, VFIODevice) device_list; QLIST_ENTRY(VFIOGroup) next; QLIST_ENTRY(VFIOGroup) container_next; - bool balloon_allowed; + bool ram_block_discard_allowed; } VFIOGroup; typedef struct VFIODMABuf { diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 6f6670783f..8825bd278f 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -17,7 +17,8 @@ typedef enum VhostBackendType { VHOST_BACKEND_TYPE_NONE = 0, VHOST_BACKEND_TYPE_KERNEL = 1, VHOST_BACKEND_TYPE_USER = 2, - VHOST_BACKEND_TYPE_MAX = 3, + VHOST_BACKEND_TYPE_VDPA = 3, + VHOST_BACKEND_TYPE_MAX = 4, } VhostBackendType; typedef enum VhostSetConfigType { @@ -34,6 +35,7 @@ struct vhost_vring_state; struct vhost_vring_addr; struct vhost_scsi_target; struct vhost_iotlb_msg; +struct vhost_virtqueue; typedef int (*vhost_backend_init)(struct vhost_dev *dev, void *opaque); typedef int (*vhost_backend_cleanup)(struct vhost_dev *dev); @@ -112,6 +114,16 @@ typedef int (*vhost_get_inflight_fd_op)(struct vhost_dev *dev, typedef int (*vhost_set_inflight_fd_op)(struct vhost_dev *dev, struct vhost_inflight *inflight); +typedef int (*vhost_dev_start_op)(struct vhost_dev *dev, bool started); + +typedef int (*vhost_vq_get_addr_op)(struct vhost_dev *dev, + struct vhost_vring_addr *addr, + struct vhost_virtqueue *vq); + +typedef int (*vhost_get_device_id_op)(struct vhost_dev *dev, uint32_t *dev_id); + +typedef bool (*vhost_force_iommu_op)(struct vhost_dev *dev); + typedef struct VhostOps { VhostBackendType backend_type; vhost_backend_init vhost_backend_init; @@ -152,9 +164,14 @@ typedef struct VhostOps { vhost_backend_mem_section_filter_op vhost_backend_mem_section_filter; vhost_get_inflight_fd_op vhost_get_inflight_fd; vhost_set_inflight_fd_op vhost_set_inflight_fd; + vhost_dev_start_op vhost_dev_start; + vhost_vq_get_addr_op vhost_vq_get_addr; + vhost_get_device_id_op vhost_get_device_id; + vhost_force_iommu_op vhost_force_iommu; } VhostOps; extern const VhostOps user_ops; +extern const VhostOps vdpa_ops; int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type); diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h new file mode 100644 index 0000000000..6455663388 --- /dev/null +++ b/include/hw/virtio/vhost-vdpa.h @@ -0,0 +1,26 @@ +/* + * vhost-vdpa.h + * + * Copyright(c) 2017-2018 Intel Corporation. + * Copyright(c) 2020 Red Hat, 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. + * + */ + +#ifndef HW_VIRTIO_VHOST_VDPA_H +#define HW_VIRTIO_VHOST_VDPA_H + +#include "hw/virtio/virtio.h" + +typedef struct vhost_vdpa { + int device_fd; + uint32_t msg_type; + MemoryListener listener; +} VhostVDPA; + +extern AddressSpace address_space_memory; +extern int vhost_vdpa_get_device_id(struct vhost_dev *dev, + uint32_t *device_id); +#endif diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 085450c6f8..767a95ec0b 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -92,6 +92,13 @@ struct vhost_dev { const VhostDevConfigOps *config_ops; }; +struct vhost_net { + struct vhost_dev dev; + struct vhost_virtqueue vqs[2]; + int backend; + NetClientState *nc; +}; + int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, uint32_t busyloop_timeout); diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 38c9399cd4..0f6f215925 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -84,6 +84,10 @@ typedef struct VirtioBusClass { int (*ioeventfd_assign)(DeviceState *d, EventNotifier *notifier, int n, bool assign); /* + * Whether queue number n is enabled. + */ + bool (*queue_enabled)(DeviceState *d, int n); + /* * Does the transport have variable vring alignment? * (ie can it ever call virtio_queue_set_align()?) * Note that changing this will break migration for this transport. diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index e653004d7c..49eb105cd8 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -53,6 +53,8 @@ typedef struct VirtIOIOMMU { GHashTable *as_by_busptr; IOMMUPciBus *iommu_pcibus_by_bus_num[PCI_BUS_MAX]; PCIBus *primary_bus; + ReservedRegion *reserved_regions; + uint32_t nb_reserved_regions; GTree *domains; QemuMutex mutex; GTree *endpoints; diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h new file mode 100644 index 0000000000..0778224964 --- /dev/null +++ b/include/hw/virtio/virtio-mem.h @@ -0,0 +1,86 @@ +/* + * Virtio MEM device + * + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * David Hildenbrand <david@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_MEM_H +#define HW_VIRTIO_MEM_H + +#include "standard-headers/linux/virtio_mem.h" +#include "hw/virtio/virtio.h" +#include "qapi/qapi-types-misc.h" +#include "sysemu/hostmem.h" + +#define TYPE_VIRTIO_MEM "virtio-mem" + +#define VIRTIO_MEM(obj) \ + OBJECT_CHECK(VirtIOMEM, (obj), TYPE_VIRTIO_MEM) +#define VIRTIO_MEM_CLASS(oc) \ + OBJECT_CLASS_CHECK(VirtIOMEMClass, (oc), TYPE_VIRTIO_MEM) +#define VIRTIO_MEM_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtIOMEMClass, (obj), TYPE_VIRTIO_MEM) + +#define VIRTIO_MEM_MEMDEV_PROP "memdev" +#define VIRTIO_MEM_NODE_PROP "node" +#define VIRTIO_MEM_SIZE_PROP "size" +#define VIRTIO_MEM_REQUESTED_SIZE_PROP "requested-size" +#define VIRTIO_MEM_BLOCK_SIZE_PROP "block-size" +#define VIRTIO_MEM_ADDR_PROP "memaddr" + +typedef struct VirtIOMEM { + VirtIODevice parent_obj; + + /* guest -> host request queue */ + VirtQueue *vq; + + /* bitmap used to track unplugged memory */ + int32_t bitmap_size; + unsigned long *bitmap; + + /* assigned memory backend and memory region */ + HostMemoryBackend *memdev; + + /* NUMA node */ + uint32_t node; + + /* assigned address of the region in guest physical memory */ + uint64_t addr; + + /* usable region size (<= region_size) */ + uint64_t usable_region_size; + + /* actual size (how much the guest plugged) */ + uint64_t size; + + /* requested size */ + uint64_t requested_size; + + /* block size and alignment */ + uint64_t block_size; + + /* notifiers to notify when "size" changes */ + NotifierList size_change_notifiers; + + /* don't migrate unplugged memory */ + NotifierWithReturn precopy_notifier; +} VirtIOMEM; + +typedef struct VirtIOMEMClass { + /* private */ + VirtIODevice parent; + + /* public */ + void (*fill_device_info)(const VirtIOMEM *vmen, VirtioMEMDeviceInfo *vi); + MemoryRegion *(*get_memory_region)(VirtIOMEM *vmem, Error **errp); + void (*add_size_change_notifier)(VirtIOMEM *vmem, Notifier *notifier); + void (*remove_size_change_notifier)(VirtIOMEM *vmem, Notifier *notifier); +} VirtIOMEMClass; + +#endif diff --git a/include/migration/colo.h b/include/migration/colo.h index 1636e6f907..768e1f04c3 100644 --- a/include/migration/colo.h +++ b/include/migration/colo.h @@ -25,7 +25,7 @@ void migrate_start_colo_process(MigrationState *s); bool migration_in_colo_state(void); /* loadvm */ -void migration_incoming_enable_colo(void); +int migration_incoming_enable_colo(void); void migration_incoming_disable_colo(void); bool migration_incoming_colo_enabled(void); void *colo_process_incoming_thread(void *opaque); diff --git a/include/migration/misc.h b/include/migration/misc.h index d2762257aa..34e7d75713 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -69,6 +69,8 @@ bool migration_has_failed(MigrationState *); /* ...and after the device transmission */ bool migration_in_postcopy_after_devices(MigrationState *); void migration_global_dump(Monitor *mon); +/* True if incomming migration entered POSTCOPY_INCOMING_DISCARD */ +bool migration_in_incoming_postcopy(void); /* migration/block-dirty-bitmap.c */ void dirty_bitmap_mig_init(void); diff --git a/include/net/net.h b/include/net/net.h index 39085d9444..e7ef42d62b 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -176,6 +176,7 @@ void hmp_info_network(Monitor *mon, const QDict *qdict); void net_socket_rs_init(SocketReadState *rs, SocketReadStateFinalize *finalize, bool vnet_hdr); +NetClientState *qemu_get_peer(NetClientState *nc, int queue_index); /* NIC info */ diff --git a/include/net/vhost-vdpa.h b/include/net/vhost-vdpa.h new file mode 100644 index 0000000000..45e34b7cfc --- /dev/null +++ b/include/net/vhost-vdpa.h @@ -0,0 +1,22 @@ +/* + * vhost-vdpa.h + * + * Copyright(c) 2017-2018 Intel Corporation. + * Copyright(c) 2020 Red Hat, 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. + * + */ + +#ifndef VHOST_VDPA_H +#define VHOST_VDPA_H + +#define TYPE_VHOST_VDPA "vhost-vdpa" + +struct vhost_net *vhost_vdpa_get_vhost_net(NetClientState *nc); +uint64_t vhost_vdpa_get_acked_features(NetClientState *nc); + +extern const int vdpa_feature_bits[]; + +#endif /* VHOST_VDPA_H */ diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 77e47398c4..172b0051d8 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -28,6 +28,11 @@ void vhost_net_cleanup(VHostNetState *net); uint64_t vhost_net_get_features(VHostNetState *net, uint64_t features); void vhost_net_ack_features(VHostNetState *net, uint64_t features); +int vhost_net_get_config(struct vhost_net *net, uint8_t *config, + uint32_t config_len); + +int vhost_net_set_config(struct vhost_net *net, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags); bool vhost_net_virtqueue_pending(VHostNetState *net, int n); void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, int idx, bool mask); diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 4cd170e6cd..cdca2991d8 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -77,8 +77,8 @@ static inline int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) } } #else -void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b); -void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b); +void muls64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b); +void mulu64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b); int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); int divs128(int64_t *plow, int64_t *phigh, int64_t divisor); diff --git a/include/qemu/module.h b/include/qemu/module.h index 011ae1ae76..9121a475c1 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -70,5 +70,7 @@ void register_dso_module_init(void (*fn)(void), module_init_type type); void module_call_init(module_init_type type); bool module_load_one(const char *prefix, const char *lib_name); +void module_load_qom_one(const char *type); +void module_load_qom_all(void); #endif diff --git a/include/qemu/option.h b/include/qemu/option.h index eb4097889d..ac50d25774 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -28,6 +28,19 @@ #include "qemu/queue.h" +/** + * get_opt_value + * @p: a pointer to the option name, delimited by commas + * @value: a non-NULL pointer that will received the delimited options + * + * The @value char pointer will be allocated and filled with + * the delimited options. + * + * Returns the position of the comma delimiter/zero byte after the + * option name in @p. + * The memory pointer in @value must be released with a call to g_free() + * when no longer required. + */ const char *get_opt_value(const char *p, char **value); void parse_option_size(const char *name, const char *value, diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index ce4a78b687..15f5047bf1 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -58,6 +58,7 @@ typedef struct ISABus ISABus; typedef struct ISADevice ISADevice; typedef struct IsaDma IsaDma; typedef struct MACAddr MACAddr; +typedef struct ReservedRegion ReservedRegion; typedef struct MachineClass MachineClass; typedef struct MachineState MachineState; typedef struct MemoryListener MemoryListener; diff --git a/include/qom/object.h b/include/qom/object.h index 94a61ccc3f..51f188137f 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -994,6 +994,18 @@ bool object_class_is_abstract(ObjectClass *klass); */ ObjectClass *object_class_by_name(const char *typename); +/** + * module_object_class_by_name: + * @typename: The QOM typename to obtain the class for. + * + * For objects which might be provided by a module. Behaves like + * object_class_by_name, but additionally tries to load the module + * needed in case the class is not available. + * + * Returns: The class for @typename or %NULL if not found. + */ +ObjectClass *module_object_class_by_name(const char *typename); + void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque), const char *implements_type, bool include_abstract, void *opaque); diff --git a/include/sysemu/balloon.h b/include/sysemu/balloon.h index aea0c44985..20a2defe3a 100644 --- a/include/sysemu/balloon.h +++ b/include/sysemu/balloon.h @@ -23,7 +23,5 @@ typedef void (QEMUBalloonStatus)(void *opaque, BalloonInfo *info); int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, QEMUBalloonStatus *stat_func, void *opaque); void qemu_remove_balloon_handler(void *opaque); -bool qemu_balloon_is_inhibited(void); -void qemu_balloon_inhibit(bool state); #endif diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 0defa1d8c1..0713ae1311 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -279,13 +279,17 @@ * FIXME: create a macro to define this kind of entry */ { TARGET_SIOCGSTAMP_OLD, TARGET_SIOCGSTAMP_OLD, - "SIOCGSTAMP_OLD", IOC_R, do_ioctl_SIOCGSTAMP }, + "SIOCGSTAMP_OLD", IOC_R, do_ioctl_SIOCGSTAMP, + { MK_PTR(MK_STRUCT(STRUCT_timeval)) } }, { TARGET_SIOCGSTAMPNS_OLD, TARGET_SIOCGSTAMPNS_OLD, - "SIOCGSTAMPNS_OLD", IOC_R, do_ioctl_SIOCGSTAMPNS }, + "SIOCGSTAMPNS_OLD", IOC_R, do_ioctl_SIOCGSTAMPNS, + { MK_PTR(MK_STRUCT(STRUCT_timespec)) } }, { TARGET_SIOCGSTAMP_NEW, TARGET_SIOCGSTAMP_NEW, - "SIOCGSTAMP_NEW", IOC_R, do_ioctl_SIOCGSTAMP }, + "SIOCGSTAMP_NEW", IOC_R, do_ioctl_SIOCGSTAMP, + { MK_PTR(MK_STRUCT(STRUCT__kernel_sock_timeval)) } }, { TARGET_SIOCGSTAMPNS_NEW, TARGET_SIOCGSTAMPNS_NEW, - "SIOCGSTAMPNS_NEW", IOC_R, do_ioctl_SIOCGSTAMPNS }, + "SIOCGSTAMPNS_NEW", IOC_R, do_ioctl_SIOCGSTAMPNS, + { MK_PTR(MK_STRUCT(STRUCT__kernel_timespec)) } }, IOCTL(RNDGETENTCNT, IOC_R, MK_PTR(TYPE_INT)) IOCTL(RNDADDTOENTCNT, IOC_W, MK_PTR(TYPE_INT)) @@ -574,6 +578,11 @@ IOCTL_SPECIAL(SIOCDELRT, IOC_W, do_ioctl_rt, MK_PTR(MK_STRUCT(STRUCT_rtentry))) +#ifdef HAVE_DRM_H + IOCTL_SPECIAL(DRM_IOCTL_VERSION, IOC_RW, do_ioctl_drm, + MK_PTR(MK_STRUCT(STRUCT_drm_version))) +#endif + #ifdef TARGET_TIOCSTART IOCTL_IGNORE(TIOCSTART) IOCTL_IGNORE(TIOCSTOP) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index ce902f5132..5c964389c1 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -184,6 +184,26 @@ struct linux_binprm { int (*core_dump)(int, const CPUArchState *); /* coredump routine */ }; +typedef struct IOCTLEntry IOCTLEntry; + +typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, int cmd, abi_long arg); + +struct IOCTLEntry { + int target_cmd; + unsigned int host_cmd; + const char *name; + int access; + do_ioctl_fn *do_ioctl; + const argtype arg_type[5]; +}; + +extern IOCTLEntry ioctl_entries[]; + +#define IOC_R 0x0001 +#define IOC_W 0x0002 +#define IOC_RW (IOC_R | IOC_W) + void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); @@ -383,7 +403,9 @@ int host_to_target_waitstatus(int status); void print_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6); -void print_syscall_ret(int num, abi_long arg1); +void print_syscall_ret(int num, abi_long ret, + abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6); /** * print_taken_signal: * @target_signum: target signal being taken @@ -668,6 +690,22 @@ static inline int is_error(abi_long ret) return (abi_ulong)ret >= (abi_ulong)(-4096); } +#if TARGET_ABI_BITS == 32 +static inline uint64_t target_offset64(uint32_t word0, uint32_t word1) +{ +#ifdef TARGET_WORDS_BIGENDIAN + return ((uint64_t)word0 << 32) | word1; +#else + return ((uint64_t)word1 << 32) | word0; +#endif +} +#else /* TARGET_ABI_BITS == 32 */ +static inline uint64_t target_offset64(uint64_t word0, uint64_t word1) +{ + return word0; +} +#endif /* TARGET_ABI_BITS != 32 */ + /** * preexit_cleanup: housekeeping before the guest exits * diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 7645cc04ca..02532f198d 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -69,7 +69,11 @@ static void save_window(CPUSPARCState *env) save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); env->wim = new_wim; #else - save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); + /* + * cansave is zero if the spill trap handler is triggered by `save` and + * nonzero if triggered by a `flushw` + */ + save_window_offset(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2)); env->cansave++; env->canrestore--; #endif diff --git a/linux-user/strace.c b/linux-user/strace.c index 0d9095c674..5235b2260c 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -19,7 +19,9 @@ struct syscallname { void (*call)(const struct syscallname *, abi_long, abi_long, abi_long, abi_long, abi_long, abi_long); - void (*result)(const struct syscallname *, abi_long); + void (*result)(const struct syscallname *, abi_long, + abi_long, abi_long, abi_long, + abi_long, abi_long, abi_long); }; #ifdef __GNUC__ @@ -631,18 +633,12 @@ print_clockid(int clockid, int last) /* select */ #ifdef TARGET_NR__newselect -static long newselect_arg1 = 0; -static long newselect_arg2 = 0; -static long newselect_arg3 = 0; -static long newselect_arg4 = 0; -static long newselect_arg5 = 0; - static void print_newselect(const struct syscallname *name, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) { - qemu_log("%s(" TARGET_ABI_FMT_ld ",", name->name, arg1); + print_syscall_prologue(name); print_fdset(arg1, arg2); qemu_log(","); print_fdset(arg1, arg3); @@ -650,14 +646,7 @@ print_newselect(const struct syscallname *name, print_fdset(arg1, arg4); qemu_log(","); print_timeval(arg5, 1); - qemu_log(")"); - - /* save for use in the return output function below */ - newselect_arg1=arg1; - newselect_arg2=arg2; - newselect_arg3=arg3; - newselect_arg4=arg4; - newselect_arg5=arg5; + print_syscall_epilogue(name); } #endif @@ -736,17 +725,29 @@ print_ipc(const struct syscallname *name, */ static void -print_syscall_ret_addr(const struct syscallname *name, abi_long ret) +print_syscall_err(abi_long ret) { const char *errstr = NULL; + qemu_log(" = "); if (ret < 0) { + qemu_log("-1 errno=%d", errno); errstr = target_strerror(-ret); + if (errstr) { + qemu_log(" (%s)", errstr); + } } - if (errstr) { - qemu_log(" = -1 errno=%d (%s)\n", (int)-ret, errstr); - } else { - qemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret); +} + +static void +print_syscall_ret_addr(const struct syscallname *name, abi_long ret, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_err(ret); + + if (ret >= 0) { + qemu_log("0x" TARGET_ABI_FMT_lx "\n", ret); } } @@ -760,17 +761,25 @@ print_syscall_ret_raw(struct syscallname *name, abi_long ret) #ifdef TARGET_NR__newselect static void -print_syscall_ret_newselect(const struct syscallname *name, abi_long ret) -{ - qemu_log(" = 0x" TARGET_ABI_FMT_lx " (", ret); - print_fdset(newselect_arg1,newselect_arg2); - qemu_log(","); - print_fdset(newselect_arg1,newselect_arg3); - qemu_log(","); - print_fdset(newselect_arg1,newselect_arg4); - qemu_log(","); - print_timeval(newselect_arg5, 1); - qemu_log(")\n"); +print_syscall_ret_newselect(const struct syscallname *name, abi_long ret, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_err(ret); + + if (ret >= 0) { + qemu_log(" = 0x" TARGET_ABI_FMT_lx " (", ret); + print_fdset(arg0, arg1); + qemu_log(","); + print_fdset(arg0, arg2); + qemu_log(","); + print_fdset(arg0, arg3); + qemu_log(","); + print_timeval(arg4, 1); + qemu_log(")"); + } + + qemu_log("\n"); } #endif @@ -783,18 +792,13 @@ print_syscall_ret_newselect(const struct syscallname *name, abi_long ret) #define TARGET_TIME_ERROR 5 /* clock not synchronized */ #ifdef TARGET_NR_adjtimex static void -print_syscall_ret_adjtimex(const struct syscallname *name, abi_long ret) +print_syscall_ret_adjtimex(const struct syscallname *name, abi_long ret, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) { - const char *errstr = NULL; + print_syscall_err(ret); - qemu_log(" = "); - if (ret < 0) { - qemu_log("-1 errno=%d", errno); - errstr = target_strerror(-ret); - if (errstr) { - qemu_log(" (%s)", errstr); - } - } else { + if (ret >= 0) { qemu_log(TARGET_ABI_FMT_ld, ret); switch (ret) { case TARGET_TIME_OK: @@ -822,6 +826,78 @@ print_syscall_ret_adjtimex(const struct syscallname *name, abi_long ret) } #endif +#if defined(TARGET_NR_listxattr) || defined(TARGET_NR_llistxattr) \ + || defined(TARGGET_NR_flistxattr) +static void +print_syscall_ret_listxattr(const struct syscallname *name, abi_long ret, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_err(ret); + + if (ret >= 0) { + qemu_log(TARGET_ABI_FMT_ld, ret); + qemu_log(" (list = "); + if (arg1 != 0) { + abi_long attr = arg1; + while (ret) { + if (attr != arg1) { + qemu_log(","); + } + print_string(attr, 1); + ret -= target_strlen(attr) + 1; + attr += target_strlen(attr) + 1; + } + } else { + qemu_log("NULL"); + } + qemu_log(")"); + } + + qemu_log("\n"); +} +#define print_syscall_ret_llistxattr print_syscall_ret_listxattr +#define print_syscall_ret_flistxattr print_syscall_ret_listxattr +#endif + +#ifdef TARGET_NR_ioctl +static void +print_syscall_ret_ioctl(const struct syscallname *name, abi_long ret, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_err(ret); + + if (ret >= 0) { + qemu_log(TARGET_ABI_FMT_ld, ret); + + const IOCTLEntry *ie; + const argtype *arg_type; + void *argptr; + int target_size; + + for (ie = ioctl_entries; ie->target_cmd != 0; ie++) { + if (ie->target_cmd == arg1) { + break; + } + } + + if (ie->target_cmd == arg1 && + (ie->access == IOC_R || ie->access == IOC_RW)) { + arg_type = ie->arg_type; + qemu_log(" ("); + arg_type++; + target_size = thunk_type_size(arg_type, 0); + argptr = lock_user(VERIFY_READ, arg2, target_size, 1); + thunk_print(argptr, arg_type); + unlock_user(argptr, arg2, target_size); + qemu_log(")"); + } + } + qemu_log("\n"); +} +#endif + UNUSED static struct flags access_flags[] = { FLAG_GENERIC(F_OK), FLAG_GENERIC(R_OK), @@ -1097,6 +1173,26 @@ UNUSED static struct flags statx_mask[] = { FLAG_END, }; +UNUSED static struct flags falloc_flags[] = { + FLAG_GENERIC(FALLOC_FL_KEEP_SIZE), + FLAG_GENERIC(FALLOC_FL_PUNCH_HOLE), +#ifdef FALLOC_FL_NO_HIDE_STALE + FLAG_GENERIC(FALLOC_FL_NO_HIDE_STALE), +#endif +#ifdef FALLOC_FL_COLLAPSE_RANGE + FLAG_GENERIC(FALLOC_FL_COLLAPSE_RANGE), +#endif +#ifdef FALLOC_FL_ZERO_RANGE + FLAG_GENERIC(FALLOC_FL_ZERO_RANGE), +#endif +#ifdef FALLOC_FL_INSERT_RANGE + FLAG_GENERIC(FALLOC_FL_INSERT_RANGE), +#endif +#ifdef FALLOC_FL_UNSHARE_RANGE + FLAG_GENERIC(FALLOC_FL_UNSHARE_RANGE), +#endif +}; + /* * print_xxx utility functions. These are used to print syscall * parameters in certain format. All of these have parameter @@ -1353,6 +1449,18 @@ print_access(const struct syscallname *name, } #endif +#ifdef TARGET_NR_acct +static void +print_acct(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_string(arg0, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_brk static void print_brk(const struct syscallname *name, @@ -1402,6 +1510,21 @@ print_chmod(const struct syscallname *name, } #endif +#if defined(TARGET_NR_chown) || defined(TARGET_NR_lchown) +static void +print_chown(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_string(arg0, 0); + print_raw_param("%d", arg1, 0); + print_raw_param("%d", arg2, 1); + print_syscall_epilogue(name); +} +#define print_lchown print_chown +#endif + #ifdef TARGET_NR_clock_adjtime static void print_clock_adjtime(const struct syscallname *name, @@ -1487,6 +1610,26 @@ print_faccessat(const struct syscallname *name, } #endif +#ifdef TARGET_NR_fallocate +static void +print_fallocate(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_flags(falloc_flags, arg1, 0); +#if TARGET_ABI_BITS == 32 + print_raw_param("%" PRIu64, target_offset64(arg2, arg3), 0); + print_raw_param("%" PRIu64, target_offset64(arg4, arg5), 1); +#else + print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); + print_raw_param(TARGET_ABI_FMT_ld, arg3, 1); +#endif + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_fchmodat static void print_fchmodat(const struct syscallname *name, @@ -1617,6 +1760,92 @@ print_fcntl(const struct syscallname *name, #define print_fcntl64 print_fcntl #endif +#ifdef TARGET_NR_fgetxattr +static void +print_fgetxattr(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_string(arg1, 0); + print_pointer(arg2, 0); + print_raw_param(TARGET_FMT_lu, arg3, 1); + print_syscall_epilogue(name); +} +#endif + +#ifdef TARGET_NR_flistxattr +static void +print_flistxattr(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_pointer(arg1, 0); + print_raw_param(TARGET_FMT_lu, arg2, 1); + print_syscall_epilogue(name); +} +#endif + +#if defined(TARGET_NR_getxattr) || defined(TARGET_NR_lgetxattr) +static void +print_getxattr(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_string(arg0, 0); + print_string(arg1, 0); + print_pointer(arg2, 0); + print_raw_param(TARGET_FMT_lu, arg3, 1); + print_syscall_epilogue(name); +} +#define print_lgetxattr print_getxattr +#endif + +#if defined(TARGET_NR_listxattr) || defined(TARGET_NR_llistxattr) +static void +print_listxattr(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_string(arg0, 0); + print_pointer(arg1, 0); + print_raw_param(TARGET_FMT_lu, arg2, 1); + print_syscall_epilogue(name); +} +#define print_llistxattr print_listxattr +#endif + +#if defined(TARGET_NR_fremovexattr) +static void +print_fremovexattr(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_string(arg1, 1); + print_syscall_epilogue(name); +} +#endif + +#if defined(TARGET_NR_removexattr) || defined(TARGET_NR_lremovexattr) +static void +print_removexattr(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_string(arg0, 0); + print_string(arg1, 1); + print_syscall_epilogue(name); +} +#define print_lremovexattr print_removexattr +#endif #ifdef TARGET_NR_futimesat static void @@ -1697,6 +1926,37 @@ print__llseek(const struct syscallname *name, } #endif +#ifdef TARGET_NR_lseek +static void +print_lseek(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_raw_param(TARGET_ABI_FMT_ld, arg1, 0); + switch (arg2) { + case SEEK_SET: + qemu_log("SEEK_SET"); break; + case SEEK_CUR: + qemu_log("SEEK_CUR"); break; + case SEEK_END: + qemu_log("SEEK_END"); break; +#ifdef SEEK_DATA + case SEEK_DATA: + qemu_log("SEEK_DATA"); break; +#endif +#ifdef SEEK_HOLE + case SEEK_HOLE: + qemu_log("SEEK_HOLE"); break; +#endif + default: + print_raw_param("%#x", arg2, 1); + } + print_syscall_epilogue(name); +} +#endif + #if defined(TARGET_NR_socket) static void print_socket(const struct syscallname *name, @@ -2804,6 +3064,75 @@ print_statx(const struct syscallname *name, } #endif +#ifdef TARGET_NR_ioctl +static void +print_ioctl(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + + const IOCTLEntry *ie; + const argtype *arg_type; + void *argptr; + int target_size; + + for (ie = ioctl_entries; ie->target_cmd != 0; ie++) { + if (ie->target_cmd == arg1) { + break; + } + } + + if (ie->target_cmd == 0) { + print_raw_param("%#x", arg1, 0); + print_raw_param("%#x", arg2, 1); + } else { + qemu_log("%s", ie->name); + arg_type = ie->arg_type; + + if (arg_type[0] != TYPE_NULL) { + qemu_log(","); + + switch (arg_type[0]) { + case TYPE_PTRVOID: + print_pointer(arg2, 1); + break; + case TYPE_CHAR: + case TYPE_SHORT: + case TYPE_INT: + print_raw_param("%d", arg2, 1); + break; + case TYPE_LONG: + print_raw_param(TARGET_ABI_FMT_ld, arg2, 1); + break; + case TYPE_ULONG: + print_raw_param(TARGET_ABI_FMT_lu, arg2, 1); + break; + case TYPE_PTR: + switch (ie->access) { + case IOC_R: + print_pointer(arg2, 1); + break; + case IOC_W: + case IOC_RW: + arg_type++; + target_size = thunk_type_size(arg_type, 0); + argptr = lock_user(VERIFY_READ, arg2, target_size, 1); + thunk_print(argptr, arg_type); + unlock_user(argptr, arg2, target_size); + break; + } + break; + default: + g_assert_not_reached(); + } + } + } + print_syscall_epilogue(name); +} +#endif + /* * An array of all of the syscalls we know about */ @@ -2847,25 +3176,25 @@ print_syscall(int num, void -print_syscall_ret(int num, abi_long ret) +print_syscall_ret(int num, abi_long ret, + abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6) { int i; - const char *errstr = NULL; for(i=0;i<nsyscalls;i++) if( scnames[i].nr == num ) { if( scnames[i].result != NULL ) { - scnames[i].result(&scnames[i], ret); + scnames[i].result(&scnames[i], ret, + arg1, arg2, arg3, + arg4, arg5, arg6); } else { - if (ret < 0) { - errstr = target_strerror(-ret); - } - if (errstr) { - qemu_log(" = -1 errno=" TARGET_ABI_FMT_ld " (%s)\n", - -ret, errstr); - } else { - qemu_log(" = " TARGET_ABI_FMT_ld "\n", ret); + print_syscall_err(ret); + + if (ret >= 0) { + qemu_log(TARGET_ABI_FMT_ld, ret); } + qemu_log("\n"); } break; } diff --git a/linux-user/strace.list b/linux-user/strace.list index 9281c0a758..a04706a524 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -13,7 +13,7 @@ { TARGET_NR_access, "access" , NULL, print_access, NULL }, #endif #ifdef TARGET_NR_acct -{ TARGET_NR_acct, "acct" , NULL, NULL, NULL }, +{ TARGET_NR_acct, "acct" , NULL, print_acct, NULL }, #endif #ifdef TARGET_NR_add_key { TARGET_NR_add_key, "add_key" , NULL, NULL, NULL }, @@ -71,7 +71,7 @@ { TARGET_NR_chmod, "chmod" , NULL, print_chmod, NULL }, #endif #ifdef TARGET_NR_chown -{ TARGET_NR_chown, "chown" , NULL, NULL, NULL }, +{ TARGET_NR_chown, "chown" , NULL, print_chown, NULL }, #endif #ifdef TARGET_NR_chown32 { TARGET_NR_chown32, "chown32" , NULL, NULL, NULL }, @@ -182,7 +182,7 @@ { TARGET_NR_fadvise64_64, "fadvise64_64" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_fallocate -{ TARGET_NR_fallocate, "fallocate" , NULL, NULL, NULL }, +{ TARGET_NR_fallocate, "fallocate" , NULL, print_fallocate, NULL }, #endif #ifdef TARGET_NR_fanotify_init { TARGET_NR_fanotify_init, "fanotify_init" , NULL, NULL, NULL }, @@ -215,16 +215,17 @@ { TARGET_NR_fcntl64, "fcntl64" , NULL, print_fcntl64, NULL }, #endif #ifdef TARGET_NR_fdatasync -{ TARGET_NR_fdatasync, "fdatasync" , NULL, NULL, NULL }, +{ TARGET_NR_fdatasync, "fdatasync" , "%s(%d)", NULL, NULL }, #endif #ifdef TARGET_NR_fgetxattr -{ TARGET_NR_fgetxattr, "fgetxattr" , NULL, NULL, NULL }, +{ TARGET_NR_fgetxattr, "fgetxattr" , NULL, print_fgetxattr, NULL }, #endif #ifdef TARGET_NR_finit_module { TARGET_NR_finit_module, "finit_module" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_flistxattr -{ TARGET_NR_flistxattr, "flistxattr" , NULL, NULL, NULL }, +{ TARGET_NR_flistxattr, "flistxattr" , NULL, print_flistxattr, + print_syscall_ret_flistxattr}, #endif #ifdef TARGET_NR_flock { TARGET_NR_flock, "flock" , NULL, NULL, NULL }, @@ -233,7 +234,7 @@ { TARGET_NR_fork, "fork" , "%s()", NULL, NULL }, #endif #ifdef TARGET_NR_fremovexattr -{ TARGET_NR_fremovexattr, "fremovexattr" , NULL, NULL, NULL }, +{ TARGET_NR_fremovexattr, "fremovexattr" , NULL, print_fremovexattr, NULL }, #endif #ifdef TARGET_NR_fsetxattr { TARGET_NR_fsetxattr, "fsetxattr" , NULL, NULL, NULL }, @@ -251,7 +252,7 @@ { TARGET_NR_fstatfs64, "fstatfs64" , "%s(%d,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_fsync -{ TARGET_NR_fsync, "fsync" , NULL, NULL, NULL }, +{ TARGET_NR_fsync, "fsync" , "%s(%d)", NULL, NULL }, #endif #ifdef TARGET_NR_ftime { TARGET_NR_ftime, "ftime" , NULL, NULL, NULL }, @@ -396,7 +397,7 @@ { TARGET_NR_getuid32, "getuid32" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_getxattr -{ TARGET_NR_getxattr, "getxattr" , NULL, NULL, NULL }, +{ TARGET_NR_getxattr, "getxattr" , NULL, print_getxattr, NULL }, #endif #ifdef TARGET_NR_getxgid { TARGET_NR_getxgid, "getxgid" , NULL, NULL, NULL }, @@ -432,7 +433,8 @@ { TARGET_NR_io_cancel, "io_cancel" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_ioctl -{ TARGET_NR_ioctl, "ioctl" , "%s(%d,%#x,%#x)", NULL, NULL }, +{ TARGET_NR_ioctl, "ioctl" , NULL, print_ioctl, + print_syscall_ret_ioctl}, #endif #ifdef TARGET_NR_io_destroy { TARGET_NR_io_destroy, "io_destroy" , NULL, NULL, NULL }, @@ -474,13 +476,13 @@ { TARGET_NR_kill, "kill", NULL, print_kill, NULL }, #endif #ifdef TARGET_NR_lchown -{ TARGET_NR_lchown, "lchown" , NULL, NULL, NULL }, +{ TARGET_NR_lchown, "lchown" , NULL, print_lchown, NULL }, #endif #ifdef TARGET_NR_lchown32 { TARGET_NR_lchown32, "lchown32" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_lgetxattr -{ TARGET_NR_lgetxattr, "lgetxattr" , NULL, NULL, NULL }, +{ TARGET_NR_lgetxattr, "lgetxattr" , NULL, print_lgetxattr, NULL }, #endif #ifdef TARGET_NR_link { TARGET_NR_link, "link" , NULL, print_link, NULL }, @@ -492,13 +494,15 @@ { TARGET_NR_Linux, "Linux" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_listen -{ TARGET_NR_listen, "listen" , NULL, NULL, NULL }, +{ TARGET_NR_listen, "listen" , "%s(%d,%d)", NULL, NULL }, #endif #ifdef TARGET_NR_listxattr -{ TARGET_NR_listxattr, "listxattr" , NULL, NULL, NULL }, +{ TARGET_NR_listxattr, "listxattr" , NULL, print_listxattr, + print_syscall_ret_listxattr}, #endif #ifdef TARGET_NR_llistxattr -{ TARGET_NR_llistxattr, "llistxattr" , NULL, NULL, NULL }, +{ TARGET_NR_llistxattr, "llistxattr" , NULL, print_llistxattr, + print_syscall_ret_llistxattr}, #endif #ifdef TARGET_NR__llseek { TARGET_NR__llseek, "_llseek" , NULL, print__llseek, NULL }, @@ -510,10 +514,10 @@ { TARGET_NR_lookup_dcookie, "lookup_dcookie" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_lremovexattr -{ TARGET_NR_lremovexattr, "lremovexattr" , NULL, NULL, NULL }, +{ TARGET_NR_lremovexattr, "lremovexattr" , NULL, print_lremovexattr, NULL }, #endif #ifdef TARGET_NR_lseek -{ TARGET_NR_lseek, "lseek" , NULL, NULL, NULL }, +{ TARGET_NR_lseek, "lseek" , NULL, print_lseek, NULL }, #endif #ifdef TARGET_NR_lsetxattr { TARGET_NR_lsetxattr, "lsetxattr" , NULL, NULL, NULL }, @@ -1116,7 +1120,7 @@ { TARGET_NR_remap_file_pages, "remap_file_pages" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_removexattr -{ TARGET_NR_removexattr, "removexattr" , NULL, NULL, NULL }, +{ TARGET_NR_removexattr, "removexattr" , NULL, print_removexattr, NULL }, #endif #ifdef TARGET_NR_rename { TARGET_NR_rename, "rename" , NULL, print_rename, NULL }, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 97de9fb5c9..82afadcea0 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -112,6 +112,9 @@ #include <linux/if_alg.h> #include <linux/rtc.h> #include <sound/asound.h> +#ifdef HAVE_DRM_H +#include <libdrm/drm.h> +#endif #include "linux_loop.h" #include "uname.h" @@ -4478,24 +4481,6 @@ STRUCT_MAX #undef STRUCT #undef STRUCT_SPECIAL -typedef struct IOCTLEntry IOCTLEntry; - -typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp, - int fd, int cmd, abi_long arg); - -struct IOCTLEntry { - int target_cmd; - unsigned int host_cmd; - const char *name; - int access; - do_ioctl_fn *do_ioctl; - const argtype arg_type[5]; -}; - -#define IOC_R 0x0001 -#define IOC_W 0x0002 -#define IOC_RW (IOC_R | IOC_W) - #define MAX_STRUCT_SIZE 4096 #ifdef CONFIG_FIEMAP @@ -5276,7 +5261,102 @@ static abi_long do_ioctl_tiocgptpeer(const IOCTLEntry *ie, uint8_t *buf_temp, } #endif -static IOCTLEntry ioctl_entries[] = { +#ifdef HAVE_DRM_H + +static void unlock_drm_version(struct drm_version *host_ver, + struct target_drm_version *target_ver, + bool copy) +{ + unlock_user(host_ver->name, target_ver->name, + copy ? host_ver->name_len : 0); + unlock_user(host_ver->date, target_ver->date, + copy ? host_ver->date_len : 0); + unlock_user(host_ver->desc, target_ver->desc, + copy ? host_ver->desc_len : 0); +} + +static inline abi_long target_to_host_drmversion(struct drm_version *host_ver, + struct target_drm_version *target_ver) +{ + memset(host_ver, 0, sizeof(*host_ver)); + + __get_user(host_ver->name_len, &target_ver->name_len); + if (host_ver->name_len) { + host_ver->name = lock_user(VERIFY_WRITE, target_ver->name, + target_ver->name_len, 0); + if (!host_ver->name) { + return -EFAULT; + } + } + + __get_user(host_ver->date_len, &target_ver->date_len); + if (host_ver->date_len) { + host_ver->date = lock_user(VERIFY_WRITE, target_ver->date, + target_ver->date_len, 0); + if (!host_ver->date) { + goto err; + } + } + + __get_user(host_ver->desc_len, &target_ver->desc_len); + if (host_ver->desc_len) { + host_ver->desc = lock_user(VERIFY_WRITE, target_ver->desc, + target_ver->desc_len, 0); + if (!host_ver->desc) { + goto err; + } + } + + return 0; +err: + unlock_drm_version(host_ver, target_ver, false); + return -EFAULT; +} + +static inline void host_to_target_drmversion( + struct target_drm_version *target_ver, + struct drm_version *host_ver) +{ + __put_user(host_ver->version_major, &target_ver->version_major); + __put_user(host_ver->version_minor, &target_ver->version_minor); + __put_user(host_ver->version_patchlevel, &target_ver->version_patchlevel); + __put_user(host_ver->name_len, &target_ver->name_len); + __put_user(host_ver->date_len, &target_ver->date_len); + __put_user(host_ver->desc_len, &target_ver->desc_len); + unlock_drm_version(host_ver, target_ver, true); +} + +static abi_long do_ioctl_drm(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, int cmd, abi_long arg) +{ + struct drm_version *ver; + struct target_drm_version *target_ver; + abi_long ret; + + switch (ie->host_cmd) { + case DRM_IOCTL_VERSION: + if (!lock_user_struct(VERIFY_WRITE, target_ver, arg, 0)) { + return -TARGET_EFAULT; + } + ver = (struct drm_version *)buf_temp; + ret = target_to_host_drmversion(ver, target_ver); + if (!is_error(ret)) { + ret = get_errno(safe_ioctl(fd, ie->host_cmd, ver)); + if (is_error(ret)) { + unlock_drm_version(ver, target_ver, false); + } else { + host_to_target_drmversion(target_ver, ver); + } + } + unlock_user_struct(target_ver, arg, 0); + return ret; + } + return -TARGET_ENOSYS; +} + +#endif + +IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, ...) \ { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, #define IOCTL_SPECIAL(cmd, access, dofn, ...) \ @@ -6614,22 +6694,6 @@ void syscall_init(void) } } -#if TARGET_ABI_BITS == 32 -static inline uint64_t target_offset64(uint32_t word0, uint32_t word1) -{ -#ifdef TARGET_WORDS_BIGENDIAN - return ((uint64_t)word0 << 32) | word1; -#else - return ((uint64_t)word1 << 32) | word0; -#endif -} -#else /* TARGET_ABI_BITS == 32 */ -static inline uint64_t target_offset64(uint64_t word0, uint64_t word1) -{ - return word0; -} -#endif /* TARGET_ABI_BITS != 32 */ - #ifdef TARGET_NR_truncate64 static inline abi_long target_truncate64(void *cpu_env, const char *arg1, abi_long arg2, @@ -12467,7 +12531,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg5, arg6, arg7, arg8); if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { - print_syscall_ret(num, ret); + print_syscall_ret(num, ret, arg1, arg2, arg3, arg4, arg5, arg6); } record_syscall_return(cpu, num, ret); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 152ec637cb..3c261cff0e 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -1167,6 +1167,9 @@ struct target_rtc_pll_info { #define TARGET_DM_TARGET_MSG TARGET_IOWRU(0xfd, 0x0e) #define TARGET_DM_DEV_SET_GEOMETRY TARGET_IOWRU(0xfd, 0x0f) +/* drm ioctls */ +#define TARGET_DRM_IOCTL_VERSION TARGET_IOWRU('d', 0x00) + /* from asm/termbits.h */ #define TARGET_NCC 8 @@ -2598,6 +2601,18 @@ struct target_mq_attr { abi_long mq_curmsgs; }; +struct target_drm_version { + int version_major; + int version_minor; + int version_patchlevel; + abi_ulong name_len; + abi_ulong name; + abi_ulong date_len; + abi_ulong date; + abi_ulong desc_len; + abi_ulong desc; +}; + #include "socket.h" #include "errno_defs.h" diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 4e12c1661e..3f1f033464 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -137,10 +137,32 @@ STRUCT(snd_timer_params, TYPE_INT, /* filter */ MK_ARRAY(TYPE_CHAR, 60)) /* reserved */ +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) +STRUCT(timeval, + TYPE_LONG, /* tv_sec */ + TYPE_INT) /* tv_usec */ + +STRUCT(_kernel_sock_timeval, + TYPE_LONG, /* tv_sec */ + TYPE_INT) /* tv_usec */ +#else +STRUCT(timeval, + TYPE_LONG, /* tv_sec */ + TYPE_LONG) /* tv_usec */ + +STRUCT(_kernel_sock_timeval, + TYPE_LONGLONG, /* tv_sec */ + TYPE_LONGLONG) /* tv_usec */ +#endif + STRUCT(timespec, TYPE_LONG, /* tv_sec */ TYPE_LONG) /* tv_nsec */ +STRUCT(_kernel_timespec, + TYPE_LONGLONG, /* tv_sec */ + TYPE_LONGLONG) /* tv_nsec */ + STRUCT(snd_timer_status, MK_STRUCT(STRUCT_timespec), /* tstamp */ TYPE_INT, /* resolution */ @@ -292,6 +314,17 @@ STRUCT(dm_target_versions, STRUCT(dm_target_msg, TYPE_ULONGLONG) /* sector */ +STRUCT(drm_version, + TYPE_INT, /* version_major */ + TYPE_INT, /* version_minor */ + TYPE_INT, /* version_patchlevel */ + TYPE_ULONG, /* name_len */ + TYPE_PTRVOID, /* name */ + TYPE_ULONG, /* date_len */ + TYPE_PTRVOID, /* date */ + TYPE_ULONG, /* desc_len */ + TYPE_PTRVOID) /* desc */ + STRUCT(file_clone_range, TYPE_LONGLONG, /* src_fd */ TYPE_ULONGLONG, /* src_offset */ diff --git a/migration/migration.c b/migration/migration.c index 481a590f72..92e44e021e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -338,12 +338,18 @@ bool migration_incoming_colo_enabled(void) void migration_incoming_disable_colo(void) { + ram_block_discard_disable(false); migration_colo_enabled = false; } -void migration_incoming_enable_colo(void) +int migration_incoming_enable_colo(void) { + if (ram_block_discard_disable(true)) { + error_report("COLO: cannot disable RAM discard"); + return -EBUSY; + } migration_colo_enabled = true; + return 0; } void migrate_add_address(SocketAddress *address) @@ -1772,6 +1778,13 @@ bool migration_in_postcopy_after_devices(MigrationState *s) return migration_in_postcopy() && s->postcopy_after_devices; } +bool migration_in_incoming_postcopy(void) +{ + PostcopyState ps = postcopy_state_get(); + + return ps >= POSTCOPY_INCOMING_DISCARD && ps < POSTCOPY_INCOMING_END; +} + bool migration_is_idle(void) { MigrationState *s = current_migration; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index a36402722b..1bb22f2b6c 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -27,7 +27,6 @@ #include "qemu/notify.h" #include "qemu/rcu.h" #include "sysemu/sysemu.h" -#include "sysemu/balloon.h" #include "qemu/error-report.h" #include "trace.h" #include "hw/boards.h" @@ -389,7 +388,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) */ if (munlockall()) { error_report("%s: munlockall: %s", __func__, strerror(errno)); - return -1; + goto out; } /* @@ -521,20 +520,6 @@ int postcopy_ram_incoming_init(MigrationIncomingState *mis) } /* - * Manage a single vote to the QEMU balloon inhibitor for all postcopy usage, - * last caller wins. - */ -static void postcopy_balloon_inhibit(bool state) -{ - static bool cur_state = false; - - if (state != cur_state) { - qemu_balloon_inhibit(state); - cur_state = state; - } -} - -/* * At the end of a migration where postcopy_ram_incoming_init was called. */ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) @@ -565,8 +550,6 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) mis->have_fault_thread = false; } - postcopy_balloon_inhibit(false); - if (enable_mlock) { if (os_mlock() < 0) { error_report("mlock: %s", strerror(errno)); @@ -1160,12 +1143,6 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) } memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size); - /* - * Ballooning can mark pages as absent while we're postcopying - * that would cause false userfaults. - */ - postcopy_balloon_inhibit(true); - trace_postcopy_ram_enable_notify(); return 0; diff --git a/migration/ram.c b/migration/ram.c index 069b6e30bc..5554a7d2d8 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -859,9 +859,11 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, /* Called with RCU critical section */ static void ramblock_sync_dirty_bitmap(RAMState *rs, RAMBlock *rb) { - rs->migration_dirty_pages += - cpu_physical_memory_sync_dirty_bitmap(rb, 0, rb->used_length, - &rs->num_dirty_pages_period); + uint64_t new_dirty_pages = + cpu_physical_memory_sync_dirty_bitmap(rb, 0, rb->used_length); + + rs->migration_dirty_pages += new_dirty_pages; + rs->num_dirty_pages_period += new_dirty_pages; } /** diff --git a/migration/rdma.c b/migration/rdma.c index 3b18823268..bea6532813 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -29,6 +29,7 @@ #include "qemu/sockets.h" #include "qemu/bitmap.h" #include "qemu/coroutine.h" +#include "exec/memory.h" #include <sys/socket.h> #include <netdb.h> #include <arpa/inet.h> @@ -4016,8 +4017,14 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) Error *local_err = NULL; trace_rdma_start_incoming_migration(); - rdma = qemu_rdma_data_init(host_port, &local_err); + /* Avoid ram_block_discard_disable(), cannot change during migration. */ + if (ram_block_discard_is_required()) { + error_setg(errp, "RDMA: cannot disable RAM discard"); + return; + } + + rdma = qemu_rdma_data_init(host_port, &local_err); if (rdma == NULL) { goto err; } @@ -4066,10 +4073,17 @@ void rdma_start_outgoing_migration(void *opaque, const char *host_port, Error **errp) { MigrationState *s = opaque; - RDMAContext *rdma = qemu_rdma_data_init(host_port, errp); RDMAContext *rdma_return_path = NULL; + RDMAContext *rdma; int ret = 0; + /* Avoid ram_block_discard_disable(), cannot change during migration. */ + if (ram_block_discard_is_required()) { + error_setg(errp, "RDMA: cannot disable RAM discard"); + return; + } + + rdma = qemu_rdma_data_init(host_port, errp); if (rdma == NULL) { goto err; } diff --git a/migration/savevm.c b/migration/savevm.c index b979ea6e7f..6e01724605 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2111,8 +2111,15 @@ static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis, static int loadvm_process_enable_colo(MigrationIncomingState *mis) { - migration_incoming_enable_colo(); - return colo_init_ram_cache(); + int ret = migration_incoming_enable_colo(); + + if (!ret) { + ret = colo_init_ram_cache(); + if (ret) { + migration_incoming_disable_colo(); + } + } + return ret; } /* diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 2b0b58a336..2ec13e4cc3 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -1821,6 +1821,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) MemoryDeviceInfoList *info_list = qmp_query_memory_devices(&err); MemoryDeviceInfoList *info; VirtioPMEMDeviceInfo *vpi; + VirtioMEMDeviceInfo *vmi; MemoryDeviceInfo *value; PCDIMMDeviceInfo *di; @@ -1855,6 +1856,21 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) monitor_printf(mon, " size: %" PRIu64 "\n", vpi->size); monitor_printf(mon, " memdev: %s\n", vpi->memdev); break; + case MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM: + vmi = value->u.virtio_mem.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + vmi->id ? vmi->id : ""); + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vmi->memaddr); + monitor_printf(mon, " node: %" PRId64 "\n", vmi->node); + monitor_printf(mon, " requested-size: %" PRIu64 "\n", + vmi->requested_size); + monitor_printf(mon, " size: %" PRIu64 "\n", vmi->size); + monitor_printf(mon, " max-size: %" PRIu64 "\n", vmi->max_size); + monitor_printf(mon, " block-size: %" PRIu64 "\n", + vmi->block_size); + monitor_printf(mon, " memdev: %s\n", vmi->memdev); + break; default: g_assert_not_reached(); } diff --git a/monitor/monitor.c b/monitor/monitor.c index 125494410a..19dcb8fbe3 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -235,6 +235,7 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { [QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS }, [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS }, [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE] = { 1000 * SCALE_MS }, }; /* diff --git a/net/Makefile.objs b/net/Makefile.objs index c5d076d19c..5ab45545db 100644 --- a/net/Makefile.objs +++ b/net/Makefile.objs @@ -26,7 +26,7 @@ tap-obj-$(CONFIG_SOLARIS) = tap-solaris.o tap-obj-y ?= tap-stub.o common-obj-$(CONFIG_POSIX) += tap.o $(tap-obj-y) common-obj-$(CONFIG_WIN32) += tap-win32.o - +common-obj-$(CONFIG_VHOST_NET_VDPA) += vhost-vdpa.o vde.o-libs = $(VDE_LIBS) common-obj-$(CONFIG_CAN_BUS) += can/ diff --git a/net/clients.h b/net/clients.h index a6ef267e19..92f9b59aed 100644 --- a/net/clients.h +++ b/net/clients.h @@ -61,4 +61,6 @@ int net_init_netmap(const Netdev *netdev, const char *name, int net_init_vhost_user(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); +int net_init_vhost_vdpa(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp); #endif /* QEMU_NET_CLIENTS_H */ @@ -325,6 +325,13 @@ void *qemu_get_nic_opaque(NetClientState *nc) return nic->opaque; } +NetClientState *qemu_get_peer(NetClientState *nc, int queue_index) +{ + assert(nc != NULL); + NetClientState *ncs = nc + queue_index; + return ncs->peer; +} + static void qemu_cleanup_net_client(NetClientState *nc) { QTAILQ_REMOVE(&net_clients, nc, next); @@ -959,6 +966,9 @@ static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])( #ifdef CONFIG_VHOST_NET_USER [NET_CLIENT_DRIVER_VHOST_USER] = net_init_vhost_user, #endif +#ifdef CONFIG_VHOST_NET_VDPA + [NET_CLIENT_DRIVER_VHOST_VDPA] = net_init_vhost_vdpa, +#endif #ifdef CONFIG_L2TPV3 [NET_CLIENT_DRIVER_L2TPV3] = net_init_l2tpv3, #endif diff --git a/net/tap-solaris.c b/net/tap-solaris.c index 4725d2314e..d03165c57c 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -27,6 +27,7 @@ #include "tap_int.h" #include "qemu/ctype.h" #include "qemu/cutils.h" +#include "qemu-common.h" #include <sys/ethernet.h> #include <sys/sockio.h> diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c new file mode 100644 index 0000000000..bc0e0d2d35 --- /dev/null +++ b/net/vhost-vdpa.c @@ -0,0 +1,228 @@ +/* + * vhost-vdpa.c + * + * Copyright(c) 2017-2018 Intel Corporation. + * Copyright(c) 2020 Red Hat, 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. + * + */ + +#include "qemu/osdep.h" +#include "clients.h" +#include "net/vhost_net.h" +#include "net/vhost-vdpa.h" +#include "hw/virtio/vhost-vdpa.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "qapi/error.h" +#include <sys/ioctl.h> +#include <err.h> +#include "standard-headers/linux/virtio_net.h" +#include "monitor/monitor.h" +#include "hw/virtio/vhost.h" + +/* Todo:need to add the multiqueue support here */ +typedef struct VhostVDPAState { + NetClientState nc; + struct vhost_vdpa vhost_vdpa; + VHostNetState *vhost_net; + uint64_t acked_features; + bool started; +} VhostVDPAState; + +const int vdpa_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_F_ANY_LAYOUT, + VIRTIO_F_VERSION_1, + VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GSO, + VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_ECN, + VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_PACKED, + VIRTIO_NET_F_GUEST_ANNOUNCE, + VHOST_INVALID_FEATURE_BIT +}; + +VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) +{ + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + return s->vhost_net; +} + +uint64_t vhost_vdpa_get_acked_features(NetClientState *nc) +{ + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + s->acked_features = vhost_net_get_acked_features(s->vhost_net); + + return s->acked_features; +} + +static int vhost_vdpa_net_check_device_id(struct vhost_net *net) +{ + uint32_t device_id; + int ret; + struct vhost_dev *hdev; + + hdev = (struct vhost_dev *)&net->dev; + ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id); + if (device_id != VIRTIO_ID_NET) { + return -ENOTSUP; + } + return ret; +} + +static void vhost_vdpa_del(NetClientState *ncs) +{ + VhostVDPAState *s; + assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + s = DO_UPCAST(VhostVDPAState, nc, ncs); + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + } +} + +static int vhost_vdpa_add(NetClientState *ncs, void *be) +{ + VhostNetOptions options; + struct vhost_net *net = NULL; + VhostVDPAState *s; + int ret; + + options.backend_type = VHOST_BACKEND_TYPE_VDPA; + assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + s = DO_UPCAST(VhostVDPAState, nc, ncs); + options.net_backend = ncs; + options.opaque = be; + options.busyloop_timeout = 0; + + net = vhost_net_init(&options); + if (!net) { + error_report("failed to init vhost_net for queue"); + goto err; + } + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + g_free(s->vhost_net); + } + s->vhost_net = net; + ret = vhost_vdpa_net_check_device_id(net); + if (ret) { + goto err; + } + return 0; +err: + if (net) { + vhost_net_cleanup(net); + } + vhost_vdpa_del(ncs); + return -1; +} + +static void vhost_vdpa_cleanup(NetClientState *nc) +{ + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + g_free(s->vhost_net); + s->vhost_net = NULL; + } +} + +static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) +{ + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + + return true; +} + +static bool vhost_vdpa_has_ufo(NetClientState *nc) +{ + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + uint64_t features = 0; + features |= (1ULL << VIRTIO_NET_F_HOST_UFO); + features = vhost_net_get_features(s->vhost_net, features); + return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO)); + +} + +static NetClientInfo net_vhost_vdpa_info = { + .type = NET_CLIENT_DRIVER_VHOST_VDPA, + .size = sizeof(VhostVDPAState), + .cleanup = vhost_vdpa_cleanup, + .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, + .has_ufo = vhost_vdpa_has_ufo, +}; + +static int net_vhost_vdpa_init(NetClientState *peer, const char *device, + const char *name, const char *vhostdev) +{ + NetClientState *nc = NULL; + VhostVDPAState *s; + int vdpa_device_fd = -1; + int ret = 0; + assert(name); + nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); + snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); + nc->queue_index = 0; + s = DO_UPCAST(VhostVDPAState, nc, nc); + vdpa_device_fd = qemu_open(vhostdev, O_RDWR); + if (vdpa_device_fd == -1) { + return -errno; + } + s->vhost_vdpa.device_fd = vdpa_device_fd; + ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); + assert(s->vhost_net); + return ret; +} + +static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) +{ + const char *name = opaque; + const char *driver, *netdev; + + driver = qemu_opt_get(opts, "driver"); + netdev = qemu_opt_get(opts, "netdev"); + if (!driver || !netdev) { + return 0; + } + if (strcmp(netdev, name) == 0 && + !g_str_has_prefix(driver, "virtio-net-")) { + error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); + return -1; + } + return 0; +} + +int net_init_vhost_vdpa(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp) +{ + const NetdevVhostVDPAOptions *opts; + + assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); + opts = &netdev->u.vhost_vdpa; + /* verify net frontend */ + if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, + (char *)name, errp)) { + return -1; + } + return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); +} diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex b9da9d8ecb..3074686a8c 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index a048b6b077..50bc880272 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -38,5 +38,8 @@ s390-netboot.img: @echo "s390-netboot.img not built since roms/SLOF/ is not available." endif +ALL_OBJS = $(sort $(OBJECTS) $(NETOBJS) $(LIBCOBJS) $(LIBNETOBJS)) +-include $(ALL_OBJS:%.o=%.d) + clean: rm -f *.o *.d *.img *.elf *~ *.a diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index d13b7cbd15..97205674e5 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -328,9 +328,7 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) msg[0] = '2'; break; default: - msg[0] = vlbl->LDL_version; - msg[0] &= 0x0f; /* convert EBCDIC */ - msg[0] |= 0x30; /* to ASCII (digit) */ + msg[0] = ebc2asc[vlbl->LDL_version]; msg[1] = '?'; break; } diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c index 339ec5fbe7..83ca27ab41 100644 --- a/pc-bios/s390-ccw/cio.c +++ b/pc-bios/s390-ccw/cio.c @@ -49,13 +49,13 @@ void enable_subchannel(SubChannelId schid) uint16_t cu_type(SubChannelId schid) { - Ccw1 sense_id_ccw; SenseId sense_data; - - sense_id_ccw.cmd_code = CCW_CMD_SENSE_ID; - sense_id_ccw.cda = ptr2u32(&sense_data); - sense_id_ccw.count = sizeof(sense_data); - sense_id_ccw.flags |= CCW_FLAG_SLI; + Ccw1 sense_id_ccw = { + .cmd_code = CCW_CMD_SENSE_ID, + .flags = CCW_FLAG_SLI, + .count = sizeof(sense_data), + .cda = ptr2u32(&sense_data), + }; if (do_cio(schid, CU_TYPE_UNKNOWN, ptr2u32(&sense_id_ccw), CCW_FMT1)) { panic("Failed to run SenseID CCw\n"); @@ -67,13 +67,13 @@ uint16_t cu_type(SubChannelId schid) int basic_sense(SubChannelId schid, uint16_t cutype, void *sense_data, uint16_t data_size) { - Ccw1 senseCcw; + Ccw1 senseCcw = { + .cmd_code = CCW_CMD_BASIC_SENSE, + .count = data_size, + .cda = ptr2u32(sense_data), + }; Irb irb; - senseCcw.cmd_code = CCW_CMD_BASIC_SENSE; - senseCcw.cda = ptr2u32(sense_data); - senseCcw.count = data_size; - return __do_cio(schid, ptr2u32(&senseCcw), CCW_FMT1, &irb); } @@ -314,7 +314,17 @@ static void print_irb_err(Irb *irb) */ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb) { - CmdOrb orb = {}; + /* + * QEMU's CIO implementation requires prefetch and 64-bit idaws. We + * allow all paths. + */ + CmdOrb orb = { + .fmt = fmt, + .pfch = 1, + .c64 = 1, + .lpm = 0xFF, + .cpa = ccw_addr, + }; int rc; IPL_assert(fmt == 0 || fmt == 1, "Invalid ccw format"); @@ -324,12 +334,6 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb) IPL_assert(ccw_addr <= 0xFFFFFF - 8, "Invalid ccw address"); } - orb.fmt = fmt; - orb.pfch = 1; /* QEMU's cio implementation requires prefetch */ - orb.c64 = 1; /* QEMU's cio implementation requires 64-bit idaws */ - orb.lpm = 0xFF; /* All paths allowed */ - orb.cpa = ccw_addr; - rc = ssch(schid, &orb); if (rc == 1 || rc == 2) { /* Subchannel status pending or busy. Eat status and ask for retry. */ diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h index aaa432dedd..1e5d4e92e1 100644 --- a/pc-bios/s390-ccw/cio.h +++ b/pc-bios/s390-ccw/cio.h @@ -122,12 +122,17 @@ typedef struct schib { } __attribute__ ((packed, aligned(4))) Schib; typedef struct subchannel_id { - __u32 cssid:8; - __u32:4; - __u32 m:1; - __u32 ssid:2; - __u32 one:1; - __u32 sch_no:16; + union { + struct { + __u16 cssid:8; + __u16 reserved:4; + __u16 m:1; + __u16 ssid:2; + __u16 one:1; + }; + __u16 sch_id; + }; + __u16 sch_no; } __attribute__ ((packed, aligned(4))) SubChannelId; struct chsc_header { diff --git a/pc-bios/s390-ccw/helper.h b/pc-bios/s390-ccw/helper.h index 78d5bc7442..dfcfea0ff0 100644 --- a/pc-bios/s390-ccw/helper.h +++ b/pc-bios/s390-ccw/helper.h @@ -14,11 +14,12 @@ #define S390_CCW_HELPER_H #include "s390-ccw.h" +#include "s390-time.h" /* Avoids compiler warnings when casting a pointer to a u32 */ static inline uint32_t ptr2u32(void *ptr) { - IPL_assert((uint64_t)ptr <= 0xffffffff, "ptr2u32: ptr too large"); + IPL_assert((uint64_t)ptr <= 0xffffffffull, "ptr2u32: ptr too large"); return (uint32_t)(uint64_t)ptr; } @@ -28,4 +29,20 @@ static inline void *u32toptr(uint32_t n) return (void *)(uint64_t)n; } +static inline void yield(void) +{ + asm volatile ("diag 0,0,0x44" + : : + : "memory", "cc"); +} + +static inline void sleep(unsigned int seconds) +{ + ulong target = get_time_seconds() + seconds; + + while (get_time_seconds() < target) { + yield(); + } +} + #endif diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c index 4eba2510b0..767012bf0c 100644 --- a/pc-bios/s390-ccw/jump2ipl.c +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -8,12 +8,10 @@ #include "libc.h" #include "s390-ccw.h" +#include "s390-arch.h" #define KERN_IMAGE_START 0x010000UL -#define PSW_MASK_64 0x0000000100000000ULL -#define PSW_MASK_32 0x0000000080000000ULL -#define PSW_MASK_SHORTPSW 0x0008000000000000ULL -#define RESET_PSW_MASK (PSW_MASK_SHORTPSW | PSW_MASK_32 | PSW_MASK_64) +#define RESET_PSW_MASK (PSW_MASK_SHORTPSW | PSW_MASK_64) typedef struct ResetInfo { uint64_t ipl_psw; @@ -54,7 +52,7 @@ void jump_to_IPL_code(uint64_t address) current->ipl_psw = (uint64_t) &jump_to_IPL_2; current->ipl_psw |= RESET_PSW_MASK; - current->ipl_continue = address & 0x7fffffff; + current->ipl_continue = address & PSW_MASK_SHORT_ADDR; debug_print_int("set IPL addr to", current->ipl_continue); @@ -86,7 +84,7 @@ void jump_to_low_kernel(void) /* Trying to get PSW at zero address */ if (*((uint64_t *)0) & RESET_PSW_MASK) { - jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); + jump_to_IPL_code((*((uint64_t *)0)) & PSW_MASK_SHORT_ADDR); } /* No other option left, so use the Linux kernel start address */ diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 4e65b411e1..146a50760b 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -36,11 +36,9 @@ LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ */ void write_subsystem_identification(void) { - SubChannelId *schid = (SubChannelId *) 184; - uint32_t *zeroes = (uint32_t *) 188; - - *schid = blk_schid; - *zeroes = 0; + lowcore->subchannel_id = blk_schid.sch_id; + lowcore->subchannel_nr = blk_schid.sch_no; + lowcore->io_int_parm = 0; } void write_iplb_location(void) @@ -48,13 +46,6 @@ void write_iplb_location(void) lowcore->ptr_iplb = ptr2u32(&iplb); } -void panic(const char *string) -{ - sclp_print(string); - disabled_wait(); - while (1) { } -} - unsigned int get_loadparm_index(void) { return atoui(loadparm_str); diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index ce3815b201..de8260a5d6 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -12,6 +12,7 @@ #include "libc.h" #include "s390-ccw.h" #include "sclp.h" +#include "s390-time.h" #define KEYCODE_NO_INP '\0' #define KEYCODE_ESCAPE '\033' diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index 5eefb7c289..577c023afe 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -1,8 +1,7 @@ SLOF_DIR := $(SRC_PATH)/roms/SLOF -NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o \ - libnet.a libc.a +NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include LIBNET_INC := -I$(SLOF_DIR)/lib/libnet @@ -11,15 +10,16 @@ NETLDFLAGS := $(LDFLAGS) -Ttext=0x7800000 $(NETOBJS): QEMU_CFLAGS += $(LIBC_INC) $(LIBNET_INC) -s390-netboot.elf: $(NETOBJS) - $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $(NETOBJS),"BUILD","$(TARGET_DIR)$@") +s390-netboot.elf: $(NETOBJS) libnet.a libc.a + $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,"BUILD","$(TARGET_DIR)$@") s390-netboot.img: s390-netboot.elf $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,"STRIP","$(TARGET_DIR)$@") # libc files: -LIBC_CFLAGS := $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) +LIBC_CFLAGS = $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ + -MMD -MP -MT $@ -MF $(@:%.o=%.d) CTYPE_OBJS = isdigit.o isxdigit.o toupper.o %.o : $(SLOF_DIR)/lib/libc/ctype/%.c @@ -52,7 +52,8 @@ libc.a: $(LIBCOBJS) LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o -LIBNETCFLAGS := $(QEMU_CFLAGS) $(CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) +LIBNETCFLAGS = $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ + -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d) %.o : $(SLOF_DIR)/lib/libnet/%.c $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 309ffa30d9..056e93a818 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -35,6 +35,7 @@ #include "s390-ccw.h" #include "cio.h" #include "virtio.h" +#include "s390-time.h" #define DEFAULT_BOOT_RETRIES 10 #define DEFAULT_TFTP_RETRIES 20 @@ -57,24 +58,14 @@ static SubChannelId net_schid = { .one = 1 }; static uint8_t mac[6]; static uint64_t dest_timer; -static uint64_t get_timer_ms(void) -{ - uint64_t clk; - - asm volatile(" stck %0 " : : "Q"(clk) : "memory"); - - /* Bit 51 is incremented each microsecond */ - return (clk >> (63 - 51)) / 1000; -} - void set_timer(int val) { - dest_timer = get_timer_ms() + val; + dest_timer = get_time_ms() + val; } int get_timer(void) { - return dest_timer - get_timer_ms(); + return dest_timer - get_time_ms(); } int get_sec_ticks(void) @@ -448,14 +439,6 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) return rc; } -void panic(const char *string) -{ - sclp_print(string); - for (;;) { - disabled_wait(); - } -} - void write_subsystem_identification(void) { SubChannelId *schid = (SubChannelId *) 184; diff --git a/pc-bios/s390-ccw/s390-arch.h b/pc-bios/s390-ccw/s390-arch.h index 5f36361c02..6da44d4436 100644 --- a/pc-bios/s390-ccw/s390-arch.h +++ b/pc-bios/s390-ccw/s390-arch.h @@ -26,10 +26,12 @@ _Static_assert(sizeof(struct PSWLegacy) == 8, "PSWLegacy size incorrect"); /* s390 psw bit masks */ #define PSW_MASK_IOINT 0x0200000000000000ULL +#define PSW_MASK_SHORTPSW 0x0008000000000000ULL #define PSW_MASK_WAIT 0x0002000000000000ULL #define PSW_MASK_EAMODE 0x0000000100000000ULL #define PSW_MASK_BAMODE 0x0000000080000000ULL -#define PSW_MASK_ZMODE (PSW_MASK_EAMODE | PSW_MASK_BAMODE) +#define PSW_MASK_SHORT_ADDR 0x000000007fffffffULL +#define PSW_MASK_64 (PSW_MASK_EAMODE | PSW_MASK_BAMODE) /* Low core mapping */ typedef struct LowCore { diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 21f27e7990..36b884cced 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -50,12 +50,11 @@ typedef unsigned long long __u64; #include "iplb.h" /* start.s */ -void disabled_wait(void); +void disabled_wait(void) __attribute__ ((__noreturn__)); void consume_sclp_int(void); void consume_io_int(void); /* main.c */ -void panic(const char *string); void write_subsystem_identification(void); void write_iplb_location(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); @@ -74,8 +73,6 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, bool virtio_is_supported(SubChannelId schid); void virtio_blk_setup_device(SubChannelId schid); int virtio_read(ulong sector, void *load_addr); -u64 get_clock(void); -ulong get_second(void); /* bootmap.c */ void zipl_load(void); @@ -93,6 +90,12 @@ bool menu_is_enabled_enum(void); #define MAX_BOOT_ENTRIES 31 +static inline void panic(const char *string) +{ + sclp_print(string); + disabled_wait(); +} + static inline void fill_hex(char *out, unsigned char val) { const char hex[] = "0123456789abcdef"; @@ -144,24 +147,8 @@ static inline void debug_print_addr(const char *desc, void *p) #define KVM_S390_VIRTIO_SET_STATUS 2 #define KVM_S390_VIRTIO_CCW_NOTIFY 3 -static inline void yield(void) -{ - asm volatile ("diag 0,0,0x44" - : : - : "memory", "cc"); -} - #define MAX_SECTOR_SIZE 4096 -static inline void sleep(unsigned int seconds) -{ - ulong target = get_second() + seconds; - - while (get_second() < target) { - yield(); - } -} - static inline void IPL_assert(bool term, const char *message) { if (!term) { diff --git a/pc-bios/s390-ccw/s390-time.h b/pc-bios/s390-ccw/s390-time.h new file mode 100644 index 0000000000..ed6d982371 --- /dev/null +++ b/pc-bios/s390-ccw/s390-time.h @@ -0,0 +1,23 @@ +#ifndef TIME_H +#define TIME_H + +static inline u64 get_clock(void) +{ + u64 r; + + asm volatile("stck %0" : "=Q" (r) : : "cc"); + return r; +} + +static inline u64 get_time_ms(void) +{ + /* Bit 51 is incremented each microsecond */ + return (get_clock() >> 12) / 1000; +} + +static inline u64 get_time_seconds(void) +{ + return get_time_ms() / 1000; +} + +#endif diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S index aa8fceb19d..ce519300a1 100644 --- a/pc-bios/s390-ccw/start.S +++ b/pc-bios/s390-ccw/start.S @@ -47,8 +47,9 @@ memsetxc: */ .globl disabled_wait disabled_wait: - larl %r1,disabled_wait_psw - lpswe 0(%r1) + larl %r1,disabled_wait_psw + lpswe 0(%r1) +1: j 1b /* diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c index ff7f4dad25..2fcb0a58c5 100644 --- a/pc-bios/s390-ccw/virtio-net.c +++ b/pc-bios/s390-ccw/virtio-net.c @@ -19,6 +19,8 @@ #include <ethernet.h> #include "s390-ccw.h" #include "virtio.h" +#include "s390-time.h" +#include "helper.h" #ifndef DEBUG_VIRTIO_NET #define DEBUG_VIRTIO_NET 0 diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index 4fe4b9d261..eddfb8a7ad 100644 --- a/pc-bios/s390-ccw/virtio-scsi.c +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -14,6 +14,8 @@ #include "virtio.h" #include "scsi.h" #include "virtio-scsi.h" +#include "s390-time.h" +#include "helper.h" static ScsiDevice default_scsi_device; static VirtioScsiCmdReq req; diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index fb40ca9828..ab49840db8 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -15,6 +15,7 @@ #include "virtio-scsi.h" #include "bswap.h" #include "helper.h" +#include "s390-time.h" #define VRING_WAIT_REPLY_TIMEOUT 30 @@ -157,19 +158,6 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags) } } -u64 get_clock(void) -{ - u64 r; - - asm volatile("stck %0" : "=Q" (r) : : "cc"); - return r; -} - -ulong get_second(void) -{ - return (get_clock() >> 12) / 1000000; -} - int vr_poll(VRing *vr) { if (vr->used->idx == vr->used_idx) { @@ -194,7 +182,7 @@ int vr_poll(VRing *vr) */ int vring_wait_reply(void) { - ulong target_second = get_second() + vdev.wait_reply_timeout; + ulong target_second = get_time_seconds() + vdev.wait_reply_timeout; /* Wait for any queue to be updated by the host */ do { @@ -207,7 +195,7 @@ int vring_wait_reply(void) if (r) { return 0; } - } while (!vdev.wait_reply_timeout || (get_second() < target_second)); + } while (!vdev.wait_reply_timeout || (get_time_seconds() < target_second)); return 1; } diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img Binary files differindex 6bb5c86eb0..d9c319604b 100644 --- a/pc-bios/s390-netboot.img +++ b/pc-bios/s390-netboot.img diff --git a/qapi/block-core.json b/qapi/block-core.json index 0e1c6a59f2..b20332e592 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4675,6 +4675,74 @@ 'options': 'BlockdevCreateOptions' } } ## +# @BlockdevAmendOptionsLUKS: +# +# Driver specific image amend options for LUKS. +# +# Since: 5.1 +## +{ 'struct': 'BlockdevAmendOptionsLUKS', + 'base': 'QCryptoBlockAmendOptionsLUKS', + 'data': { } +} + +## +# @BlockdevAmendOptionsQcow2: +# +# Driver specific image amend options for qcow2. +# For now, only encryption options can be amended +# +# @encrypt Encryption options to be amended +# +# Since: 5.1 +## +{ 'struct': 'BlockdevAmendOptionsQcow2', + 'data': { '*encrypt': 'QCryptoBlockAmendOptions' } } + +## +# @BlockdevAmendOptions: +# +# Options for amending an image format +# +# @driver: Block driver of the node to amend. +# +# Since: 5.1 +## +{ 'union': 'BlockdevAmendOptions', + 'base': { + 'driver': 'BlockdevDriver' }, + 'discriminator': 'driver', + 'data': { + 'luks': 'BlockdevAmendOptionsLUKS', + 'qcow2': 'BlockdevAmendOptionsQcow2' } } + +## +# @x-blockdev-amend: +# +# Starts a job to amend format specific options of an existing open block device +# The job is automatically finalized, but a manual job-dismiss is required. +# +# @job-id: Identifier for the newly created job. +# +# @node-name: Name of the block node to work on +# +# @options: Options (driver specific) +# +# @force: Allow unsafe operations, format specific +# For luks that allows erase of the last active keyslot +# (permanent loss of data), +# and replacement of an active keyslot +# (possible loss of data if IO error happens) +# +# Since: 5.1 +## +{ 'command': 'x-blockdev-amend', + 'data': { 'job-id': 'str', + 'node-name': 'str', + 'options': 'BlockdevAmendOptions', + '*force': 'bool' } } + +## # @BlockErrorAction: # # An enumeration of action that has been taken when a DISK I/O occurs diff --git a/qapi/crypto.json b/qapi/crypto.json index b2a4cff683..5a68e0db25 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -297,7 +297,6 @@ 'uuid': 'str', 'slots': [ 'QCryptoBlockInfoLUKSSlot' ] }} - ## # @QCryptoBlockInfo: # @@ -309,3 +308,75 @@ 'base': 'QCryptoBlockInfoBase', 'discriminator': 'format', 'data': { 'luks': 'QCryptoBlockInfoLUKS' } } + +## +# @QCryptoBlockLUKSKeyslotState: +# +# Defines state of keyslots that are affected by the update +# +# @active: The slots contain the given password and marked as active +# @inactive: The slots are erased (contain garbage) and marked as inactive +# +# Since: 5.1 +## +{ 'enum': 'QCryptoBlockLUKSKeyslotState', + 'data': [ 'active', 'inactive' ] } + + +## +# @QCryptoBlockAmendOptionsLUKS: +# +# This struct defines the update parameters that activate/de-activate set +# of keyslots +# +# @state: the desired state of the keyslots +# +# @new-secret: The ID of a QCryptoSecret object providing the password to be +# written into added active keyslots +# +# @old-secret: Optional (for deactivation only) +# If given will deactive all keyslots that +# match password located in QCryptoSecret with this ID +# +# @iter-time: Optional (for activation only) +# Number of milliseconds to spend in +# PBKDF passphrase processing for the newly activated keyslot. +# Currently defaults to 2000. +# +# @keyslot: Optional. ID of the keyslot to activate/deactivate. +# For keyslot activation, keyslot should not be active already +# (this is unsafe to update an active keyslot), +# but possible if 'force' parameter is given. +# If keyslot is not given, first free keyslot will be written. +# +# For keyslot deactivation, this parameter specifies the exact +# keyslot to deactivate +# +# @secret: Optional. The ID of a QCryptoSecret object providing the +# password to use to retrive current master key. +# Defaults to the same secret that was used to open the image +# +# +# Since 5.1 +## +{ 'struct': 'QCryptoBlockAmendOptionsLUKS', + 'data': { 'state': 'QCryptoBlockLUKSKeyslotState', + '*new-secret': 'str', + '*old-secret': 'str', + '*keyslot': 'int', + '*iter-time': 'int', + '*secret': 'str' } } + +## +# @QCryptoBlockAmendOptions: +# +# The options that are available for all encryption formats +# when amending encryption settings +# +# Since: 5.1 +## +{ 'union': 'QCryptoBlockAmendOptions', + 'base': 'QCryptoBlockOptionsBase', + 'discriminator': 'format', + 'data': { + 'luks': 'QCryptoBlockAmendOptionsLUKS' } } diff --git a/qapi/job.json b/qapi/job.json index 5e658281f5..c48a0c3e34 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -19,10 +19,12 @@ # # @create: image creation job type, see "blockdev-create" (since 3.0) # +# @amend: image options amend job type, see "x-blockdev-amend" (since 5.1) +# # Since: 1.7 ## { 'enum': 'JobType', - 'data': ['commit', 'stream', 'mirror', 'backup', 'create'] } + 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend'] } ## # @JobStatus: diff --git a/qapi/misc.json b/qapi/misc.json index a5a0beb902..149c925246 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -1357,18 +1357,55 @@ } ## +# @VirtioMEMDeviceInfo: +# +# VirtioMEMDevice state information +# +# @id: device's ID +# +# @memaddr: physical address in memory, where device is mapped +# +# @requested-size: the user requested size of the device +# +# @size: the (current) size of memory that the device provides +# +# @max-size: the maximum size of memory that the device can provide +# +# @block-size: the block size of memory that the device provides +# +# @node: NUMA node number where device is assigned to +# +# @memdev: memory backend linked with the region +# +# Since: 5.1 +## +{ 'struct': 'VirtioMEMDeviceInfo', + 'data': { '*id': 'str', + 'memaddr': 'size', + 'requested-size': 'size', + 'size': 'size', + 'max-size': 'size', + 'block-size': 'size', + 'node': 'int', + 'memdev': 'str' + } +} + +## # @MemoryDeviceInfo: # # Union containing information about a memory device # # nvdimm is included since 2.12. virtio-pmem is included since 4.1. +# virtio-mem is included since 5.1. # # Since: 2.1 ## { 'union': 'MemoryDeviceInfo', 'data': { 'dimm': 'PCDIMMDeviceInfo', 'nvdimm': 'PCDIMMDeviceInfo', - 'virtio-pmem': 'VirtioPMEMDeviceInfo' + 'virtio-pmem': 'VirtioPMEMDeviceInfo', + 'virtio-mem': 'VirtioMEMDeviceInfo' } } @@ -1398,6 +1435,31 @@ { 'command': 'query-memory-devices', 'returns': ['MemoryDeviceInfo'] } ## +# @MEMORY_DEVICE_SIZE_CHANGE: +# +# Emitted when the size of a memory device changes. Only emitted for memory +# devices that can actually change the size (e.g., virtio-mem due to guest +# action). +# +# @id: device's ID +# @size: the new size of memory that the device provides +# +# Note: this event is rate-limited. +# +# Since: 5.1 +# +# Example: +# +# <- { "event": "MEMORY_DEVICE_SIZE_CHANGE", +# "data": { "id": "vm0", "size": 1073741824}, +# "timestamp": { "seconds": 1588168529, "microseconds": 201316 } } +# +## +{ 'event': 'MEMORY_DEVICE_SIZE_CHANGE', + 'data': { '*id': 'str', 'size': 'size' } } + + +## # @MEM_UNPLUG_ERROR: # # Emitted when memory hot unplug error occurs. diff --git a/qapi/net.json b/qapi/net.json index 9244c9af56..558d520a2f 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -429,15 +429,38 @@ '*queues': 'int' } } ## +# @NetdevVhostVDPAOptions: +# +# Vhost-vdpa network backend +# +# vDPA device is a device that uses a datapath which complies with the virtio +# specifications with a vendor specific control path. +# +# @vhostdev: path of vhost-vdpa device +# (default:'/dev/vhost-vdpa-0') +# +# @queues: number of queues to be created for multiqueue vhost-vdpa +# (default: 1) +# +# Since: 5.1 +## +{ 'struct': 'NetdevVhostVDPAOptions', + 'data': { + '*vhostdev': 'str', + '*queues': 'int' } } + +## # @NetClientDriver: # # Available netdev drivers. # # Since: 2.7 +# +# @vhost-vdpa since 5.1 ## { 'enum': 'NetClientDriver', 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', - 'bridge', 'hubport', 'netmap', 'vhost-user' ] } + 'bridge', 'hubport', 'netmap', 'vhost-user', 'vhost-vdpa' ] } ## # @Netdev: @@ -465,7 +488,8 @@ 'bridge': 'NetdevBridgeOptions', 'hubport': 'NetdevHubPortOptions', 'netmap': 'NetdevNetmapOptions', - 'vhost-user': 'NetdevVhostUserOptions' } } + 'vhost-user': 'NetdevVhostUserOptions', + 'vhost-vdpa': 'NetdevVhostVDPAOptions' } } ## # @NetFilterDirection: diff --git a/qdev-monitor.c b/qdev-monitor.c index 22da107484..648b8ac4fa 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -53,7 +53,9 @@ typedef struct QDevAlias /* Please keep this table sorted by typename. */ static const QDevAlias qdev_alias_table[] = { + { "AC97", "ac97" }, /* -soundhw name */ { "e1000", "e1000-82540em" }, + { "ES1370", "es1370" }, /* -soundhw name */ { "ich9-ahci", "ahci" }, { "lsi53c895a", "lsi" }, { "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_S390X }, @@ -147,6 +149,7 @@ static void qdev_print_devinfos(bool show_no_user) int i; bool cat_printed; + module_load_qom_all(); list = object_class_get_list_sorted(TYPE_DEVICE, false); for (i = 0; i <= DEVICE_CATEGORY_MAX; i++) { @@ -215,13 +218,13 @@ static DeviceClass *qdev_get_device_class(const char **driver, Error **errp) DeviceClass *dc; const char *original_name = *driver; - oc = object_class_by_name(*driver); + oc = module_object_class_by_name(*driver); if (!oc) { const char *typename = find_typename_by_alias(*driver); if (typename) { *driver = typename; - oc = object_class_by_name(*driver); + oc = module_object_class_by_name(*driver); } } diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 10b910b67c..b89c019b76 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -10,9 +10,9 @@ HXCOMM When amending the rST sections, please remember to copy the usage HXCOMM over to the per-command sections in docs/tools/qemu-img.rst. DEF("amend", img_amend, - "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] -o options filename") + "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] [--force] -o options filename") SRST -.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] -o OPTIONS FILENAME +.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] [--force] -o OPTIONS FILENAME ERST DEF("bench", img_bench, diff --git a/qemu-img.c b/qemu-img.c index d7e846e607..53bd32bf8f 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -79,6 +79,7 @@ enum { OPTION_DISABLE = 273, OPTION_MERGE = 274, OPTION_BITMAPS = 275, + OPTION_FORCE = 276, }; typedef enum OutputFormat { @@ -1680,7 +1681,6 @@ typedef struct ImgConvertState { BlockBackend *target; bool has_zero_init; bool compressed; - bool unallocated_blocks_are_zero; bool target_is_new; bool target_has_backing; int64_t target_backing_sectors; /* negative if unknown */ @@ -1725,7 +1725,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) if (s->target_backing_sectors >= 0) { if (sector_num >= s->target_backing_sectors) { - post_backing_zero = s->unallocated_blocks_are_zero; + post_backing_zero = true; } else if (sector_num + n > s->target_backing_sectors) { /* Split requests around target_backing_sectors (because * starting from there, zeros are handled differently) */ @@ -2084,15 +2084,6 @@ static int convert_do_copy(ImgConvertState *s) s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target)); } - if (!s->has_zero_init && !s->target_has_backing && - bdrv_can_write_zeroes_with_unmap(blk_bs(s->target))) - { - ret = blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK); - if (ret == 0) { - s->has_zero_init = true; - } - } - /* Allocate buffer for copied data. For compressed images, only one cluster * can be copied at a time. */ if (s->compressed) { @@ -2686,7 +2677,6 @@ static int img_convert(int argc, char **argv) } else { s.compressed = s.compressed || bdi.needs_compressed_writes; s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; - s.unallocated_blocks_are_zero = bdi.unallocated_blocks_are_zero; } ret = convert_do_copy(&s); @@ -4076,12 +4066,11 @@ static int print_amend_option_help(const char *format) return 1; } - /* Every driver supporting amendment must have create_opts */ - assert(drv->create_opts); + /* Every driver supporting amendment must have amend_opts */ + assert(drv->amend_opts); - printf("Creation options for '%s':\n", format); - qemu_opts_print_help(drv->create_opts, false); - printf("\nNote that not all of these options may be amendable.\n"); + printf("Amend options for '%s':\n", format); + qemu_opts_print_help(drv->amend_opts, false); return 0; } @@ -4090,7 +4079,7 @@ static int img_amend(int argc, char **argv) Error *err = NULL; int c, ret = 0; char *options = NULL; - QemuOptsList *create_opts = NULL; + QemuOptsList *amend_opts = NULL; QemuOpts *opts = NULL; const char *fmt = NULL, *filename, *cache; int flags; @@ -4099,6 +4088,7 @@ static int img_amend(int argc, char **argv) BlockBackend *blk = NULL; BlockDriverState *bs = NULL; bool image_opts = false; + bool force = false; cache = BDRV_DEFAULT_CACHE; for (;;) { @@ -4106,6 +4096,7 @@ static int img_amend(int argc, char **argv) {"help", no_argument, 0, 'h'}, {"object", required_argument, 0, OPTION_OBJECT}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"force", no_argument, 0, OPTION_FORCE}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, ":ho:f:t:pq", @@ -4153,6 +4144,9 @@ static int img_amend(int argc, char **argv) case OPTION_IMAGE_OPTS: image_opts = true; break; + case OPTION_FORCE: + force = true; + break; } } @@ -4216,13 +4210,28 @@ static int img_amend(int argc, char **argv) goto out; } - /* Every driver supporting amendment must have create_opts */ - assert(bs->drv->create_opts); + /* Every driver supporting amendment must have amend_opts */ + assert(bs->drv->amend_opts); - create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); - opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); + amend_opts = qemu_opts_append(amend_opts, bs->drv->amend_opts); + opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort); qemu_opts_do_parse(opts, options, NULL, &err); + if (err) { + /* Try to parse options using the create options */ + Error *err1 = NULL; + amend_opts = qemu_opts_append(amend_opts, bs->drv->create_opts); + qemu_opts_del(opts); + opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort); + qemu_opts_do_parse(opts, options, NULL, &err1); + + if (!err1) { + error_append_hint(&err, + "This option is only supported for image creation\n"); + } else { + error_free(err1); + } + error_report_err(err); ret = -1; goto out; @@ -4230,7 +4239,7 @@ static int img_amend(int argc, char **argv) /* In case the driver does not call amend_status_cb() */ qemu_progress_print(0.f, 0); - ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, &err); + ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, force, &err); qemu_progress_print(100.f, 0); if (ret < 0) { error_report_err(err); @@ -4243,7 +4252,7 @@ out: out_no_progress: blk_unref(blk); qemu_opts_del(opts); - qemu_opts_free(create_opts); + qemu_opts_free(amend_opts); g_free(options); if (ret) { diff --git a/qemu-options.hx b/qemu-options.hx index b2cbbbf281..c6edb4047b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2419,6 +2419,10 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev vhost-user,id=str,chardev=dev[,vhostforce=on|off]\n" " configure a vhost-user network, backed by a chardev 'dev'\n" #endif +#ifdef __linux__ + "-netdev vhost-vdpa,id=str,vhostdev=/path/to/dev\n" + " configure a vhost-vdpa network,Establish a vhost-vdpa netdev\n" +#endif "-netdev hubport,id=str,hubid=n[,netdev=nd]\n" " configure a hub port on the hub with ID 'n'\n", QEMU_ARCH_ALL) DEF("nic", HAS_ARG, QEMU_OPTION_nic, @@ -2897,6 +2901,14 @@ SRST -netdev type=vhost-user,id=net0,chardev=chr0 \ -device virtio-net-pci,netdev=net0 +``-netdev vhost-vdpa,vhostdev=/path/to/dev`` + Establish a vhost-vdpa netdev. + + vDPA device is a device that uses a datapath which complies with + the virtio specifications with a vendor specific control path. + vDPA devices can be both physically located on the hardware or + emulated by software. + ``-netdev hubport,id=id,hubid=hubid[,netdev=nd]`` Create a hub port on the emulated hub with ID hubid. diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c index 9e7adfe3a6..7e9b0e0d3f 100644 --- a/qemu-storage-daemon.c +++ b/qemu-storage-daemon.c @@ -316,6 +316,7 @@ int main(int argc, char *argv[]) module_call_init(MODULE_INIT_QOM); module_call_init(MODULE_INIT_TRACE); + qemu_add_opts(&qemu_object_opts); qemu_add_opts(&qemu_trace_opts); qcrypto_init(&error_fatal); bdrv_init(); @@ -334,5 +335,9 @@ int main(int argc, char *argv[]) main_loop_wait(false); } + monitor_cleanup(); + qemu_chr_cleanup(); + user_creatable_cleanup(); + return EXIT_SUCCESS; } diff --git a/qom/object.c b/qom/object.c index 6ece96bc2b..34daaf1280 100644 --- a/qom/object.c +++ b/qom/object.c @@ -985,6 +985,20 @@ ObjectClass *object_class_by_name(const char *typename) return type->class; } +ObjectClass *module_object_class_by_name(const char *typename) +{ + ObjectClass *oc; + + oc = object_class_by_name(typename); +#ifdef CONFIG_MODULES + if (!oc) { + module_load_qom_one(typename); + oc = object_class_by_name(typename); + } +#endif + return oc; +} + ObjectClass *object_class_get_parent(ObjectClass *class) { TypeImpl *type = type_get_parent(class->type); diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index c5249e44d0..5e2c8cbf33 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -116,6 +116,7 @@ ObjectTypeInfoList *qmp_qom_list_types(bool has_implements, { ObjectTypeInfoList *ret = NULL; + module_load_qom_all(); object_class_foreach(qom_list_types_tramp, implements, abstract, &ret); return ret; @@ -130,7 +131,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, ObjectPropertyIterator iter; ObjectPropertyInfoList *prop_list = NULL; - klass = object_class_by_name(typename); + klass = module_object_class_by_name(typename); if (klass == NULL) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", typename); diff --git a/softmmu/vl.c b/softmmu/vl.c index 159f0352a9..f3ff5d06ca 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -1777,8 +1777,8 @@ static bool vga_interface_available(VGAInterfaceType t) assert(t < VGA_TYPE_MAX); return !ti->class_names[0] || - object_class_by_name(ti->class_names[0]) || - object_class_by_name(ti->class_names[1]); + module_object_class_by_name(ti->class_names[0]) || + module_object_class_by_name(ti->class_names[1]); } static const char * @@ -3851,17 +3851,7 @@ void qemu_init(int argc, char **argv, char **envp) machine_class); os_daemonize(); - - /* - * If QTest is enabled, keep the rcu_atfork enabled, since system processes - * may be forked testing purposes (e.g. fork-server based fuzzing) The fork - * should happen before a signle cpu instruction is executed, to prevent - * deadlocks. See commit 73c6e40, rcu: "completely disable pthread_atfork - * callbacks as soon as possible" - */ - if (!qtest_enabled()) { - rcu_disable_atfork(); - } + rcu_disable_atfork(); if (pid_file && !qemu_write_pidfile(pid_file, &err)) { error_reportf_err(err, "cannot create PID file: "); @@ -4150,12 +4140,17 @@ void qemu_init(int argc, char **argv, char **envp) fsdev_init_func, NULL, &error_fatal); #endif + /* spice needs the timers to be initialized by this point */ + /* spice must initialize before audio as it changes the default auiodev */ + qemu_spice_init(); + /* - * Note: we need to create block backends before + * Note: we need to create audio and block backends before * machine_set_property(), so machine properties can refer to * them. */ configure_blockdev(&bdo_queue, machine_class, snapshot); + audio_init_audiodevs(); machine_opts = qemu_get_machine_opts(); qemu_opt_foreach(machine_opts, machine_set_property, current_machine, @@ -4249,9 +4244,6 @@ void qemu_init(int argc, char **argv, char **envp) semihosting_arg_fallback(kernel_filename, kernel_cmdline); } - /* spice needs the timers to be initialized by this point */ - qemu_spice_init(); - cpu_ticks_init(); if (default_net) { @@ -4361,8 +4353,6 @@ void qemu_init(int argc, char **argv, char **envp) create_default_memdev(current_machine, mem_path); } - audio_init_audiodevs(); - /* from here on runstate is RUN_STATE_PRELAUNCH */ machine_run_board_init(current_machine); diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index f32b9e47a3..d42046afe4 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -8,10 +8,12 @@ stub-obj-y += fdset.o stub-obj-y += gdbstub.o stub-obj-y += iothread-lock.o stub-obj-y += is-daemonized.o +stub-obj-y += isa-bus.o stub-obj-$(CONFIG_LINUX_AIO) += linux-aio.o stub-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o stub-obj-y += monitor-core.o stub-obj-y += notify-event.o +stub-obj-y += pci-bus.o stub-obj-y += qmp_memory_device.o stub-obj-y += qtest.o stub-obj-y += ramfb.o @@ -19,10 +21,10 @@ stub-obj-y += replay.o stub-obj-y += runstate-check.o stub-obj-$(CONFIG_SOFTMMU) += semihost.o stub-obj-y += set-fd-handler.o -stub-obj-y += vmgenid.o stub-obj-y += sysbus.o stub-obj-y += tpm.o stub-obj-y += trace-control.o +stub-obj-y += vmgenid.o stub-obj-y += vmstate.o stub-obj-$(CONFIG_SOFTMMU) += win32-kbd-hook.o diff --git a/stubs/isa-bus.c b/stubs/isa-bus.c new file mode 100644 index 0000000000..522f448997 --- /dev/null +++ b/stubs/isa-bus.c @@ -0,0 +1,7 @@ +#include "qemu/osdep.h" +#include "hw/isa/isa.h" + +ISADevice *isa_create_simple(ISABus *bus, const char *name) +{ + g_assert_not_reached(); +} diff --git a/stubs/pci-bus.c b/stubs/pci-bus.c new file mode 100644 index 0000000000..a8932fa932 --- /dev/null +++ b/stubs/pci-bus.c @@ -0,0 +1,7 @@ +#include "qemu/osdep.h" +#include "hw/pci/pci.h" + +PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) +{ + g_assert_not_reached(); +} diff --git a/target/arm/cpu.h b/target/arm/cpu.h index cf99dcca9f..9e8ed423ea 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -573,6 +573,8 @@ typedef struct CPUARMState { uint64_t esr; } serror; + uint8_t ext_dabt_raised; /* Tracking/verifying injection of ext DABT */ + /* State of our input IRQ/FIQ/VIRQ/VFIQ lines */ uint32_t irq_line_state; diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 7c672c78b8..8bb7318378 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -39,6 +39,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { static bool cap_has_mp_state; static bool cap_has_inject_serror_esr; +static bool cap_has_inject_ext_dabt; static ARMHostCPUFeatures arm_host_cpu_features; @@ -245,6 +246,16 @@ int kvm_arch_init(MachineState *ms, KVMState *s) ret = -EINVAL; } + if (kvm_check_extension(s, KVM_CAP_ARM_NISV_TO_USER)) { + if (kvm_vm_enable_cap(s, KVM_CAP_ARM_NISV_TO_USER, 0)) { + error_report("Failed to enable KVM_CAP_ARM_NISV_TO_USER cap"); + } else { + /* Set status for supporting the external dabt injection */ + cap_has_inject_ext_dabt = kvm_check_extension(s, + KVM_CAP_ARM_INJECT_EXT_DABT); + } + } + return ret; } @@ -738,6 +749,29 @@ int kvm_get_vcpu_events(ARMCPU *cpu) void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + if (unlikely(env->ext_dabt_raised)) { + /* + * Verifying that the ext DABT has been properly injected, + * otherwise risking indefinitely re-running the faulting instruction + * Covering a very narrow case for kernels 5.5..5.5.4 + * when injected abort was misconfigured to be + * an IMPLEMENTATION DEFINED exception (for 32-bit EL1) + */ + if (!arm_feature(env, ARM_FEATURE_AARCH64) && + unlikely(!kvm_arm_verify_ext_dabt_pending(cs))) { + + error_report("Data abort exception with no valid ISS generated by " + "guest memory access. KVM unable to emulate faulting " + "instruction. Failed to inject an external data abort " + "into the guest."); + abort(); + } + /* Clear the status */ + env->ext_dabt_raised = 0; + } } MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) @@ -810,6 +844,47 @@ void kvm_arm_vm_state_change(void *opaque, int running, RunState state) } } +/** + * kvm_arm_handle_dabt_nisv: + * @cs: CPUState + * @esr_iss: ISS encoding (limited) for the exception from Data Abort + * ISV bit set to '0b0' -> no valid instruction syndrome + * @fault_ipa: faulting address for the synchronous data abort + * + * Returns: 0 if the exception has been handled, < 0 otherwise + */ +static int kvm_arm_handle_dabt_nisv(CPUState *cs, uint64_t esr_iss, + uint64_t fault_ipa) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + /* + * Request KVM to inject the external data abort into the guest + */ + if (cap_has_inject_ext_dabt) { + struct kvm_vcpu_events events = { }; + /* + * The external data abort event will be handled immediately by KVM + * using the address fault that triggered the exit on given VCPU. + * Requesting injection of the external data abort does not rely + * on any other VCPU state. Therefore, in this particular case, the VCPU + * synchronization can be exceptionally skipped. + */ + events.exception.ext_dabt_pending = 1; + /* KVM_CAP_ARM_INJECT_EXT_DABT implies KVM_CAP_VCPU_EVENTS */ + if (!kvm_vcpu_ioctl(cs, KVM_SET_VCPU_EVENTS, &events)) { + env->ext_dabt_raised = 1; + return 0; + } + } else { + error_report("Data abort exception triggered by guest memory access " + "at physical address: 0x" TARGET_FMT_lx, + (target_ulong)fault_ipa); + error_printf("KVM unable to emulate faulting instruction.\n"); + } + return -1; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { int ret = 0; @@ -820,6 +895,11 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = EXCP_DEBUG; } /* otherwise return to guest */ break; + case KVM_EXIT_ARM_NISV: + /* External DABT with no valid iss to decode */ + ret = kvm_arm_handle_dabt_nisv(cs, run->arm_nisv.esr_iss, + run->arm_nisv.fault_ipa); + break; default: qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", __func__, run->exit_reason); diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index 7b3a19e9ae..0af46b41c8 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -559,3 +559,37 @@ void kvm_arm_pmu_init(CPUState *cs) { qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); } + +#define ARM_REG_DFSR ARM_CP15_REG32(0, 5, 0, 0) +#define ARM_REG_TTBCR ARM_CP15_REG32(0, 2, 0, 2) +/* + *DFSR: + * TTBCR.EAE == 0 + * FS[4] - DFSR[10] + * FS[3:0] - DFSR[3:0] + * TTBCR.EAE == 1 + * FS, bits [5:0] + */ +#define DFSR_FSC(lpae, v) \ + ((lpae) ? ((v) & 0x3F) : (((v) >> 6) | ((v) & 0x1F))) + +#define DFSC_EXTABT(lpae) ((lpae) ? 0x10 : 0x08) + +bool kvm_arm_verify_ext_dabt_pending(CPUState *cs) +{ + uint32_t dfsr_val; + + if (!kvm_get_one_reg(cs, ARM_REG_DFSR, &dfsr_val)) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint32_t ttbcr; + int lpae = 0; + + if (!kvm_get_one_reg(cs, ARM_REG_TTBCR, &ttbcr)) { + lpae = arm_feature(env, ARM_FEATURE_LPAE) && (ttbcr & TTBCR_EAE); + } + /* The verification is based on FS filed of the DFSR reg only*/ + return (DFSR_FSC(lpae, dfsr_val) == DFSC_EXTABT(lpae)); + } + return false; +} diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 3dc494aaa7..1169237905 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -1493,3 +1493,52 @@ bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit) return false; } + +#define ARM64_REG_ESR_EL1 ARM64_SYS_REG(3, 0, 5, 2, 0) +#define ARM64_REG_TCR_EL1 ARM64_SYS_REG(3, 0, 2, 0, 2) + +/* + * ESR_EL1 + * ISS encoding + * AARCH64: DFSC, bits [5:0] + * AARCH32: + * TTBCR.EAE == 0 + * FS[4] - DFSR[10] + * FS[3:0] - DFSR[3:0] + * TTBCR.EAE == 1 + * FS, bits [5:0] + */ +#define ESR_DFSC(aarch64, lpae, v) \ + ((aarch64 || (lpae)) ? ((v) & 0x3F) \ + : (((v) >> 6) | ((v) & 0x1F))) + +#define ESR_DFSC_EXTABT(aarch64, lpae) \ + ((aarch64) ? 0x10 : (lpae) ? 0x10 : 0x8) + +bool kvm_arm_verify_ext_dabt_pending(CPUState *cs) +{ + uint64_t dfsr_val; + + if (!kvm_get_one_reg(cs, ARM64_REG_ESR_EL1, &dfsr_val)) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + int aarch64_mode = arm_feature(env, ARM_FEATURE_AARCH64); + int lpae = 0; + + if (!aarch64_mode) { + uint64_t ttbcr; + + if (!kvm_get_one_reg(cs, ARM64_REG_TCR_EL1, &ttbcr)) { + lpae = arm_feature(env, ARM_FEATURE_LPAE) + && (ttbcr & TTBCR_EAE); + } + } + /* + * The verification here is based on the DFSC bits + * of the ESR_EL1 reg only + */ + return (ESR_DFSC(aarch64_mode, lpae, dfsr_val) == + ESR_DFSC_EXTABT(aarch64_mode, lpae)); + } + return false; +} diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index a4ce4fd93d..adb38514bf 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -450,6 +450,16 @@ struct kvm_guest_debug_arch; void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr); /** + * kvm_arm_verify_ext_dabt_pending: + * @cs: CPUState + * + * Verify the fault status code wrt the Ext DABT injection + * + * Returns: true if the fault status code is as expected, false otherwise + */ +bool kvm_arm_verify_ext_dabt_pending(CPUState *cs); + +/** * its_class_name: * * Return the ITS class name to use depending on whether KVM acceleration diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 73d753f11f..8c0764957c 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -461,6 +461,12 @@ TCGv_i64 new_tmp_a64(DisasContext *s) return s->tmp_a64[s->tmp_a64_count++] = tcg_temp_new_i64(); } +TCGv_i64 new_tmp_a64_local(DisasContext *s) +{ + assert(s->tmp_a64_count < TMP_A64_MAX); + return s->tmp_a64[s->tmp_a64_count++] = tcg_temp_local_new_i64(); +} + TCGv_i64 new_tmp_a64_zero(DisasContext *s) { TCGv_i64 t = new_tmp_a64(s); diff --git a/target/arm/translate-a64.h b/target/arm/translate-a64.h index 49e4865918..647f0c74f6 100644 --- a/target/arm/translate-a64.h +++ b/target/arm/translate-a64.h @@ -30,6 +30,7 @@ void unallocated_encoding(DisasContext *s); } while (0) TCGv_i64 new_tmp_a64(DisasContext *s); +TCGv_i64 new_tmp_a64_local(DisasContext *s); TCGv_i64 new_tmp_a64_zero(DisasContext *s); TCGv_i64 cpu_reg(DisasContext *s, int reg); TCGv_i64 cpu_reg_sp(DisasContext *s, int reg); diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index f318ca265f..08f0fd15b2 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -4372,9 +4372,8 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) /* Copy the clean address into a local temp, live across the loop. */ t0 = clean_addr; - clean_addr = tcg_temp_local_new_i64(); + clean_addr = new_tmp_a64_local(s); tcg_gen_mov_i64(clean_addr, t0); - tcg_temp_free_i64(t0); gen_set_label(loop); @@ -4422,7 +4421,6 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) tcg_gen_st_i64(t0, cpu_env, vofs + len_align); tcg_temp_free_i64(t0); } - tcg_temp_free_i64(clean_addr); } /* Similarly for stores. */ @@ -4463,9 +4461,8 @@ static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) /* Copy the clean address into a local temp, live across the loop. */ t0 = clean_addr; - clean_addr = tcg_temp_local_new_i64(); + clean_addr = new_tmp_a64_local(s); tcg_gen_mov_i64(clean_addr, t0); - tcg_temp_free_i64(t0); gen_set_label(loop); @@ -4509,7 +4506,6 @@ static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) } tcg_temp_free_i64(t0); } - tcg_temp_free_i64(clean_addr); } static bool trans_LDR_zri(DisasContext *s, arg_rri *a) diff --git a/target/i386/sev.c b/target/i386/sev.c index d273174ad3..f100a53231 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -680,6 +680,12 @@ sev_guest_init(const char *id) uint32_t host_cbitpos; struct sev_user_data_status status = {}; + ret = ram_block_discard_disable(true); + if (ret) { + error_report("%s: cannot disable RAM discard", __func__); + return NULL; + } + sev = lookup_sev_guest_info(id); if (!sev) { error_report("%s: '%s' is not a valid '%s' object", @@ -751,6 +757,7 @@ sev_guest_init(const char *id) return sev; err: sev_guest = NULL; + ram_block_discard_disable(false); return NULL; } diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 79b0b10ea9..3ff5765795 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -643,7 +643,7 @@ static int get_physical_address(CPUM68KState *env, hwaddr *physical, /* Transparent Translation Register bit */ env->mmu.mmusr = M68K_MMU_T_040 | M68K_MMU_R_040; } - *physical = address & TARGET_PAGE_MASK; + *physical = address; *page_size = TARGET_PAGE_SIZE; return 0; } @@ -771,7 +771,7 @@ static int get_physical_address(CPUM68KState *env, hwaddr *physical, } *page_size = 1 << page_bits; page_mask = ~(*page_size - 1); - *physical = next & page_mask; + *physical = (next & page_mask) + (address & (*page_size - 1)); if (access_type & ACCESS_PTEST) { env->mmu.mmusr |= next & M68K_MMU_SR_MASK_040; @@ -820,10 +820,12 @@ hwaddr m68k_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) if (env->sr & SR_S) { access_type |= ACCESS_SUPER; } + if (get_physical_address(env, &phys_addr, &prot, addr, access_type, &page_size) != 0) { return -1; } + return phys_addr; } @@ -887,10 +889,8 @@ bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size, ret = get_physical_address(&cpu->env, &physical, &prot, address, access_type, &page_size); if (likely(ret == 0)) { - address &= TARGET_PAGE_MASK; - physical += address & (page_size - 1); - tlb_set_page(cs, address, physical, - prot, mmu_idx, TARGET_PAGE_SIZE); + tlb_set_page(cs, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot, mmu_idx, page_size); return true; } @@ -1379,9 +1379,8 @@ void HELPER(ptest)(CPUM68KState *env, uint32_t addr, uint32_t is_read) ret = get_physical_address(env, &physical, &prot, addr, access_type, &page_size); if (ret == 0) { - addr &= TARGET_PAGE_MASK; - physical += addr & (page_size - 1); - tlb_set_page(env_cpu(env), addr, physical, + tlb_set_page(env_cpu(env), addr & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot, access_type & ACCESS_SUPER ? MMU_KERNEL_IDX : MMU_USER_IDX, page_size); } diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs index ff651f69f6..ff38df6219 100644 --- a/target/riscv/Makefile.objs +++ b/target/riscv/Makefile.objs @@ -1,4 +1,4 @@ -obj-y += translate.o op_helper.o cpu_helper.o cpu.o csr.o fpu_helper.o gdbstub.o +obj-y += translate.o op_helper.o cpu_helper.o cpu.o csr.o fpu_helper.o vector_helper.o gdbstub.o obj-$(CONFIG_SOFTMMU) += pmp.o ifeq ($(CONFIG_SOFTMMU),y) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 391a0b9eec..228b9bdb5d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -106,6 +106,11 @@ static void set_priv_version(CPURISCVState *env, int priv_ver) env->priv_ver = priv_ver; } +static void set_vext_version(CPURISCVState *env, int vext_ver) +{ + env->vext_ver = vext_ver; +} + static void set_feature(CPURISCVState *env, int feature) { env->features |= (1ULL << feature); @@ -334,6 +339,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); int priv_version = PRIV_VERSION_1_11_0; + int vext_version = VEXT_VERSION_0_07_1; target_ulong target_misa = 0; Error *local_err = NULL; @@ -357,6 +363,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } set_priv_version(env, priv_version); + set_vext_version(env, vext_version); if (cpu->cfg.mmu) { set_feature(env, RISCV_FEATURE_MMU); @@ -423,6 +430,45 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) if (cpu->cfg.ext_h) { target_misa |= RVH; } + if (cpu->cfg.ext_v) { + target_misa |= RVV; + if (!is_power_of_2(cpu->cfg.vlen)) { + error_setg(errp, + "Vector extension VLEN must be power of 2"); + return; + } + if (cpu->cfg.vlen > RV_VLEN_MAX || cpu->cfg.vlen < 128) { + error_setg(errp, + "Vector extension implementation only supports VLEN " + "in the range [128, %d]", RV_VLEN_MAX); + return; + } + if (!is_power_of_2(cpu->cfg.elen)) { + error_setg(errp, + "Vector extension ELEN must be power of 2"); + return; + } + if (cpu->cfg.elen > 64 || cpu->cfg.vlen < 8) { + error_setg(errp, + "Vector extension implementation only supports ELEN " + "in the range [8, 64]"); + return; + } + if (cpu->cfg.vext_spec) { + if (!g_strcmp0(cpu->cfg.vext_spec, "v0.7.1")) { + vext_version = VEXT_VERSION_0_07_1; + } else { + error_setg(errp, + "Unsupported vector spec version '%s'", + cpu->cfg.vext_spec); + return; + } + } else { + qemu_log("vector verison is not specified, " + "use the default value v0.7.1\n"); + } + set_vext_version(env, vext_version); + } set_misa(env, RVXLEN | target_misa); } @@ -462,10 +508,14 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), /* This is experimental so mark with 'x-' */ DEFINE_PROP_BOOL("x-h", RISCVCPU, cfg.ext_h, false), + DEFINE_PROP_BOOL("x-v", RISCVCPU, cfg.ext_v, false), DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), + DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec), + DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), + DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), DEFINE_PROP_END_OF_LIST(), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 80569f0d44..eef20ca6e5 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -21,6 +21,7 @@ #define RISCV_CPU_H #include "hw/core/cpu.h" +#include "hw/registerfields.h" #include "exec/cpu-defs.h" #include "fpu/softfloat-types.h" @@ -59,6 +60,7 @@ #define RVA RV('A') #define RVF RV('F') #define RVD RV('D') +#define RVV RV('V') #define RVC RV('C') #define RVS RV('S') #define RVU RV('U') @@ -77,6 +79,8 @@ enum { #define PRIV_VERSION_1_10_0 0x00011000 #define PRIV_VERSION_1_11_0 0x00011100 +#define VEXT_VERSION_0_07_1 0x00000701 + #define TRANSLATE_PMP_FAIL 2 #define TRANSLATE_FAIL 1 #define TRANSLATE_SUCCESS 0 @@ -88,9 +92,26 @@ typedef struct CPURISCVState CPURISCVState; #include "pmp.h" +#define RV_VLEN_MAX 256 + +FIELD(VTYPE, VLMUL, 0, 2) +FIELD(VTYPE, VSEW, 2, 3) +FIELD(VTYPE, VEDIV, 5, 2) +FIELD(VTYPE, RESERVED, 7, sizeof(target_ulong) * 8 - 9) +FIELD(VTYPE, VILL, sizeof(target_ulong) * 8 - 2, 1) + struct CPURISCVState { target_ulong gpr[32]; uint64_t fpr[32]; /* assume both F and D extensions */ + + /* vector coprocessor state. */ + uint64_t vreg[32 * RV_VLEN_MAX / 64] QEMU_ALIGNED(16); + target_ulong vxrm; + target_ulong vxsat; + target_ulong vl; + target_ulong vstart; + target_ulong vtype; + target_ulong pc; target_ulong load_res; target_ulong load_val; @@ -101,6 +122,7 @@ struct CPURISCVState { target_ulong guest_phys_fault_addr; target_ulong priv_ver; + target_ulong vext_ver; target_ulong misa; target_ulong misa_mask; @@ -257,12 +279,16 @@ typedef struct RISCVCPU { bool ext_s; bool ext_u; bool ext_h; + bool ext_v; bool ext_counters; bool ext_ifencei; bool ext_icsr; char *priv_spec; char *user_spec; + char *vext_spec; + uint16_t vlen; + uint16_t elen; bool mmu; bool pmp; } cfg; @@ -335,19 +361,62 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); #define TB_FLAGS_MMU_MASK 3 #define TB_FLAGS_MSTATUS_FS MSTATUS_FS +typedef CPURISCVState CPUArchState; +typedef RISCVCPU ArchCPU; +#include "exec/cpu-all.h" + +FIELD(TB_FLAGS, VL_EQ_VLMAX, 2, 1) +FIELD(TB_FLAGS, LMUL, 3, 2) +FIELD(TB_FLAGS, SEW, 5, 3) +FIELD(TB_FLAGS, VILL, 8, 1) + +/* + * A simplification for VLMAX + * = (1 << LMUL) * VLEN / (8 * (1 << SEW)) + * = (VLEN << LMUL) / (8 << SEW) + * = (VLEN << LMUL) >> (SEW + 3) + * = VLEN >> (SEW + 3 - LMUL) + */ +static inline uint32_t vext_get_vlmax(RISCVCPU *cpu, target_ulong vtype) +{ + uint8_t sew, lmul; + + sew = FIELD_EX64(vtype, VTYPE, VSEW); + lmul = FIELD_EX64(vtype, VTYPE, VLMUL); + return cpu->cfg.vlen >> (sew + 3 - lmul); +} + static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) + target_ulong *cs_base, uint32_t *pflags) { + uint32_t flags = 0; + *pc = env->pc; *cs_base = 0; + + if (riscv_has_ext(env, RVV)) { + uint32_t vlmax = vext_get_vlmax(env_archcpu(env), env->vtype); + bool vl_eq_vlmax = (env->vstart == 0) && (vlmax == env->vl); + flags = FIELD_DP32(flags, TB_FLAGS, VILL, + FIELD_EX64(env->vtype, VTYPE, VILL)); + flags = FIELD_DP32(flags, TB_FLAGS, SEW, + FIELD_EX64(env->vtype, VTYPE, VSEW)); + flags = FIELD_DP32(flags, TB_FLAGS, LMUL, + FIELD_EX64(env->vtype, VTYPE, VLMUL)); + flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); + } else { + flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); + } + #ifdef CONFIG_USER_ONLY - *flags = TB_FLAGS_MSTATUS_FS; + flags |= TB_FLAGS_MSTATUS_FS; #else - *flags = cpu_mmu_index(env, 0); + flags |= cpu_mmu_index(env, 0); if (riscv_cpu_fp_enabled(env)) { - *flags |= env->mstatus & MSTATUS_FS; + flags |= env->mstatus & MSTATUS_FS; } #endif + *pflags = flags; } int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, @@ -388,9 +457,4 @@ void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops); void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); -typedef CPURISCVState CPUArchState; -typedef RISCVCPU ArchCPU; - -#include "exec/cpu-all.h" - #endif /* RISCV_CPU_H */ diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 7f64ee1174..8117e8b5a7 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -29,6 +29,14 @@ #define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) #define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) +/* Vector Fixed-Point round model */ +#define FSR_VXRM_SHIFT 9 +#define FSR_VXRM (0x3 << FSR_VXRM_SHIFT) + +/* Vector Fixed-Point saturation flag */ +#define FSR_VXSAT_SHIFT 8 +#define FSR_VXSAT (0x1 << FSR_VXSAT_SHIFT) + /* Control and Status Registers */ /* User Trap Setup */ @@ -48,6 +56,13 @@ #define CSR_FRM 0x002 #define CSR_FCSR 0x003 +/* User Vector CSRs */ +#define CSR_VSTART 0x008 +#define CSR_VXSAT 0x009 +#define CSR_VXRM 0x00a +#define CSR_VL 0xc20 +#define CSR_VTYPE 0xc21 + /* User Timers and Counters */ #define CSR_CYCLE 0xc00 #define CSR_TIME 0xc01 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 383be0a955..ac01c835e1 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -46,6 +46,10 @@ void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) static int fs(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) + /* loose check condition for fcsr in vector extension */ + if ((csrno == CSR_FCSR) && (env->misa & RVV)) { + return 0; + } if (!env->debugger && !riscv_cpu_fp_enabled(env)) { return -1; } @@ -53,6 +57,14 @@ static int fs(CPURISCVState *env, int csrno) return 0; } +static int vs(CPURISCVState *env, int csrno) +{ + if (env->misa & RVV) { + return 0; + } + return -1; +} + static int ctr(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) @@ -154,6 +166,10 @@ static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val) #endif *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT) | (env->frm << FSR_RD_SHIFT); + if (vs(env, csrno) >= 0) { + *val |= (env->vxrm << FSR_VXRM_SHIFT) + | (env->vxsat << FSR_VXSAT_SHIFT); + } return 0; } @@ -166,10 +182,62 @@ static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val) env->mstatus |= MSTATUS_FS; #endif env->frm = (val & FSR_RD) >> FSR_RD_SHIFT; + if (vs(env, csrno) >= 0) { + env->vxrm = (val & FSR_VXRM) >> FSR_VXRM_SHIFT; + env->vxsat = (val & FSR_VXSAT) >> FSR_VXSAT_SHIFT; + } riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); return 0; } +static int read_vtype(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vtype; + return 0; +} + +static int read_vl(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vl; + return 0; +} + +static int read_vxrm(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vxrm; + return 0; +} + +static int write_vxrm(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vxrm = val; + return 0; +} + +static int read_vxsat(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vxsat; + return 0; +} + +static int write_vxsat(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vxsat = val; + return 0; +} + +static int read_vstart(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vstart; + return 0; +} + +static int write_vstart(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vstart = val; + return 0; +} + /* User Timers and Counters */ static int read_instret(CPURISCVState *env, int csrno, target_ulong *val) { @@ -1183,7 +1251,12 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_FFLAGS] = { fs, read_fflags, write_fflags }, [CSR_FRM] = { fs, read_frm, write_frm }, [CSR_FCSR] = { fs, read_fcsr, write_fcsr }, - + /* Vector CSRs */ + [CSR_VSTART] = { vs, read_vstart, write_vstart }, + [CSR_VXSAT] = { vs, read_vxsat, write_vxsat }, + [CSR_VXRM] = { vs, read_vxrm, write_vxrm }, + [CSR_VL] = { vs, read_vl }, + [CSR_VTYPE] = { vs, read_vtype }, /* User Timers and Counters */ [CSR_CYCLE] = { ctr, read_instret }, [CSR_INSTRET] = { ctr, read_instret }, diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 0b79562a69..4379756dc4 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -22,6 +22,7 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" +#include "internals.h" target_ulong riscv_cpu_get_fflags(CPURISCVState *env) { @@ -230,21 +231,7 @@ uint64_t helper_fcvt_s_lu(CPURISCVState *env, uint64_t rs1) target_ulong helper_fclass_s(uint64_t frs1) { - float32 f = frs1; - bool sign = float32_is_neg(f); - - if (float32_is_infinity(f)) { - return sign ? 1 << 0 : 1 << 7; - } else if (float32_is_zero(f)) { - return sign ? 1 << 3 : 1 << 4; - } else if (float32_is_zero_or_denormal(f)) { - return sign ? 1 << 2 : 1 << 5; - } else if (float32_is_any_nan(f)) { - float_status s = { }; /* for snan_bit_is_one */ - return float32_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; - } else { - return sign ? 1 << 1 : 1 << 6; - } + return fclass_s(frs1); } uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) @@ -353,19 +340,5 @@ uint64_t helper_fcvt_d_lu(CPURISCVState *env, uint64_t rs1) target_ulong helper_fclass_d(uint64_t frs1) { - float64 f = frs1; - bool sign = float64_is_neg(f); - - if (float64_is_infinity(f)) { - return sign ? 1 << 0 : 1 << 7; - } else if (float64_is_zero(f)) { - return sign ? 1 << 3 : 1 << 4; - } else if (float64_is_zero_or_denormal(f)) { - return sign ? 1 << 2 : 1 << 5; - } else if (float64_is_any_nan(f)) { - float_status s = { }; /* for snan_bit_is_one */ - return float64_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; - } else { - return sign ? 1 << 1 : 1 << 6; - } + return fclass_d(frs1); } diff --git a/target/riscv/helper.h b/target/riscv/helper.h index b36be978d5..acc298219d 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -81,3 +81,1072 @@ DEF_HELPER_1(tlb_flush, void, env) #ifndef CONFIG_USER_ONLY DEF_HELPER_1(hyp_tlb_flush, void, env) #endif + +/* Vector functions */ +DEF_HELPER_3(vsetvl, tl, env, tl, tl) +DEF_HELPER_5(vlb_v_b, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlb_v_b_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlb_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlb_v_h_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlb_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlb_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlb_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlb_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlh_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlh_v_h_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlh_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlh_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlh_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlh_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlw_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlw_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlw_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlw_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vle_v_b, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vle_v_b_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vle_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vle_v_h_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vle_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vle_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vle_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vle_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbu_v_b, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbu_v_b_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbu_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbu_v_h_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbu_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbu_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbu_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbu_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhu_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhu_v_h_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhu_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhu_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhu_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhu_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlwu_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlwu_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlwu_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlwu_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsb_v_b, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsb_v_b_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsb_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsb_v_h_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsb_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsb_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsb_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsb_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsh_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsh_v_h_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsh_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsh_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsh_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsh_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsw_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsw_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsw_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vsw_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vse_v_b, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vse_v_b_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vse_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vse_v_h_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vse_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vse_v_w_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vse_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vse_v_d_mask, void, ptr, ptr, tl, env, i32) +DEF_HELPER_6(vlsb_v_b, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsb_v_h, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsb_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsb_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsh_v_h, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsh_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsh_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsw_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsw_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlse_v_b, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlse_v_h, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlse_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlse_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsbu_v_b, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsbu_v_h, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsbu_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlsbu_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlshu_v_h, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlshu_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlshu_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlswu_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlswu_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssb_v_b, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssb_v_h, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssb_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssb_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssh_v_h, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssh_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssh_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssw_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vssw_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vsse_v_b, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vsse_v_h, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vsse_v_w, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vsse_v_d, void, ptr, ptr, tl, tl, env, i32) +DEF_HELPER_6(vlxb_v_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxb_v_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxb_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxb_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxh_v_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxh_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxh_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxe_v_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxe_v_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxe_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxe_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxbu_v_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxbu_v_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxbu_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxbu_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxhu_v_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxhu_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxhu_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxwu_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vlxwu_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxb_v_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxb_v_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxb_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxb_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxh_v_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxh_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxh_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxe_v_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxe_v_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxe_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsxe_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_5(vlbff_v_b, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbff_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbff_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbff_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhff_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhff_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhff_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlwff_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlwff_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vleff_v_b, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vleff_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vleff_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vleff_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbuff_v_b, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbuff_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbuff_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlbuff_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhuff_v_h, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhuff_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlhuff_v_d, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlwuff_v_w, void, ptr, ptr, tl, env, i32) +DEF_HELPER_5(vlwuff_v_d, void, ptr, ptr, tl, env, i32) +#ifdef TARGET_RISCV64 +DEF_HELPER_6(vamoswapw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoswapd_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoaddw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoaddd_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoxorw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoxord_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoandw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoandd_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoorw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoord_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamominw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamomind_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamomaxw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamomaxd_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamominuw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamominud_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamomaxuw_v_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamomaxud_v_d, void, ptr, ptr, tl, ptr, env, i32) +#endif +DEF_HELPER_6(vamoswapw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoaddw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoxorw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoandw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamoorw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamominw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamomaxw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamominuw_v_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vamomaxuw_v_w, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vadd_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vadd_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsub_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsub_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vadd_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vadd_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vadd_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vadd_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsub_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsub_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsub_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsub_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrsub_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrsub_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrsub_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrsub_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_FLAGS_4(vec_rsubs8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vec_rsubs16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vec_rsubs32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vec_rsubs64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_6(vwaddu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwaddu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwaddu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsubu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsubu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsubu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwadd_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsub_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwaddu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwaddu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwaddu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsubu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsubu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsubu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwadd_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwadd_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwadd_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsub_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsub_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsub_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwaddu_wv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwaddu_wv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwaddu_wv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsubu_wv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsubu_wv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsubu_wv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwadd_wv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwadd_wv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwadd_wv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsub_wv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsub_wv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsub_wv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwaddu_wx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwaddu_wx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwaddu_wx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsubu_wx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsubu_wx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsubu_wx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwadd_wx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwadd_wx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwadd_wx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsub_wx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsub_wx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsub_wx_w, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vadc_vvm_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vadc_vvm_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vadc_vvm_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vadc_vvm_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsbc_vvm_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsbc_vvm_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsbc_vvm_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsbc_vvm_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmadc_vvm_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmadc_vvm_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmadc_vvm_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmadc_vvm_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsbc_vvm_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsbc_vvm_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsbc_vvm_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsbc_vvm_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vadc_vxm_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vadc_vxm_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vadc_vxm_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vadc_vxm_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsbc_vxm_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsbc_vxm_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsbc_vxm_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsbc_vxm_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmadc_vxm_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmadc_vxm_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmadc_vxm_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmadc_vxm_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsbc_vxm_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsbc_vxm_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsbc_vxm_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsbc_vxm_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vand_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vand_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vand_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vand_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vor_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vor_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vor_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vor_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vxor_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vxor_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vxor_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vxor_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vand_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vand_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vand_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vand_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vor_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vor_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vor_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vor_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vxor_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vxor_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vxor_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vxor_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vsll_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsll_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsll_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsll_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsrl_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsrl_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsrl_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsrl_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsra_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsra_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsra_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsra_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsll_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsll_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsll_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsll_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsrl_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsrl_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsrl_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsrl_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsra_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsra_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsra_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsra_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vnsrl_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnsrl_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnsrl_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnsra_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnsra_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnsra_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnsrl_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnsrl_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnsrl_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnsra_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnsra_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnsra_vx_w, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vmseq_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmseq_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmseq_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmseq_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsne_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsne_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsne_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsne_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsltu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsltu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsltu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsltu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmslt_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmslt_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmslt_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmslt_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsleu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsleu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsleu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsleu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsle_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsle_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsle_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmsle_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmseq_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmseq_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmseq_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmseq_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsne_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsne_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsne_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsne_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsltu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsltu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsltu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsltu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmslt_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmslt_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmslt_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmslt_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsleu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsleu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsleu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsleu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsle_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsle_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsle_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsle_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsgtu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsgtu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsgtu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsgtu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsgt_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsgt_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsgt_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmsgt_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vminu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vminu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vminu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vminu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmin_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmin_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmin_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmin_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmaxu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmaxu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmaxu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmaxu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmax_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmax_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmax_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmax_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vminu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vminu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vminu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vminu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmin_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmin_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmin_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmin_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmaxu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmaxu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmaxu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmaxu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmax_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmax_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmax_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmax_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vmul_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmul_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmul_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmul_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulh_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulh_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulh_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulh_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulhu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulhu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulhu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulhu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulhsu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulhsu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulhsu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmulhsu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmul_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmul_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmul_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmul_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulh_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulh_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulh_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulh_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulhu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulhu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulhu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulhu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulhsu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulhsu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulhsu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmulhsu_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vdivu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vdivu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vdivu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vdivu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vdiv_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vdiv_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vdiv_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vdiv_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vremu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vremu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vremu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vremu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrem_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrem_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrem_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrem_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vdivu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vdivu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vdivu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vdivu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vdiv_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vdiv_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vdiv_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vdiv_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vremu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vremu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vremu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vremu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrem_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrem_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrem_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrem_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vwmul_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmul_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmul_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmulu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmulu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmulu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmulsu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmulsu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmulsu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmul_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmul_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmul_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmulu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmulu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmulu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmulsu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmulsu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmulsu_vx_w, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vmacc_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmacc_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmacc_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmacc_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnmsac_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnmsac_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnmsac_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnmsac_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmadd_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmadd_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnmsub_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnmsub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnmsub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnmsub_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmacc_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmacc_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmacc_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmacc_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnmsac_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnmsac_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnmsac_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnmsac_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmadd_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmadd_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmadd_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmadd_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnmsub_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnmsub_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnmsub_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnmsub_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vwmaccu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmaccu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmaccu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmacc_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmacc_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmacc_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmaccsu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmaccsu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmaccsu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwmaccu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmaccu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmaccu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmacc_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmacc_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmacc_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmaccsu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmaccsu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmaccsu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmaccus_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmaccus_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwmaccus_vx_w, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vmerge_vvm_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmerge_vvm_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmerge_vvm_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmerge_vvm_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmerge_vxm_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmerge_vxm_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmerge_vxm_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vmerge_vxm_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_4(vmv_v_v_b, void, ptr, ptr, env, i32) +DEF_HELPER_4(vmv_v_v_h, void, ptr, ptr, env, i32) +DEF_HELPER_4(vmv_v_v_w, void, ptr, ptr, env, i32) +DEF_HELPER_4(vmv_v_v_d, void, ptr, ptr, env, i32) +DEF_HELPER_4(vmv_v_x_b, void, ptr, i64, env, i32) +DEF_HELPER_4(vmv_v_x_h, void, ptr, i64, env, i32) +DEF_HELPER_4(vmv_v_x_w, void, ptr, i64, env, i32) +DEF_HELPER_4(vmv_v_x_d, void, ptr, i64, env, i32) + +DEF_HELPER_6(vsaddu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsaddu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsaddu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsaddu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsadd_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsadd_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssubu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssubu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssubu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssubu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssub_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssub_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsaddu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsaddu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsaddu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsaddu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsadd_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsadd_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsadd_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsadd_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssubu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssubu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssubu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssubu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssub_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssub_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssub_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssub_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vaadd_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vaadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vaadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vaadd_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vasub_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vasub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vasub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vasub_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vaadd_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vaadd_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vaadd_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vaadd_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vasub_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vasub_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vasub_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vasub_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vsmul_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsmul_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsmul_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsmul_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vsmul_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsmul_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsmul_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vsmul_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vwsmaccu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmaccu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmaccu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmacc_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmacc_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmacc_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmaccsu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmaccsu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmaccsu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsmaccu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmaccu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmaccu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmacc_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmacc_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmacc_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmaccsu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmaccsu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmaccsu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmaccus_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmaccus_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsmaccus_vx_w, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vssrl_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssrl_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssrl_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssrl_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssra_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssra_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssra_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssra_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vssrl_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssrl_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssrl_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssrl_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssra_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssra_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssra_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vssra_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vnclip_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnclip_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnclip_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnclipu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnclipu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnclipu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vnclipu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnclipu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnclipu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnclip_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnclip_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vnclip_vx_w, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vfadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfadd_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsub_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfadd_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfadd_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfadd_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsub_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsub_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsub_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfrsub_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfrsub_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfrsub_vf_d, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_6(vfwadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwsub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwsub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwadd_wv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwadd_wv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwsub_wv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwsub_wv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwadd_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwadd_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwsub_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwsub_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwadd_wf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwadd_wf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwsub_wf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwsub_wf_w, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_6(vfmul_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmul_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmul_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfdiv_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfdiv_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfdiv_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmul_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmul_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmul_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfdiv_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfdiv_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfdiv_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfrdiv_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfrdiv_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfrdiv_vf_d, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_6(vfwmul_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwmul_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwmul_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwmul_vf_w, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_6(vfmacc_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmacc_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmacc_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmacc_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmacc_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmacc_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmsac_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmsac_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmsac_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmsac_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmsac_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmsac_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmadd_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmadd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmadd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmadd_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmsub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmsub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmsub_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmsub_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmsub_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfnmsub_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmacc_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmacc_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmacc_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmacc_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmacc_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmacc_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmsac_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmsac_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmsac_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmsac_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmsac_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmsac_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmadd_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmadd_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmadd_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmadd_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmadd_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmadd_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmsub_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmsub_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmsub_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmsub_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmsub_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfnmsub_vf_d, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_6(vfwmacc_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwmacc_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwnmacc_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwnmacc_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwmsac_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwmsac_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwnmsac_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwnmsac_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwmacc_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwmacc_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwnmacc_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwnmacc_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwmsac_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwmsac_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwnmsac_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfwnmsac_vf_w, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_5(vfsqrt_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfsqrt_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfsqrt_v_d, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vfmin_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmin_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmin_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmax_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmax_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmax_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfmin_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmin_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmin_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmax_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmax_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmax_vf_d, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_6(vfsgnj_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnj_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnj_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnjn_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnjn_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnjn_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnjx_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnjx_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnjx_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfsgnj_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsgnj_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsgnj_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsgnjn_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsgnjn_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsgnjn_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsgnjx_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsgnjx_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfsgnjx_vf_d, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_6(vmfeq_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfeq_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfeq_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfne_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfne_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfne_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmflt_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmflt_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmflt_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfle_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfle_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfle_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmfeq_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfeq_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfeq_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfne_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfne_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfne_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmflt_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmflt_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmflt_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfle_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfle_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfle_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfgt_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfgt_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfgt_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfge_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfge_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmfge_vf_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmford_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmford_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmford_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmford_vf_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmford_vf_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vmford_vf_d, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_5(vfclass_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfclass_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfclass_v_d, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vfmerge_vfm_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmerge_vfm_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vfmerge_vfm_d, void, ptr, ptr, i64, ptr, env, i32) + +DEF_HELPER_5(vfcvt_xu_f_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_xu_f_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_xu_f_v_d, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_x_f_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_x_f_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_x_f_v_d, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_f_xu_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_f_xu_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_f_xu_v_d, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_f_x_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_f_x_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfcvt_f_x_v_d, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_5(vfwcvt_xu_f_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_xu_f_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_x_f_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_x_f_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_f_xu_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_f_xu_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_f_x_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_f_x_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_f_f_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvt_f_f_v_w, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_5(vfncvt_xu_f_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_xu_f_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_x_f_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_x_f_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_f_xu_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_f_xu_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_f_x_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_f_x_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_f_f_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfncvt_f_f_v_w, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vredsum_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredsum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredsum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredsum_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmaxu_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmaxu_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmaxu_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmaxu_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmax_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmax_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmax_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmax_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredminu_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredminu_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredminu_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredminu_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmin_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmin_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmin_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredmin_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredand_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredand_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredand_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredand_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredor_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredor_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredor_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredor_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredxor_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredxor_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredxor_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vredxor_vs_d, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vwredsumu_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwredsumu_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwredsumu_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwredsum_vs_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwredsum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwredsum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vfredsum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredsum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredsum_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredmax_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredmax_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredmax_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredmin_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredmin_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredmin_vs_d, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vfwredsum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwredsum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vmand_mm, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmnand_mm, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmandnot_mm, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmxor_mm, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmor_mm, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmnor_mm, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmornot_mm, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vmxnor_mm, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_4(vmpopc_m, tl, ptr, ptr, env, i32) + +DEF_HELPER_4(vmfirst_m, tl, ptr, ptr, env, i32) + +DEF_HELPER_5(vmsbf_m, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vmsif_m, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vmsof_m, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_5(viota_m_b, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(viota_m_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(viota_m_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(viota_m_d, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_4(vid_v_b, void, ptr, ptr, env, i32) +DEF_HELPER_4(vid_v_h, void, ptr, ptr, env, i32) +DEF_HELPER_4(vid_v_w, void, ptr, ptr, env, i32) +DEF_HELPER_4(vid_v_d, void, ptr, ptr, env, i32) + +DEF_HELPER_6(vslideup_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslideup_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslideup_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslideup_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslidedown_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslidedown_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslidedown_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslidedown_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vrgather_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrgather_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrgather_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrgather_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrgather_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrgather_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrgather_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrgather_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vcompress_vm_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vcompress_vm_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vcompress_vm_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vcompress_vm_d, void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/riscv/insn32-64.decode b/target/riscv/insn32-64.decode index 380bf791bc..86153d93fa 100644 --- a/target/riscv/insn32-64.decode +++ b/target/riscv/insn32-64.decode @@ -57,6 +57,17 @@ amomax_d 10100 . . ..... ..... 011 ..... 0101111 @atom_st amominu_d 11000 . . ..... ..... 011 ..... 0101111 @atom_st amomaxu_d 11100 . . ..... ..... 011 ..... 0101111 @atom_st +#*** Vector AMO operations (in addition to Zvamo) *** +vamoswapd_v 00001 . . ..... ..... 111 ..... 0101111 @r_wdvm +vamoaddd_v 00000 . . ..... ..... 111 ..... 0101111 @r_wdvm +vamoxord_v 00100 . . ..... ..... 111 ..... 0101111 @r_wdvm +vamoandd_v 01100 . . ..... ..... 111 ..... 0101111 @r_wdvm +vamoord_v 01000 . . ..... ..... 111 ..... 0101111 @r_wdvm +vamomind_v 10000 . . ..... ..... 111 ..... 0101111 @r_wdvm +vamomaxd_v 10100 . . ..... ..... 111 ..... 0101111 @r_wdvm +vamominud_v 11000 . . ..... ..... 111 ..... 0101111 @r_wdvm +vamomaxud_v 11100 . . ..... ..... 111 ..... 0101111 @r_wdvm + # *** RV64F Standard Extension (in addition to RV32F) *** fcvt_l_s 1100000 00010 ..... ... ..... 1010011 @r2_rm fcvt_lu_s 1100000 00011 ..... ... ..... 1010011 @r2_rm diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 4c8d1215ce..bdd8563067 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -25,6 +25,7 @@ %sh10 20:10 %csr 20:12 %rm 12:3 +%nf 29:3 !function=ex_plus_1 # immediates: %imm_i 20:s12 @@ -43,6 +44,11 @@ &u imm rd &shift shamt rs1 rd &atomic aq rl rs2 rs1 rd +&rmrr vm rd rs1 rs2 +&rmr vm rd rs2 +&rwdvm vm wd rd rs1 rs2 +&r2nfvm vm rd rs1 nf +&rnfvm vm rd rs1 rs2 nf # Formats 32: @r ....... ..... ..... ... ..... ....... &r %rs2 %rs1 %rd @@ -62,6 +68,16 @@ @r_rm ....... ..... ..... ... ..... ....... %rs2 %rs1 %rm %rd @r2_rm ....... ..... ..... ... ..... ....... %rs1 %rm %rd @r2 ....... ..... ..... ... ..... ....... %rs1 %rd +@r2_nfvm ... ... vm:1 ..... ..... ... ..... ....... &r2nfvm %nf %rs1 %rd +@r2_vm ...... vm:1 ..... ..... ... ..... ....... &rmr %rs2 %rd +@r1_vm ...... vm:1 ..... ..... ... ..... ....... %rd +@r_nfvm ... ... vm:1 ..... ..... ... ..... ....... &rnfvm %nf %rs2 %rs1 %rd +@r2rd ....... ..... ..... ... ..... ....... %rs2 %rd +@r_vm ...... vm:1 ..... ..... ... ..... ....... &rmrr %rs2 %rs1 %rd +@r_vm_1 ...... . ..... ..... ... ..... ....... &rmrr vm=1 %rs2 %rs1 %rd +@r_vm_0 ...... . ..... ..... ... ..... ....... &rmrr vm=0 %rs2 %rs1 %rd +@r_wdvm ..... wd:1 vm:1 ..... ..... ... ..... ....... &rwdvm %rs2 %rs1 %rd +@r2_zimm . zimm:11 ..... ... ..... ....... %rs1 %rd @hfence_gvma ....... ..... ..... ... ..... ....... %rs2 %rs1 @hfence_vvma ....... ..... ..... ... ..... ....... %rs2 %rs1 @@ -209,3 +225,359 @@ fcvt_d_wu 1101001 00001 ..... ... ..... 1010011 @r2_rm # *** RV32H Base Instruction Set *** hfence_gvma 0110001 ..... ..... 000 00000 1110011 @hfence_gvma hfence_vvma 0010001 ..... ..... 000 00000 1110011 @hfence_vvma + +# *** RV32V Extension *** + +# *** Vector loads and stores are encoded within LOADFP/STORE-FP *** +vlb_v ... 100 . 00000 ..... 000 ..... 0000111 @r2_nfvm +vlh_v ... 100 . 00000 ..... 101 ..... 0000111 @r2_nfvm +vlw_v ... 100 . 00000 ..... 110 ..... 0000111 @r2_nfvm +vle_v ... 000 . 00000 ..... 111 ..... 0000111 @r2_nfvm +vlbu_v ... 000 . 00000 ..... 000 ..... 0000111 @r2_nfvm +vlhu_v ... 000 . 00000 ..... 101 ..... 0000111 @r2_nfvm +vlwu_v ... 000 . 00000 ..... 110 ..... 0000111 @r2_nfvm +vlbff_v ... 100 . 10000 ..... 000 ..... 0000111 @r2_nfvm +vlhff_v ... 100 . 10000 ..... 101 ..... 0000111 @r2_nfvm +vlwff_v ... 100 . 10000 ..... 110 ..... 0000111 @r2_nfvm +vleff_v ... 000 . 10000 ..... 111 ..... 0000111 @r2_nfvm +vlbuff_v ... 000 . 10000 ..... 000 ..... 0000111 @r2_nfvm +vlhuff_v ... 000 . 10000 ..... 101 ..... 0000111 @r2_nfvm +vlwuff_v ... 000 . 10000 ..... 110 ..... 0000111 @r2_nfvm +vsb_v ... 000 . 00000 ..... 000 ..... 0100111 @r2_nfvm +vsh_v ... 000 . 00000 ..... 101 ..... 0100111 @r2_nfvm +vsw_v ... 000 . 00000 ..... 110 ..... 0100111 @r2_nfvm +vse_v ... 000 . 00000 ..... 111 ..... 0100111 @r2_nfvm + +vlsb_v ... 110 . ..... ..... 000 ..... 0000111 @r_nfvm +vlsh_v ... 110 . ..... ..... 101 ..... 0000111 @r_nfvm +vlsw_v ... 110 . ..... ..... 110 ..... 0000111 @r_nfvm +vlse_v ... 010 . ..... ..... 111 ..... 0000111 @r_nfvm +vlsbu_v ... 010 . ..... ..... 000 ..... 0000111 @r_nfvm +vlshu_v ... 010 . ..... ..... 101 ..... 0000111 @r_nfvm +vlswu_v ... 010 . ..... ..... 110 ..... 0000111 @r_nfvm +vssb_v ... 010 . ..... ..... 000 ..... 0100111 @r_nfvm +vssh_v ... 010 . ..... ..... 101 ..... 0100111 @r_nfvm +vssw_v ... 010 . ..... ..... 110 ..... 0100111 @r_nfvm +vsse_v ... 010 . ..... ..... 111 ..... 0100111 @r_nfvm + +vlxb_v ... 111 . ..... ..... 000 ..... 0000111 @r_nfvm +vlxh_v ... 111 . ..... ..... 101 ..... 0000111 @r_nfvm +vlxw_v ... 111 . ..... ..... 110 ..... 0000111 @r_nfvm +vlxe_v ... 011 . ..... ..... 111 ..... 0000111 @r_nfvm +vlxbu_v ... 011 . ..... ..... 000 ..... 0000111 @r_nfvm +vlxhu_v ... 011 . ..... ..... 101 ..... 0000111 @r_nfvm +vlxwu_v ... 011 . ..... ..... 110 ..... 0000111 @r_nfvm +# Vector ordered-indexed and unordered-indexed store insns. +vsxb_v ... -11 . ..... ..... 000 ..... 0100111 @r_nfvm +vsxh_v ... -11 . ..... ..... 101 ..... 0100111 @r_nfvm +vsxw_v ... -11 . ..... ..... 110 ..... 0100111 @r_nfvm +vsxe_v ... -11 . ..... ..... 111 ..... 0100111 @r_nfvm + +#*** Vector AMO operations are encoded under the standard AMO major opcode *** +vamoswapw_v 00001 . . ..... ..... 110 ..... 0101111 @r_wdvm +vamoaddw_v 00000 . . ..... ..... 110 ..... 0101111 @r_wdvm +vamoxorw_v 00100 . . ..... ..... 110 ..... 0101111 @r_wdvm +vamoandw_v 01100 . . ..... ..... 110 ..... 0101111 @r_wdvm +vamoorw_v 01000 . . ..... ..... 110 ..... 0101111 @r_wdvm +vamominw_v 10000 . . ..... ..... 110 ..... 0101111 @r_wdvm +vamomaxw_v 10100 . . ..... ..... 110 ..... 0101111 @r_wdvm +vamominuw_v 11000 . . ..... ..... 110 ..... 0101111 @r_wdvm +vamomaxuw_v 11100 . . ..... ..... 110 ..... 0101111 @r_wdvm + +# *** new major opcode OP-V *** +vadd_vv 000000 . ..... ..... 000 ..... 1010111 @r_vm +vadd_vx 000000 . ..... ..... 100 ..... 1010111 @r_vm +vadd_vi 000000 . ..... ..... 011 ..... 1010111 @r_vm +vsub_vv 000010 . ..... ..... 000 ..... 1010111 @r_vm +vsub_vx 000010 . ..... ..... 100 ..... 1010111 @r_vm +vrsub_vx 000011 . ..... ..... 100 ..... 1010111 @r_vm +vrsub_vi 000011 . ..... ..... 011 ..... 1010111 @r_vm +vwaddu_vv 110000 . ..... ..... 010 ..... 1010111 @r_vm +vwaddu_vx 110000 . ..... ..... 110 ..... 1010111 @r_vm +vwadd_vv 110001 . ..... ..... 010 ..... 1010111 @r_vm +vwadd_vx 110001 . ..... ..... 110 ..... 1010111 @r_vm +vwsubu_vv 110010 . ..... ..... 010 ..... 1010111 @r_vm +vwsubu_vx 110010 . ..... ..... 110 ..... 1010111 @r_vm +vwsub_vv 110011 . ..... ..... 010 ..... 1010111 @r_vm +vwsub_vx 110011 . ..... ..... 110 ..... 1010111 @r_vm +vwaddu_wv 110100 . ..... ..... 010 ..... 1010111 @r_vm +vwaddu_wx 110100 . ..... ..... 110 ..... 1010111 @r_vm +vwadd_wv 110101 . ..... ..... 010 ..... 1010111 @r_vm +vwadd_wx 110101 . ..... ..... 110 ..... 1010111 @r_vm +vwsubu_wv 110110 . ..... ..... 010 ..... 1010111 @r_vm +vwsubu_wx 110110 . ..... ..... 110 ..... 1010111 @r_vm +vwsub_wv 110111 . ..... ..... 010 ..... 1010111 @r_vm +vwsub_wx 110111 . ..... ..... 110 ..... 1010111 @r_vm +vadc_vvm 010000 1 ..... ..... 000 ..... 1010111 @r_vm_1 +vadc_vxm 010000 1 ..... ..... 100 ..... 1010111 @r_vm_1 +vadc_vim 010000 1 ..... ..... 011 ..... 1010111 @r_vm_1 +vmadc_vvm 010001 1 ..... ..... 000 ..... 1010111 @r_vm_1 +vmadc_vxm 010001 1 ..... ..... 100 ..... 1010111 @r_vm_1 +vmadc_vim 010001 1 ..... ..... 011 ..... 1010111 @r_vm_1 +vsbc_vvm 010010 1 ..... ..... 000 ..... 1010111 @r_vm_1 +vsbc_vxm 010010 1 ..... ..... 100 ..... 1010111 @r_vm_1 +vmsbc_vvm 010011 1 ..... ..... 000 ..... 1010111 @r_vm_1 +vmsbc_vxm 010011 1 ..... ..... 100 ..... 1010111 @r_vm_1 +vand_vv 001001 . ..... ..... 000 ..... 1010111 @r_vm +vand_vx 001001 . ..... ..... 100 ..... 1010111 @r_vm +vand_vi 001001 . ..... ..... 011 ..... 1010111 @r_vm +vor_vv 001010 . ..... ..... 000 ..... 1010111 @r_vm +vor_vx 001010 . ..... ..... 100 ..... 1010111 @r_vm +vor_vi 001010 . ..... ..... 011 ..... 1010111 @r_vm +vxor_vv 001011 . ..... ..... 000 ..... 1010111 @r_vm +vxor_vx 001011 . ..... ..... 100 ..... 1010111 @r_vm +vxor_vi 001011 . ..... ..... 011 ..... 1010111 @r_vm +vsll_vv 100101 . ..... ..... 000 ..... 1010111 @r_vm +vsll_vx 100101 . ..... ..... 100 ..... 1010111 @r_vm +vsll_vi 100101 . ..... ..... 011 ..... 1010111 @r_vm +vsrl_vv 101000 . ..... ..... 000 ..... 1010111 @r_vm +vsrl_vx 101000 . ..... ..... 100 ..... 1010111 @r_vm +vsrl_vi 101000 . ..... ..... 011 ..... 1010111 @r_vm +vsra_vv 101001 . ..... ..... 000 ..... 1010111 @r_vm +vsra_vx 101001 . ..... ..... 100 ..... 1010111 @r_vm +vsra_vi 101001 . ..... ..... 011 ..... 1010111 @r_vm +vnsrl_vv 101100 . ..... ..... 000 ..... 1010111 @r_vm +vnsrl_vx 101100 . ..... ..... 100 ..... 1010111 @r_vm +vnsrl_vi 101100 . ..... ..... 011 ..... 1010111 @r_vm +vnsra_vv 101101 . ..... ..... 000 ..... 1010111 @r_vm +vnsra_vx 101101 . ..... ..... 100 ..... 1010111 @r_vm +vnsra_vi 101101 . ..... ..... 011 ..... 1010111 @r_vm +vmseq_vv 011000 . ..... ..... 000 ..... 1010111 @r_vm +vmseq_vx 011000 . ..... ..... 100 ..... 1010111 @r_vm +vmseq_vi 011000 . ..... ..... 011 ..... 1010111 @r_vm +vmsne_vv 011001 . ..... ..... 000 ..... 1010111 @r_vm +vmsne_vx 011001 . ..... ..... 100 ..... 1010111 @r_vm +vmsne_vi 011001 . ..... ..... 011 ..... 1010111 @r_vm +vmsltu_vv 011010 . ..... ..... 000 ..... 1010111 @r_vm +vmsltu_vx 011010 . ..... ..... 100 ..... 1010111 @r_vm +vmslt_vv 011011 . ..... ..... 000 ..... 1010111 @r_vm +vmslt_vx 011011 . ..... ..... 100 ..... 1010111 @r_vm +vmsleu_vv 011100 . ..... ..... 000 ..... 1010111 @r_vm +vmsleu_vx 011100 . ..... ..... 100 ..... 1010111 @r_vm +vmsleu_vi 011100 . ..... ..... 011 ..... 1010111 @r_vm +vmsle_vv 011101 . ..... ..... 000 ..... 1010111 @r_vm +vmsle_vx 011101 . ..... ..... 100 ..... 1010111 @r_vm +vmsle_vi 011101 . ..... ..... 011 ..... 1010111 @r_vm +vmsgtu_vx 011110 . ..... ..... 100 ..... 1010111 @r_vm +vmsgtu_vi 011110 . ..... ..... 011 ..... 1010111 @r_vm +vmsgt_vx 011111 . ..... ..... 100 ..... 1010111 @r_vm +vmsgt_vi 011111 . ..... ..... 011 ..... 1010111 @r_vm +vminu_vv 000100 . ..... ..... 000 ..... 1010111 @r_vm +vminu_vx 000100 . ..... ..... 100 ..... 1010111 @r_vm +vmin_vv 000101 . ..... ..... 000 ..... 1010111 @r_vm +vmin_vx 000101 . ..... ..... 100 ..... 1010111 @r_vm +vmaxu_vv 000110 . ..... ..... 000 ..... 1010111 @r_vm +vmaxu_vx 000110 . ..... ..... 100 ..... 1010111 @r_vm +vmax_vv 000111 . ..... ..... 000 ..... 1010111 @r_vm +vmax_vx 000111 . ..... ..... 100 ..... 1010111 @r_vm +vmul_vv 100101 . ..... ..... 010 ..... 1010111 @r_vm +vmul_vx 100101 . ..... ..... 110 ..... 1010111 @r_vm +vmulh_vv 100111 . ..... ..... 010 ..... 1010111 @r_vm +vmulh_vx 100111 . ..... ..... 110 ..... 1010111 @r_vm +vmulhu_vv 100100 . ..... ..... 010 ..... 1010111 @r_vm +vmulhu_vx 100100 . ..... ..... 110 ..... 1010111 @r_vm +vmulhsu_vv 100110 . ..... ..... 010 ..... 1010111 @r_vm +vmulhsu_vx 100110 . ..... ..... 110 ..... 1010111 @r_vm +vdivu_vv 100000 . ..... ..... 010 ..... 1010111 @r_vm +vdivu_vx 100000 . ..... ..... 110 ..... 1010111 @r_vm +vdiv_vv 100001 . ..... ..... 010 ..... 1010111 @r_vm +vdiv_vx 100001 . ..... ..... 110 ..... 1010111 @r_vm +vremu_vv 100010 . ..... ..... 010 ..... 1010111 @r_vm +vremu_vx 100010 . ..... ..... 110 ..... 1010111 @r_vm +vrem_vv 100011 . ..... ..... 010 ..... 1010111 @r_vm +vrem_vx 100011 . ..... ..... 110 ..... 1010111 @r_vm +vwmulu_vv 111000 . ..... ..... 010 ..... 1010111 @r_vm +vwmulu_vx 111000 . ..... ..... 110 ..... 1010111 @r_vm +vwmulsu_vv 111010 . ..... ..... 010 ..... 1010111 @r_vm +vwmulsu_vx 111010 . ..... ..... 110 ..... 1010111 @r_vm +vwmul_vv 111011 . ..... ..... 010 ..... 1010111 @r_vm +vwmul_vx 111011 . ..... ..... 110 ..... 1010111 @r_vm +vmacc_vv 101101 . ..... ..... 010 ..... 1010111 @r_vm +vmacc_vx 101101 . ..... ..... 110 ..... 1010111 @r_vm +vnmsac_vv 101111 . ..... ..... 010 ..... 1010111 @r_vm +vnmsac_vx 101111 . ..... ..... 110 ..... 1010111 @r_vm +vmadd_vv 101001 . ..... ..... 010 ..... 1010111 @r_vm +vmadd_vx 101001 . ..... ..... 110 ..... 1010111 @r_vm +vnmsub_vv 101011 . ..... ..... 010 ..... 1010111 @r_vm +vnmsub_vx 101011 . ..... ..... 110 ..... 1010111 @r_vm +vwmaccu_vv 111100 . ..... ..... 010 ..... 1010111 @r_vm +vwmaccu_vx 111100 . ..... ..... 110 ..... 1010111 @r_vm +vwmacc_vv 111101 . ..... ..... 010 ..... 1010111 @r_vm +vwmacc_vx 111101 . ..... ..... 110 ..... 1010111 @r_vm +vwmaccsu_vv 111110 . ..... ..... 010 ..... 1010111 @r_vm +vwmaccsu_vx 111110 . ..... ..... 110 ..... 1010111 @r_vm +vwmaccus_vx 111111 . ..... ..... 110 ..... 1010111 @r_vm +vmv_v_v 010111 1 00000 ..... 000 ..... 1010111 @r2 +vmv_v_x 010111 1 00000 ..... 100 ..... 1010111 @r2 +vmv_v_i 010111 1 00000 ..... 011 ..... 1010111 @r2 +vmerge_vvm 010111 0 ..... ..... 000 ..... 1010111 @r_vm_0 +vmerge_vxm 010111 0 ..... ..... 100 ..... 1010111 @r_vm_0 +vmerge_vim 010111 0 ..... ..... 011 ..... 1010111 @r_vm_0 +vsaddu_vv 100000 . ..... ..... 000 ..... 1010111 @r_vm +vsaddu_vx 100000 . ..... ..... 100 ..... 1010111 @r_vm +vsaddu_vi 100000 . ..... ..... 011 ..... 1010111 @r_vm +vsadd_vv 100001 . ..... ..... 000 ..... 1010111 @r_vm +vsadd_vx 100001 . ..... ..... 100 ..... 1010111 @r_vm +vsadd_vi 100001 . ..... ..... 011 ..... 1010111 @r_vm +vssubu_vv 100010 . ..... ..... 000 ..... 1010111 @r_vm +vssubu_vx 100010 . ..... ..... 100 ..... 1010111 @r_vm +vssub_vv 100011 . ..... ..... 000 ..... 1010111 @r_vm +vssub_vx 100011 . ..... ..... 100 ..... 1010111 @r_vm +vaadd_vv 100100 . ..... ..... 000 ..... 1010111 @r_vm +vaadd_vx 100100 . ..... ..... 100 ..... 1010111 @r_vm +vaadd_vi 100100 . ..... ..... 011 ..... 1010111 @r_vm +vasub_vv 100110 . ..... ..... 000 ..... 1010111 @r_vm +vasub_vx 100110 . ..... ..... 100 ..... 1010111 @r_vm +vsmul_vv 100111 . ..... ..... 000 ..... 1010111 @r_vm +vsmul_vx 100111 . ..... ..... 100 ..... 1010111 @r_vm +vwsmaccu_vv 111100 . ..... ..... 000 ..... 1010111 @r_vm +vwsmaccu_vx 111100 . ..... ..... 100 ..... 1010111 @r_vm +vwsmacc_vv 111101 . ..... ..... 000 ..... 1010111 @r_vm +vwsmacc_vx 111101 . ..... ..... 100 ..... 1010111 @r_vm +vwsmaccsu_vv 111110 . ..... ..... 000 ..... 1010111 @r_vm +vwsmaccsu_vx 111110 . ..... ..... 100 ..... 1010111 @r_vm +vwsmaccus_vx 111111 . ..... ..... 100 ..... 1010111 @r_vm +vssrl_vv 101010 . ..... ..... 000 ..... 1010111 @r_vm +vssrl_vx 101010 . ..... ..... 100 ..... 1010111 @r_vm +vssrl_vi 101010 . ..... ..... 011 ..... 1010111 @r_vm +vssra_vv 101011 . ..... ..... 000 ..... 1010111 @r_vm +vssra_vx 101011 . ..... ..... 100 ..... 1010111 @r_vm +vssra_vi 101011 . ..... ..... 011 ..... 1010111 @r_vm +vnclipu_vv 101110 . ..... ..... 000 ..... 1010111 @r_vm +vnclipu_vx 101110 . ..... ..... 100 ..... 1010111 @r_vm +vnclipu_vi 101110 . ..... ..... 011 ..... 1010111 @r_vm +vnclip_vv 101111 . ..... ..... 000 ..... 1010111 @r_vm +vnclip_vx 101111 . ..... ..... 100 ..... 1010111 @r_vm +vnclip_vi 101111 . ..... ..... 011 ..... 1010111 @r_vm +vfadd_vv 000000 . ..... ..... 001 ..... 1010111 @r_vm +vfadd_vf 000000 . ..... ..... 101 ..... 1010111 @r_vm +vfsub_vv 000010 . ..... ..... 001 ..... 1010111 @r_vm +vfsub_vf 000010 . ..... ..... 101 ..... 1010111 @r_vm +vfrsub_vf 100111 . ..... ..... 101 ..... 1010111 @r_vm +vfwadd_vv 110000 . ..... ..... 001 ..... 1010111 @r_vm +vfwadd_vf 110000 . ..... ..... 101 ..... 1010111 @r_vm +vfwadd_wv 110100 . ..... ..... 001 ..... 1010111 @r_vm +vfwadd_wf 110100 . ..... ..... 101 ..... 1010111 @r_vm +vfwsub_vv 110010 . ..... ..... 001 ..... 1010111 @r_vm +vfwsub_vf 110010 . ..... ..... 101 ..... 1010111 @r_vm +vfwsub_wv 110110 . ..... ..... 001 ..... 1010111 @r_vm +vfwsub_wf 110110 . ..... ..... 101 ..... 1010111 @r_vm +vfmul_vv 100100 . ..... ..... 001 ..... 1010111 @r_vm +vfmul_vf 100100 . ..... ..... 101 ..... 1010111 @r_vm +vfdiv_vv 100000 . ..... ..... 001 ..... 1010111 @r_vm +vfdiv_vf 100000 . ..... ..... 101 ..... 1010111 @r_vm +vfrdiv_vf 100001 . ..... ..... 101 ..... 1010111 @r_vm +vfwmul_vv 111000 . ..... ..... 001 ..... 1010111 @r_vm +vfwmul_vf 111000 . ..... ..... 101 ..... 1010111 @r_vm +vfmacc_vv 101100 . ..... ..... 001 ..... 1010111 @r_vm +vfnmacc_vv 101101 . ..... ..... 001 ..... 1010111 @r_vm +vfnmacc_vf 101101 . ..... ..... 101 ..... 1010111 @r_vm +vfmacc_vf 101100 . ..... ..... 101 ..... 1010111 @r_vm +vfmsac_vv 101110 . ..... ..... 001 ..... 1010111 @r_vm +vfmsac_vf 101110 . ..... ..... 101 ..... 1010111 @r_vm +vfnmsac_vv 101111 . ..... ..... 001 ..... 1010111 @r_vm +vfnmsac_vf 101111 . ..... ..... 101 ..... 1010111 @r_vm +vfmadd_vv 101000 . ..... ..... 001 ..... 1010111 @r_vm +vfmadd_vf 101000 . ..... ..... 101 ..... 1010111 @r_vm +vfnmadd_vv 101001 . ..... ..... 001 ..... 1010111 @r_vm +vfnmadd_vf 101001 . ..... ..... 101 ..... 1010111 @r_vm +vfmsub_vv 101010 . ..... ..... 001 ..... 1010111 @r_vm +vfmsub_vf 101010 . ..... ..... 101 ..... 1010111 @r_vm +vfnmsub_vv 101011 . ..... ..... 001 ..... 1010111 @r_vm +vfnmsub_vf 101011 . ..... ..... 101 ..... 1010111 @r_vm +vfwmacc_vv 111100 . ..... ..... 001 ..... 1010111 @r_vm +vfwmacc_vf 111100 . ..... ..... 101 ..... 1010111 @r_vm +vfwnmacc_vv 111101 . ..... ..... 001 ..... 1010111 @r_vm +vfwnmacc_vf 111101 . ..... ..... 101 ..... 1010111 @r_vm +vfwmsac_vv 111110 . ..... ..... 001 ..... 1010111 @r_vm +vfwmsac_vf 111110 . ..... ..... 101 ..... 1010111 @r_vm +vfwnmsac_vv 111111 . ..... ..... 001 ..... 1010111 @r_vm +vfwnmsac_vf 111111 . ..... ..... 101 ..... 1010111 @r_vm +vfsqrt_v 100011 . ..... 00000 001 ..... 1010111 @r2_vm +vfmin_vv 000100 . ..... ..... 001 ..... 1010111 @r_vm +vfmin_vf 000100 . ..... ..... 101 ..... 1010111 @r_vm +vfmax_vv 000110 . ..... ..... 001 ..... 1010111 @r_vm +vfmax_vf 000110 . ..... ..... 101 ..... 1010111 @r_vm +vfsgnj_vv 001000 . ..... ..... 001 ..... 1010111 @r_vm +vfsgnj_vf 001000 . ..... ..... 101 ..... 1010111 @r_vm +vfsgnjn_vv 001001 . ..... ..... 001 ..... 1010111 @r_vm +vfsgnjn_vf 001001 . ..... ..... 101 ..... 1010111 @r_vm +vfsgnjx_vv 001010 . ..... ..... 001 ..... 1010111 @r_vm +vfsgnjx_vf 001010 . ..... ..... 101 ..... 1010111 @r_vm +vmfeq_vv 011000 . ..... ..... 001 ..... 1010111 @r_vm +vmfeq_vf 011000 . ..... ..... 101 ..... 1010111 @r_vm +vmfne_vv 011100 . ..... ..... 001 ..... 1010111 @r_vm +vmfne_vf 011100 . ..... ..... 101 ..... 1010111 @r_vm +vmflt_vv 011011 . ..... ..... 001 ..... 1010111 @r_vm +vmflt_vf 011011 . ..... ..... 101 ..... 1010111 @r_vm +vmfle_vv 011001 . ..... ..... 001 ..... 1010111 @r_vm +vmfle_vf 011001 . ..... ..... 101 ..... 1010111 @r_vm +vmfgt_vf 011101 . ..... ..... 101 ..... 1010111 @r_vm +vmfge_vf 011111 . ..... ..... 101 ..... 1010111 @r_vm +vmford_vv 011010 . ..... ..... 001 ..... 1010111 @r_vm +vmford_vf 011010 . ..... ..... 101 ..... 1010111 @r_vm +vfclass_v 100011 . ..... 10000 001 ..... 1010111 @r2_vm +vfmerge_vfm 010111 0 ..... ..... 101 ..... 1010111 @r_vm_0 +vfmv_v_f 010111 1 00000 ..... 101 ..... 1010111 @r2 +vfcvt_xu_f_v 100010 . ..... 00000 001 ..... 1010111 @r2_vm +vfcvt_x_f_v 100010 . ..... 00001 001 ..... 1010111 @r2_vm +vfcvt_f_xu_v 100010 . ..... 00010 001 ..... 1010111 @r2_vm +vfcvt_f_x_v 100010 . ..... 00011 001 ..... 1010111 @r2_vm +vfwcvt_xu_f_v 100010 . ..... 01000 001 ..... 1010111 @r2_vm +vfwcvt_x_f_v 100010 . ..... 01001 001 ..... 1010111 @r2_vm +vfwcvt_f_xu_v 100010 . ..... 01010 001 ..... 1010111 @r2_vm +vfwcvt_f_x_v 100010 . ..... 01011 001 ..... 1010111 @r2_vm +vfwcvt_f_f_v 100010 . ..... 01100 001 ..... 1010111 @r2_vm +vfncvt_xu_f_v 100010 . ..... 10000 001 ..... 1010111 @r2_vm +vfncvt_x_f_v 100010 . ..... 10001 001 ..... 1010111 @r2_vm +vfncvt_f_xu_v 100010 . ..... 10010 001 ..... 1010111 @r2_vm +vfncvt_f_x_v 100010 . ..... 10011 001 ..... 1010111 @r2_vm +vfncvt_f_f_v 100010 . ..... 10100 001 ..... 1010111 @r2_vm +vredsum_vs 000000 . ..... ..... 010 ..... 1010111 @r_vm +vredand_vs 000001 . ..... ..... 010 ..... 1010111 @r_vm +vredor_vs 000010 . ..... ..... 010 ..... 1010111 @r_vm +vredxor_vs 000011 . ..... ..... 010 ..... 1010111 @r_vm +vredminu_vs 000100 . ..... ..... 010 ..... 1010111 @r_vm +vredmin_vs 000101 . ..... ..... 010 ..... 1010111 @r_vm +vredmaxu_vs 000110 . ..... ..... 010 ..... 1010111 @r_vm +vredmax_vs 000111 . ..... ..... 010 ..... 1010111 @r_vm +vwredsumu_vs 110000 . ..... ..... 000 ..... 1010111 @r_vm +vwredsum_vs 110001 . ..... ..... 000 ..... 1010111 @r_vm +# Vector ordered and unordered reduction sum +vfredsum_vs 0000-1 . ..... ..... 001 ..... 1010111 @r_vm +vfredmin_vs 000101 . ..... ..... 001 ..... 1010111 @r_vm +vfredmax_vs 000111 . ..... ..... 001 ..... 1010111 @r_vm +# Vector widening ordered and unordered float reduction sum +vfwredsum_vs 1100-1 . ..... ..... 001 ..... 1010111 @r_vm +vmand_mm 011001 - ..... ..... 010 ..... 1010111 @r +vmnand_mm 011101 - ..... ..... 010 ..... 1010111 @r +vmandnot_mm 011000 - ..... ..... 010 ..... 1010111 @r +vmxor_mm 011011 - ..... ..... 010 ..... 1010111 @r +vmor_mm 011010 - ..... ..... 010 ..... 1010111 @r +vmnor_mm 011110 - ..... ..... 010 ..... 1010111 @r +vmornot_mm 011100 - ..... ..... 010 ..... 1010111 @r +vmxnor_mm 011111 - ..... ..... 010 ..... 1010111 @r +vmpopc_m 010100 . ..... ----- 010 ..... 1010111 @r2_vm +vmfirst_m 010101 . ..... ----- 010 ..... 1010111 @r2_vm +vmsbf_m 010110 . ..... 00001 010 ..... 1010111 @r2_vm +vmsif_m 010110 . ..... 00011 010 ..... 1010111 @r2_vm +vmsof_m 010110 . ..... 00010 010 ..... 1010111 @r2_vm +viota_m 010110 . ..... 10000 010 ..... 1010111 @r2_vm +vid_v 010110 . 00000 10001 010 ..... 1010111 @r1_vm +vext_x_v 001100 1 ..... ..... 010 ..... 1010111 @r +vmv_s_x 001101 1 00000 ..... 110 ..... 1010111 @r2 +vfmv_f_s 001100 1 ..... 00000 001 ..... 1010111 @r2rd +vfmv_s_f 001101 1 00000 ..... 101 ..... 1010111 @r2 +vslideup_vx 001110 . ..... ..... 100 ..... 1010111 @r_vm +vslideup_vi 001110 . ..... ..... 011 ..... 1010111 @r_vm +vslide1up_vx 001110 . ..... ..... 110 ..... 1010111 @r_vm +vslidedown_vx 001111 . ..... ..... 100 ..... 1010111 @r_vm +vslidedown_vi 001111 . ..... ..... 011 ..... 1010111 @r_vm +vslide1down_vx 001111 . ..... ..... 110 ..... 1010111 @r_vm +vrgather_vv 001100 . ..... ..... 000 ..... 1010111 @r_vm +vrgather_vx 001100 . ..... ..... 100 ..... 1010111 @r_vm +vrgather_vi 001100 . ..... ..... 011 ..... 1010111 @r_vm +vcompress_vm 010111 - ..... ..... 010 ..... 1010111 @r + +vsetvli 0 ........... ..... 111 ..... 1010111 @r2_zimm +vsetvl 1000000 ..... ..... 111 ..... 1010111 @r diff --git a/target/riscv/insn_trans/trans_rvv.inc.c b/target/riscv/insn_trans/trans_rvv.inc.c new file mode 100644 index 0000000000..dc333e6a91 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvv.inc.c @@ -0,0 +1,2888 @@ +/* + * RISC-V translation routines for the RVV Standard Extension. + * + * Copyright (c) 2020 T-Head Semiconductor Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "tcg/tcg-op-gvec.h" +#include "tcg/tcg-gvec-desc.h" +#include "internals.h" + +static bool trans_vsetvl(DisasContext *ctx, arg_vsetvl *a) +{ + TCGv s1, s2, dst; + + if (!has_ext(ctx, RVV)) { + return false; + } + + s2 = tcg_temp_new(); + dst = tcg_temp_new(); + + /* Using x0 as the rs1 register specifier, encodes an infinite AVL */ + if (a->rs1 == 0) { + /* As the mask is at least one bit, RV_VLEN_MAX is >= VLMAX */ + s1 = tcg_const_tl(RV_VLEN_MAX); + } else { + s1 = tcg_temp_new(); + gen_get_gpr(s1, a->rs1); + } + gen_get_gpr(s2, a->rs2); + gen_helper_vsetvl(dst, cpu_env, s1, s2); + gen_set_gpr(a->rd, dst); + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); + lookup_and_goto_ptr(ctx); + ctx->base.is_jmp = DISAS_NORETURN; + + tcg_temp_free(s1); + tcg_temp_free(s2); + tcg_temp_free(dst); + return true; +} + +static bool trans_vsetvli(DisasContext *ctx, arg_vsetvli *a) +{ + TCGv s1, s2, dst; + + if (!has_ext(ctx, RVV)) { + return false; + } + + s2 = tcg_const_tl(a->zimm); + dst = tcg_temp_new(); + + /* Using x0 as the rs1 register specifier, encodes an infinite AVL */ + if (a->rs1 == 0) { + /* As the mask is at least one bit, RV_VLEN_MAX is >= VLMAX */ + s1 = tcg_const_tl(RV_VLEN_MAX); + } else { + s1 = tcg_temp_new(); + gen_get_gpr(s1, a->rs1); + } + gen_helper_vsetvl(dst, cpu_env, s1, s2); + gen_set_gpr(a->rd, dst); + gen_goto_tb(ctx, 0, ctx->pc_succ_insn); + ctx->base.is_jmp = DISAS_NORETURN; + + tcg_temp_free(s1); + tcg_temp_free(s2); + tcg_temp_free(dst); + return true; +} + +/* vector register offset from env */ +static uint32_t vreg_ofs(DisasContext *s, int reg) +{ + return offsetof(CPURISCVState, vreg) + reg * s->vlen / 8; +} + +/* check functions */ + +/* + * In cpu_get_tb_cpu_state(), set VILL if RVV was not present. + * So RVV is also be checked in this function. + */ +static bool vext_check_isa_ill(DisasContext *s) +{ + return !s->vill; +} + +/* + * There are two rules check here. + * + * 1. Vector register numbers are multiples of LMUL. (Section 3.2) + * + * 2. For all widening instructions, the destination LMUL value must also be + * a supported LMUL value. (Section 11.2) + */ +static bool vext_check_reg(DisasContext *s, uint32_t reg, bool widen) +{ + /* + * The destination vector register group results are arranged as if both + * SEW and LMUL were at twice their current settings. (Section 11.2). + */ + int legal = widen ? 2 << s->lmul : 1 << s->lmul; + + return !((s->lmul == 0x3 && widen) || (reg % legal)); +} + +/* + * There are two rules check here. + * + * 1. The destination vector register group for a masked vector instruction can + * only overlap the source mask register (v0) when LMUL=1. (Section 5.3) + * + * 2. In widen instructions and some other insturctions, like vslideup.vx, + * there is no need to check whether LMUL=1. + */ +static bool vext_check_overlap_mask(DisasContext *s, uint32_t vd, bool vm, + bool force) +{ + return (vm != 0 || vd != 0) || (!force && (s->lmul == 0)); +} + +/* The LMUL setting must be such that LMUL * NFIELDS <= 8. (Section 7.8) */ +static bool vext_check_nf(DisasContext *s, uint32_t nf) +{ + return (1 << s->lmul) * nf <= 8; +} + +/* + * The destination vector register group cannot overlap a source vector register + * group of a different element width. (Section 11.2) + */ +static inline bool vext_check_overlap_group(int rd, int dlen, int rs, int slen) +{ + return ((rd >= rs + slen) || (rs >= rd + dlen)); +} +/* common translation macro */ +#define GEN_VEXT_TRANS(NAME, SEQ, ARGTYPE, OP, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_##ARGTYPE *a)\ +{ \ + if (CHECK(s, a)) { \ + return OP(s, a, SEQ); \ + } \ + return false; \ +} + +/* + *** unit stride load and store + */ +typedef void gen_helper_ldst_us(TCGv_ptr, TCGv_ptr, TCGv, + TCGv_env, TCGv_i32); + +static bool ldst_us_trans(uint32_t vd, uint32_t rs1, uint32_t data, + gen_helper_ldst_us *fn, DisasContext *s) +{ + TCGv_ptr dest, mask; + TCGv base; + TCGv_i32 desc; + + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + base = tcg_temp_new(); + + /* + * As simd_desc supports at most 256 bytes, and in this implementation, + * the max vector group length is 2048 bytes. So split it into two parts. + * + * The first part is vlen in bytes, encoded in maxsz of simd_desc. + * The second part is lmul, encoded in data of simd_desc. + */ + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + gen_get_gpr(base, rs1); + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + fn(dest, mask, base, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_ptr(mask); + tcg_temp_free(base); + tcg_temp_free_i32(desc); + gen_set_label(over); + return true; +} + +static bool ld_us_op(DisasContext *s, arg_r2nfvm *a, uint8_t seq) +{ + uint32_t data = 0; + gen_helper_ldst_us *fn; + static gen_helper_ldst_us * const fns[2][7][4] = { + /* masked unit stride load */ + { { gen_helper_vlb_v_b_mask, gen_helper_vlb_v_h_mask, + gen_helper_vlb_v_w_mask, gen_helper_vlb_v_d_mask }, + { NULL, gen_helper_vlh_v_h_mask, + gen_helper_vlh_v_w_mask, gen_helper_vlh_v_d_mask }, + { NULL, NULL, + gen_helper_vlw_v_w_mask, gen_helper_vlw_v_d_mask }, + { gen_helper_vle_v_b_mask, gen_helper_vle_v_h_mask, + gen_helper_vle_v_w_mask, gen_helper_vle_v_d_mask }, + { gen_helper_vlbu_v_b_mask, gen_helper_vlbu_v_h_mask, + gen_helper_vlbu_v_w_mask, gen_helper_vlbu_v_d_mask }, + { NULL, gen_helper_vlhu_v_h_mask, + gen_helper_vlhu_v_w_mask, gen_helper_vlhu_v_d_mask }, + { NULL, NULL, + gen_helper_vlwu_v_w_mask, gen_helper_vlwu_v_d_mask } }, + /* unmasked unit stride load */ + { { gen_helper_vlb_v_b, gen_helper_vlb_v_h, + gen_helper_vlb_v_w, gen_helper_vlb_v_d }, + { NULL, gen_helper_vlh_v_h, + gen_helper_vlh_v_w, gen_helper_vlh_v_d }, + { NULL, NULL, + gen_helper_vlw_v_w, gen_helper_vlw_v_d }, + { gen_helper_vle_v_b, gen_helper_vle_v_h, + gen_helper_vle_v_w, gen_helper_vle_v_d }, + { gen_helper_vlbu_v_b, gen_helper_vlbu_v_h, + gen_helper_vlbu_v_w, gen_helper_vlbu_v_d }, + { NULL, gen_helper_vlhu_v_h, + gen_helper_vlhu_v_w, gen_helper_vlhu_v_d }, + { NULL, NULL, + gen_helper_vlwu_v_w, gen_helper_vlwu_v_d } } + }; + + fn = fns[a->vm][seq][s->sew]; + if (fn == NULL) { + return false; + } + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, NF, a->nf); + return ldst_us_trans(a->rd, a->rs1, data, fn, s); +} + +static bool ld_us_check(DisasContext *s, arg_r2nfvm* a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_nf(s, a->nf)); +} + +GEN_VEXT_TRANS(vlb_v, 0, r2nfvm, ld_us_op, ld_us_check) +GEN_VEXT_TRANS(vlh_v, 1, r2nfvm, ld_us_op, ld_us_check) +GEN_VEXT_TRANS(vlw_v, 2, r2nfvm, ld_us_op, ld_us_check) +GEN_VEXT_TRANS(vle_v, 3, r2nfvm, ld_us_op, ld_us_check) +GEN_VEXT_TRANS(vlbu_v, 4, r2nfvm, ld_us_op, ld_us_check) +GEN_VEXT_TRANS(vlhu_v, 5, r2nfvm, ld_us_op, ld_us_check) +GEN_VEXT_TRANS(vlwu_v, 6, r2nfvm, ld_us_op, ld_us_check) + +static bool st_us_op(DisasContext *s, arg_r2nfvm *a, uint8_t seq) +{ + uint32_t data = 0; + gen_helper_ldst_us *fn; + static gen_helper_ldst_us * const fns[2][4][4] = { + /* masked unit stride load and store */ + { { gen_helper_vsb_v_b_mask, gen_helper_vsb_v_h_mask, + gen_helper_vsb_v_w_mask, gen_helper_vsb_v_d_mask }, + { NULL, gen_helper_vsh_v_h_mask, + gen_helper_vsh_v_w_mask, gen_helper_vsh_v_d_mask }, + { NULL, NULL, + gen_helper_vsw_v_w_mask, gen_helper_vsw_v_d_mask }, + { gen_helper_vse_v_b_mask, gen_helper_vse_v_h_mask, + gen_helper_vse_v_w_mask, gen_helper_vse_v_d_mask } }, + /* unmasked unit stride store */ + { { gen_helper_vsb_v_b, gen_helper_vsb_v_h, + gen_helper_vsb_v_w, gen_helper_vsb_v_d }, + { NULL, gen_helper_vsh_v_h, + gen_helper_vsh_v_w, gen_helper_vsh_v_d }, + { NULL, NULL, + gen_helper_vsw_v_w, gen_helper_vsw_v_d }, + { gen_helper_vse_v_b, gen_helper_vse_v_h, + gen_helper_vse_v_w, gen_helper_vse_v_d } } + }; + + fn = fns[a->vm][seq][s->sew]; + if (fn == NULL) { + return false; + } + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, NF, a->nf); + return ldst_us_trans(a->rd, a->rs1, data, fn, s); +} + +static bool st_us_check(DisasContext *s, arg_r2nfvm* a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_nf(s, a->nf)); +} + +GEN_VEXT_TRANS(vsb_v, 0, r2nfvm, st_us_op, st_us_check) +GEN_VEXT_TRANS(vsh_v, 1, r2nfvm, st_us_op, st_us_check) +GEN_VEXT_TRANS(vsw_v, 2, r2nfvm, st_us_op, st_us_check) +GEN_VEXT_TRANS(vse_v, 3, r2nfvm, st_us_op, st_us_check) + +/* + *** stride load and store + */ +typedef void gen_helper_ldst_stride(TCGv_ptr, TCGv_ptr, TCGv, + TCGv, TCGv_env, TCGv_i32); + +static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, + uint32_t data, gen_helper_ldst_stride *fn, + DisasContext *s) +{ + TCGv_ptr dest, mask; + TCGv base, stride; + TCGv_i32 desc; + + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + base = tcg_temp_new(); + stride = tcg_temp_new(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + gen_get_gpr(base, rs1); + gen_get_gpr(stride, rs2); + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + fn(dest, mask, base, stride, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_ptr(mask); + tcg_temp_free(base); + tcg_temp_free(stride); + tcg_temp_free_i32(desc); + gen_set_label(over); + return true; +} + +static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t seq) +{ + uint32_t data = 0; + gen_helper_ldst_stride *fn; + static gen_helper_ldst_stride * const fns[7][4] = { + { gen_helper_vlsb_v_b, gen_helper_vlsb_v_h, + gen_helper_vlsb_v_w, gen_helper_vlsb_v_d }, + { NULL, gen_helper_vlsh_v_h, + gen_helper_vlsh_v_w, gen_helper_vlsh_v_d }, + { NULL, NULL, + gen_helper_vlsw_v_w, gen_helper_vlsw_v_d }, + { gen_helper_vlse_v_b, gen_helper_vlse_v_h, + gen_helper_vlse_v_w, gen_helper_vlse_v_d }, + { gen_helper_vlsbu_v_b, gen_helper_vlsbu_v_h, + gen_helper_vlsbu_v_w, gen_helper_vlsbu_v_d }, + { NULL, gen_helper_vlshu_v_h, + gen_helper_vlshu_v_w, gen_helper_vlshu_v_d }, + { NULL, NULL, + gen_helper_vlswu_v_w, gen_helper_vlswu_v_d }, + }; + + fn = fns[seq][s->sew]; + if (fn == NULL) { + return false; + } + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, NF, a->nf); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); +} + +static bool ld_stride_check(DisasContext *s, arg_rnfvm* a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_nf(s, a->nf)); +} + +GEN_VEXT_TRANS(vlsb_v, 0, rnfvm, ld_stride_op, ld_stride_check) +GEN_VEXT_TRANS(vlsh_v, 1, rnfvm, ld_stride_op, ld_stride_check) +GEN_VEXT_TRANS(vlsw_v, 2, rnfvm, ld_stride_op, ld_stride_check) +GEN_VEXT_TRANS(vlse_v, 3, rnfvm, ld_stride_op, ld_stride_check) +GEN_VEXT_TRANS(vlsbu_v, 4, rnfvm, ld_stride_op, ld_stride_check) +GEN_VEXT_TRANS(vlshu_v, 5, rnfvm, ld_stride_op, ld_stride_check) +GEN_VEXT_TRANS(vlswu_v, 6, rnfvm, ld_stride_op, ld_stride_check) + +static bool st_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t seq) +{ + uint32_t data = 0; + gen_helper_ldst_stride *fn; + static gen_helper_ldst_stride * const fns[4][4] = { + /* masked stride store */ + { gen_helper_vssb_v_b, gen_helper_vssb_v_h, + gen_helper_vssb_v_w, gen_helper_vssb_v_d }, + { NULL, gen_helper_vssh_v_h, + gen_helper_vssh_v_w, gen_helper_vssh_v_d }, + { NULL, NULL, + gen_helper_vssw_v_w, gen_helper_vssw_v_d }, + { gen_helper_vsse_v_b, gen_helper_vsse_v_h, + gen_helper_vsse_v_w, gen_helper_vsse_v_d } + }; + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, NF, a->nf); + fn = fns[seq][s->sew]; + if (fn == NULL) { + return false; + } + + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); +} + +static bool st_stride_check(DisasContext *s, arg_rnfvm* a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_nf(s, a->nf)); +} + +GEN_VEXT_TRANS(vssb_v, 0, rnfvm, st_stride_op, st_stride_check) +GEN_VEXT_TRANS(vssh_v, 1, rnfvm, st_stride_op, st_stride_check) +GEN_VEXT_TRANS(vssw_v, 2, rnfvm, st_stride_op, st_stride_check) +GEN_VEXT_TRANS(vsse_v, 3, rnfvm, st_stride_op, st_stride_check) + +/* + *** index load and store + */ +typedef void gen_helper_ldst_index(TCGv_ptr, TCGv_ptr, TCGv, + TCGv_ptr, TCGv_env, TCGv_i32); + +static bool ldst_index_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, + uint32_t data, gen_helper_ldst_index *fn, + DisasContext *s) +{ + TCGv_ptr dest, mask, index; + TCGv base; + TCGv_i32 desc; + + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + index = tcg_temp_new_ptr(); + base = tcg_temp_new(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + gen_get_gpr(base, rs1); + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(index, cpu_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + fn(dest, mask, base, index, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_ptr(mask); + tcg_temp_free_ptr(index); + tcg_temp_free(base); + tcg_temp_free_i32(desc); + gen_set_label(over); + return true; +} + +static bool ld_index_op(DisasContext *s, arg_rnfvm *a, uint8_t seq) +{ + uint32_t data = 0; + gen_helper_ldst_index *fn; + static gen_helper_ldst_index * const fns[7][4] = { + { gen_helper_vlxb_v_b, gen_helper_vlxb_v_h, + gen_helper_vlxb_v_w, gen_helper_vlxb_v_d }, + { NULL, gen_helper_vlxh_v_h, + gen_helper_vlxh_v_w, gen_helper_vlxh_v_d }, + { NULL, NULL, + gen_helper_vlxw_v_w, gen_helper_vlxw_v_d }, + { gen_helper_vlxe_v_b, gen_helper_vlxe_v_h, + gen_helper_vlxe_v_w, gen_helper_vlxe_v_d }, + { gen_helper_vlxbu_v_b, gen_helper_vlxbu_v_h, + gen_helper_vlxbu_v_w, gen_helper_vlxbu_v_d }, + { NULL, gen_helper_vlxhu_v_h, + gen_helper_vlxhu_v_w, gen_helper_vlxhu_v_d }, + { NULL, NULL, + gen_helper_vlxwu_v_w, gen_helper_vlxwu_v_d }, + }; + + fn = fns[seq][s->sew]; + if (fn == NULL) { + return false; + } + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, NF, a->nf); + return ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s); +} + +static bool ld_index_check(DisasContext *s, arg_rnfvm* a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + vext_check_nf(s, a->nf)); +} + +GEN_VEXT_TRANS(vlxb_v, 0, rnfvm, ld_index_op, ld_index_check) +GEN_VEXT_TRANS(vlxh_v, 1, rnfvm, ld_index_op, ld_index_check) +GEN_VEXT_TRANS(vlxw_v, 2, rnfvm, ld_index_op, ld_index_check) +GEN_VEXT_TRANS(vlxe_v, 3, rnfvm, ld_index_op, ld_index_check) +GEN_VEXT_TRANS(vlxbu_v, 4, rnfvm, ld_index_op, ld_index_check) +GEN_VEXT_TRANS(vlxhu_v, 5, rnfvm, ld_index_op, ld_index_check) +GEN_VEXT_TRANS(vlxwu_v, 6, rnfvm, ld_index_op, ld_index_check) + +static bool st_index_op(DisasContext *s, arg_rnfvm *a, uint8_t seq) +{ + uint32_t data = 0; + gen_helper_ldst_index *fn; + static gen_helper_ldst_index * const fns[4][4] = { + { gen_helper_vsxb_v_b, gen_helper_vsxb_v_h, + gen_helper_vsxb_v_w, gen_helper_vsxb_v_d }, + { NULL, gen_helper_vsxh_v_h, + gen_helper_vsxh_v_w, gen_helper_vsxh_v_d }, + { NULL, NULL, + gen_helper_vsxw_v_w, gen_helper_vsxw_v_d }, + { gen_helper_vsxe_v_b, gen_helper_vsxe_v_h, + gen_helper_vsxe_v_w, gen_helper_vsxe_v_d } + }; + + fn = fns[seq][s->sew]; + if (fn == NULL) { + return false; + } + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, NF, a->nf); + return ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s); +} + +static bool st_index_check(DisasContext *s, arg_rnfvm* a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + vext_check_nf(s, a->nf)); +} + +GEN_VEXT_TRANS(vsxb_v, 0, rnfvm, st_index_op, st_index_check) +GEN_VEXT_TRANS(vsxh_v, 1, rnfvm, st_index_op, st_index_check) +GEN_VEXT_TRANS(vsxw_v, 2, rnfvm, st_index_op, st_index_check) +GEN_VEXT_TRANS(vsxe_v, 3, rnfvm, st_index_op, st_index_check) + +/* + *** unit stride fault-only-first load + */ +static bool ldff_trans(uint32_t vd, uint32_t rs1, uint32_t data, + gen_helper_ldst_us *fn, DisasContext *s) +{ + TCGv_ptr dest, mask; + TCGv base; + TCGv_i32 desc; + + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + base = tcg_temp_new(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + gen_get_gpr(base, rs1); + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + fn(dest, mask, base, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_ptr(mask); + tcg_temp_free(base); + tcg_temp_free_i32(desc); + gen_set_label(over); + return true; +} + +static bool ldff_op(DisasContext *s, arg_r2nfvm *a, uint8_t seq) +{ + uint32_t data = 0; + gen_helper_ldst_us *fn; + static gen_helper_ldst_us * const fns[7][4] = { + { gen_helper_vlbff_v_b, gen_helper_vlbff_v_h, + gen_helper_vlbff_v_w, gen_helper_vlbff_v_d }, + { NULL, gen_helper_vlhff_v_h, + gen_helper_vlhff_v_w, gen_helper_vlhff_v_d }, + { NULL, NULL, + gen_helper_vlwff_v_w, gen_helper_vlwff_v_d }, + { gen_helper_vleff_v_b, gen_helper_vleff_v_h, + gen_helper_vleff_v_w, gen_helper_vleff_v_d }, + { gen_helper_vlbuff_v_b, gen_helper_vlbuff_v_h, + gen_helper_vlbuff_v_w, gen_helper_vlbuff_v_d }, + { NULL, gen_helper_vlhuff_v_h, + gen_helper_vlhuff_v_w, gen_helper_vlhuff_v_d }, + { NULL, NULL, + gen_helper_vlwuff_v_w, gen_helper_vlwuff_v_d } + }; + + fn = fns[seq][s->sew]; + if (fn == NULL) { + return false; + } + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, NF, a->nf); + return ldff_trans(a->rd, a->rs1, data, fn, s); +} + +GEN_VEXT_TRANS(vlbff_v, 0, r2nfvm, ldff_op, ld_us_check) +GEN_VEXT_TRANS(vlhff_v, 1, r2nfvm, ldff_op, ld_us_check) +GEN_VEXT_TRANS(vlwff_v, 2, r2nfvm, ldff_op, ld_us_check) +GEN_VEXT_TRANS(vleff_v, 3, r2nfvm, ldff_op, ld_us_check) +GEN_VEXT_TRANS(vlbuff_v, 4, r2nfvm, ldff_op, ld_us_check) +GEN_VEXT_TRANS(vlhuff_v, 5, r2nfvm, ldff_op, ld_us_check) +GEN_VEXT_TRANS(vlwuff_v, 6, r2nfvm, ldff_op, ld_us_check) + +/* + *** vector atomic operation + */ +typedef void gen_helper_amo(TCGv_ptr, TCGv_ptr, TCGv, TCGv_ptr, + TCGv_env, TCGv_i32); + +static bool amo_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, + uint32_t data, gen_helper_amo *fn, DisasContext *s) +{ + TCGv_ptr dest, mask, index; + TCGv base; + TCGv_i32 desc; + + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + index = tcg_temp_new_ptr(); + base = tcg_temp_new(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + gen_get_gpr(base, rs1); + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(index, cpu_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + fn(dest, mask, base, index, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_ptr(mask); + tcg_temp_free_ptr(index); + tcg_temp_free(base); + tcg_temp_free_i32(desc); + gen_set_label(over); + return true; +} + +static bool amo_op(DisasContext *s, arg_rwdvm *a, uint8_t seq) +{ + uint32_t data = 0; + gen_helper_amo *fn; + static gen_helper_amo *const fnsw[9] = { + /* no atomic operation */ + gen_helper_vamoswapw_v_w, + gen_helper_vamoaddw_v_w, + gen_helper_vamoxorw_v_w, + gen_helper_vamoandw_v_w, + gen_helper_vamoorw_v_w, + gen_helper_vamominw_v_w, + gen_helper_vamomaxw_v_w, + gen_helper_vamominuw_v_w, + gen_helper_vamomaxuw_v_w + }; +#ifdef TARGET_RISCV64 + static gen_helper_amo *const fnsd[18] = { + gen_helper_vamoswapw_v_d, + gen_helper_vamoaddw_v_d, + gen_helper_vamoxorw_v_d, + gen_helper_vamoandw_v_d, + gen_helper_vamoorw_v_d, + gen_helper_vamominw_v_d, + gen_helper_vamomaxw_v_d, + gen_helper_vamominuw_v_d, + gen_helper_vamomaxuw_v_d, + gen_helper_vamoswapd_v_d, + gen_helper_vamoaddd_v_d, + gen_helper_vamoxord_v_d, + gen_helper_vamoandd_v_d, + gen_helper_vamoord_v_d, + gen_helper_vamomind_v_d, + gen_helper_vamomaxd_v_d, + gen_helper_vamominud_v_d, + gen_helper_vamomaxud_v_d + }; +#endif + + if (tb_cflags(s->base.tb) & CF_PARALLEL) { + gen_helper_exit_atomic(cpu_env); + s->base.is_jmp = DISAS_NORETURN; + return true; + } else { + if (s->sew == 3) { +#ifdef TARGET_RISCV64 + fn = fnsd[seq]; +#else + /* Check done in amo_check(). */ + g_assert_not_reached(); +#endif + } else { + fn = fnsw[seq]; + } + } + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, WD, a->wd); + return amo_trans(a->rd, a->rs1, a->rs2, data, fn, s); +} +/* + * There are two rules check here. + * + * 1. SEW must be at least as wide as the AMO memory element size. + * + * 2. If SEW is greater than XLEN, an illegal instruction exception is raised. + */ +static bool amo_check(DisasContext *s, arg_rwdvm* a) +{ + return (!s->vill && has_ext(s, RVA) && + (!a->wd || vext_check_overlap_mask(s, a->rd, a->vm, false)) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + ((1 << s->sew) <= sizeof(target_ulong)) && + ((1 << s->sew) >= 4)); +} + +GEN_VEXT_TRANS(vamoswapw_v, 0, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamoaddw_v, 1, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamoxorw_v, 2, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamoandw_v, 3, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamoorw_v, 4, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamominw_v, 5, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamomaxw_v, 6, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamominuw_v, 7, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamomaxuw_v, 8, rwdvm, amo_op, amo_check) +#ifdef TARGET_RISCV64 +GEN_VEXT_TRANS(vamoswapd_v, 9, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamoaddd_v, 10, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamoxord_v, 11, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamoandd_v, 12, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamoord_v, 13, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamomind_v, 14, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamomaxd_v, 15, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamominud_v, 16, rwdvm, amo_op, amo_check) +GEN_VEXT_TRANS(vamomaxud_v, 17, rwdvm, amo_op, amo_check) +#endif + +/* + *** Vector Integer Arithmetic Instructions + */ +#define MAXSZ(s) (s->vlen >> (3 - s->lmul)) + +static bool opivv_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + vext_check_reg(s, a->rs1, false)); +} + +typedef void GVecGen3Fn(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t); + +static inline bool +do_opivv_gvec(DisasContext *s, arg_rmrr *a, GVecGen3Fn *gvec_fn, + gen_helper_gvec_4_ptr *fn) +{ + TCGLabel *over = gen_new_label(); + if (!opivv_check(s, a)) { + return false; + } + + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + if (a->vm && s->vl_eq_vlmax) { + gvec_fn(s->sew, vreg_ofs(s, a->rd), + vreg_ofs(s, a->rs2), vreg_ofs(s, a->rs1), + MAXSZ(s), MAXSZ(s)); + } else { + uint32_t data = 0; + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), + vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), + cpu_env, 0, s->vlen / 8, data, fn); + } + gen_set_label(over); + return true; +} + +/* OPIVV with GVEC IR */ +#define GEN_OPIVV_GVEC_TRANS(NAME, SUF) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_gvec_4_ptr * const fns[4] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ + }; \ + return do_opivv_gvec(s, a, tcg_gen_gvec_##SUF, fns[s->sew]); \ +} + +GEN_OPIVV_GVEC_TRANS(vadd_vv, add) +GEN_OPIVV_GVEC_TRANS(vsub_vv, sub) + +typedef void gen_helper_opivx(TCGv_ptr, TCGv_ptr, TCGv, TCGv_ptr, + TCGv_env, TCGv_i32); + +static bool opivx_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, uint32_t vm, + gen_helper_opivx *fn, DisasContext *s) +{ + TCGv_ptr dest, src2, mask; + TCGv src1; + TCGv_i32 desc; + uint32_t data = 0; + + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + src2 = tcg_temp_new_ptr(); + src1 = tcg_temp_new(); + gen_get_gpr(src1, rs1); + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + fn(dest, mask, src1, src2, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_ptr(mask); + tcg_temp_free_ptr(src2); + tcg_temp_free(src1); + tcg_temp_free_i32(desc); + gen_set_label(over); + return true; +} + +static bool opivx_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false)); +} + +typedef void GVecGen2sFn(unsigned, uint32_t, uint32_t, TCGv_i64, + uint32_t, uint32_t); + +static inline bool +do_opivx_gvec(DisasContext *s, arg_rmrr *a, GVecGen2sFn *gvec_fn, + gen_helper_opivx *fn) +{ + if (!opivx_check(s, a)) { + return false; + } + + if (a->vm && s->vl_eq_vlmax) { + TCGv_i64 src1 = tcg_temp_new_i64(); + TCGv tmp = tcg_temp_new(); + + gen_get_gpr(tmp, a->rs1); + tcg_gen_ext_tl_i64(src1, tmp); + gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), + src1, MAXSZ(s), MAXSZ(s)); + + tcg_temp_free_i64(src1); + tcg_temp_free(tmp); + return true; + } + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); +} + +/* OPIVX with GVEC IR */ +#define GEN_OPIVX_GVEC_TRANS(NAME, SUF) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_opivx * const fns[4] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ + }; \ + return do_opivx_gvec(s, a, tcg_gen_gvec_##SUF, fns[s->sew]); \ +} + +GEN_OPIVX_GVEC_TRANS(vadd_vx, adds) +GEN_OPIVX_GVEC_TRANS(vsub_vx, subs) + +static void gen_vec_rsub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_vec_sub8_i64(d, b, a); +} + +static void gen_vec_rsub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_vec_sub8_i64(d, b, a); +} + +static void gen_rsub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_sub_i32(ret, arg2, arg1); +} + +static void gen_rsub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_sub_i64(ret, arg2, arg1); +} + +static void gen_rsub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_sub_vec(vece, r, b, a); +} + +static void tcg_gen_gvec_rsubs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen2s rsub_op[4] = { + { .fni8 = gen_vec_rsub8_i64, + .fniv = gen_rsub_vec, + .fno = gen_helper_vec_rsubs8, + .vece = MO_8 }, + { .fni8 = gen_vec_rsub16_i64, + .fniv = gen_rsub_vec, + .fno = gen_helper_vec_rsubs16, + .vece = MO_16 }, + { .fni4 = gen_rsub_i32, + .fniv = gen_rsub_vec, + .fno = gen_helper_vec_rsubs32, + .vece = MO_32 }, + { .fni8 = gen_rsub_i64, + .fniv = gen_rsub_vec, + .fno = gen_helper_vec_rsubs64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .vece = MO_64 }, + }; + + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_2s(dofs, aofs, oprsz, maxsz, c, &rsub_op[vece]); +} + +GEN_OPIVX_GVEC_TRANS(vrsub_vx, rsubs) + +static bool opivi_trans(uint32_t vd, uint32_t imm, uint32_t vs2, uint32_t vm, + gen_helper_opivx *fn, DisasContext *s, int zx) +{ + TCGv_ptr dest, src2, mask; + TCGv src1; + TCGv_i32 desc; + uint32_t data = 0; + + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + src2 = tcg_temp_new_ptr(); + if (zx) { + src1 = tcg_const_tl(imm); + } else { + src1 = tcg_const_tl(sextract64(imm, 0, 5)); + } + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + fn(dest, mask, src1, src2, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_ptr(mask); + tcg_temp_free_ptr(src2); + tcg_temp_free(src1); + tcg_temp_free_i32(desc); + gen_set_label(over); + return true; +} + +typedef void GVecGen2iFn(unsigned, uint32_t, uint32_t, int64_t, + uint32_t, uint32_t); + +static inline bool +do_opivi_gvec(DisasContext *s, arg_rmrr *a, GVecGen2iFn *gvec_fn, + gen_helper_opivx *fn, int zx) +{ + if (!opivx_check(s, a)) { + return false; + } + + if (a->vm && s->vl_eq_vlmax) { + if (zx) { + gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), + extract64(a->rs1, 0, 5), MAXSZ(s), MAXSZ(s)); + } else { + gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), + sextract64(a->rs1, 0, 5), MAXSZ(s), MAXSZ(s)); + } + } else { + return opivi_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s, zx); + } + return true; +} + +/* OPIVI with GVEC IR */ +#define GEN_OPIVI_GVEC_TRANS(NAME, ZX, OPIVX, SUF) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_opivx * const fns[4] = { \ + gen_helper_##OPIVX##_b, gen_helper_##OPIVX##_h, \ + gen_helper_##OPIVX##_w, gen_helper_##OPIVX##_d, \ + }; \ + return do_opivi_gvec(s, a, tcg_gen_gvec_##SUF, \ + fns[s->sew], ZX); \ +} + +GEN_OPIVI_GVEC_TRANS(vadd_vi, 0, vadd_vx, addi) + +static void tcg_gen_gvec_rsubi(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz) +{ + TCGv_i64 tmp = tcg_const_i64(c); + tcg_gen_gvec_rsubs(vece, dofs, aofs, tmp, oprsz, maxsz); + tcg_temp_free_i64(tmp); +} + +GEN_OPIVI_GVEC_TRANS(vrsub_vi, 0, vrsub_vx, rsubi) + +/* Vector Widening Integer Add/Subtract */ + +/* OPIVV with WIDEN */ +static bool opivv_widen_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, false) && + vext_check_reg(s, a->rs1, false) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs2, + 1 << s->lmul) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs1, + 1 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3)); +} + +static bool do_opivv_widen(DisasContext *s, arg_rmrr *a, + gen_helper_gvec_4_ptr *fn, + bool (*checkfn)(DisasContext *, arg_rmrr *)) +{ + if (checkfn(s, a)) { + uint32_t data = 0; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), + vreg_ofs(s, a->rs1), + vreg_ofs(s, a->rs2), + cpu_env, 0, s->vlen / 8, + data, fn); + gen_set_label(over); + return true; + } + return false; +} + +#define GEN_OPIVV_WIDEN_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_gvec_4_ptr * const fns[3] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w \ + }; \ + return do_opivv_widen(s, a, fns[s->sew], CHECK); \ +} + +GEN_OPIVV_WIDEN_TRANS(vwaddu_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwadd_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwsubu_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwsub_vv, opivv_widen_check) + +/* OPIVX with WIDEN */ +static bool opivx_widen_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, false) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs2, + 1 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3)); +} + +static bool do_opivx_widen(DisasContext *s, arg_rmrr *a, + gen_helper_opivx *fn) +{ + if (opivx_widen_check(s, a)) { + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); + } + return true; +} + +#define GEN_OPIVX_WIDEN_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_opivx * const fns[3] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w \ + }; \ + return do_opivx_widen(s, a, fns[s->sew]); \ +} + +GEN_OPIVX_WIDEN_TRANS(vwaddu_vx) +GEN_OPIVX_WIDEN_TRANS(vwadd_vx) +GEN_OPIVX_WIDEN_TRANS(vwsubu_vx) +GEN_OPIVX_WIDEN_TRANS(vwsub_vx) + +/* WIDEN OPIVV with WIDEN */ +static bool opiwv_widen_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, true) && + vext_check_reg(s, a->rs1, false) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs1, + 1 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3)); +} + +static bool do_opiwv_widen(DisasContext *s, arg_rmrr *a, + gen_helper_gvec_4_ptr *fn) +{ + if (opiwv_widen_check(s, a)) { + uint32_t data = 0; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), + vreg_ofs(s, a->rs1), + vreg_ofs(s, a->rs2), + cpu_env, 0, s->vlen / 8, data, fn); + gen_set_label(over); + return true; + } + return false; +} + +#define GEN_OPIWV_WIDEN_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_gvec_4_ptr * const fns[3] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w \ + }; \ + return do_opiwv_widen(s, a, fns[s->sew]); \ +} + +GEN_OPIWV_WIDEN_TRANS(vwaddu_wv) +GEN_OPIWV_WIDEN_TRANS(vwadd_wv) +GEN_OPIWV_WIDEN_TRANS(vwsubu_wv) +GEN_OPIWV_WIDEN_TRANS(vwsub_wv) + +/* WIDEN OPIVX with WIDEN */ +static bool opiwx_widen_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, true) && + (s->lmul < 0x3) && (s->sew < 0x3)); +} + +static bool do_opiwx_widen(DisasContext *s, arg_rmrr *a, + gen_helper_opivx *fn) +{ + if (opiwx_widen_check(s, a)) { + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); + } + return false; +} + +#define GEN_OPIWX_WIDEN_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_opivx * const fns[3] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w \ + }; \ + return do_opiwx_widen(s, a, fns[s->sew]); \ +} + +GEN_OPIWX_WIDEN_TRANS(vwaddu_wx) +GEN_OPIWX_WIDEN_TRANS(vwadd_wx) +GEN_OPIWX_WIDEN_TRANS(vwsubu_wx) +GEN_OPIWX_WIDEN_TRANS(vwsub_wx) + +/* Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions */ +/* OPIVV without GVEC IR */ +#define GEN_OPIVV_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_4_ptr * const fns[4] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ + }; \ + TCGLabel *over = gen_new_label(); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs1), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fns[s->sew]); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} + +/* + * For vadc and vsbc, an illegal instruction exception is raised if the + * destination vector register is v0 and LMUL > 1. (Section 12.3) + */ +static bool opivv_vadc_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + vext_check_reg(s, a->rs1, false) && + ((a->rd != 0) || (s->lmul == 0))); +} + +GEN_OPIVV_TRANS(vadc_vvm, opivv_vadc_check) +GEN_OPIVV_TRANS(vsbc_vvm, opivv_vadc_check) + +/* + * For vmadc and vmsbc, an illegal instruction exception is raised if the + * destination vector register overlaps a source vector register group. + */ +static bool opivv_vmadc_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rs2, false) && + vext_check_reg(s, a->rs1, false) && + vext_check_overlap_group(a->rd, 1, a->rs1, 1 << s->lmul) && + vext_check_overlap_group(a->rd, 1, a->rs2, 1 << s->lmul)); +} + +GEN_OPIVV_TRANS(vmadc_vvm, opivv_vmadc_check) +GEN_OPIVV_TRANS(vmsbc_vvm, opivv_vmadc_check) + +static bool opivx_vadc_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + ((a->rd != 0) || (s->lmul == 0))); +} + +/* OPIVX without GVEC IR */ +#define GEN_OPIVX_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + static gen_helper_opivx * const fns[4] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ + }; \ + \ + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fns[s->sew], s);\ + } \ + return false; \ +} + +GEN_OPIVX_TRANS(vadc_vxm, opivx_vadc_check) +GEN_OPIVX_TRANS(vsbc_vxm, opivx_vadc_check) + +static bool opivx_vmadc_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rs2, false) && + vext_check_overlap_group(a->rd, 1, a->rs2, 1 << s->lmul)); +} + +GEN_OPIVX_TRANS(vmadc_vxm, opivx_vmadc_check) +GEN_OPIVX_TRANS(vmsbc_vxm, opivx_vmadc_check) + +/* OPIVI without GVEC IR */ +#define GEN_OPIVI_TRANS(NAME, ZX, OPIVX, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + static gen_helper_opivx * const fns[4] = { \ + gen_helper_##OPIVX##_b, gen_helper_##OPIVX##_h, \ + gen_helper_##OPIVX##_w, gen_helper_##OPIVX##_d, \ + }; \ + return opivi_trans(a->rd, a->rs1, a->rs2, a->vm, \ + fns[s->sew], s, ZX); \ + } \ + return false; \ +} + +GEN_OPIVI_TRANS(vadc_vim, 0, vadc_vxm, opivx_vadc_check) +GEN_OPIVI_TRANS(vmadc_vim, 0, vmadc_vxm, opivx_vmadc_check) + +/* Vector Bitwise Logical Instructions */ +GEN_OPIVV_GVEC_TRANS(vand_vv, and) +GEN_OPIVV_GVEC_TRANS(vor_vv, or) +GEN_OPIVV_GVEC_TRANS(vxor_vv, xor) +GEN_OPIVX_GVEC_TRANS(vand_vx, ands) +GEN_OPIVX_GVEC_TRANS(vor_vx, ors) +GEN_OPIVX_GVEC_TRANS(vxor_vx, xors) +GEN_OPIVI_GVEC_TRANS(vand_vi, 0, vand_vx, andi) +GEN_OPIVI_GVEC_TRANS(vor_vi, 0, vor_vx, ori) +GEN_OPIVI_GVEC_TRANS(vxor_vi, 0, vxor_vx, xori) + +/* Vector Single-Width Bit Shift Instructions */ +GEN_OPIVV_GVEC_TRANS(vsll_vv, shlv) +GEN_OPIVV_GVEC_TRANS(vsrl_vv, shrv) +GEN_OPIVV_GVEC_TRANS(vsra_vv, sarv) + +typedef void GVecGen2sFn32(unsigned, uint32_t, uint32_t, TCGv_i32, + uint32_t, uint32_t); + +static inline bool +do_opivx_gvec_shift(DisasContext *s, arg_rmrr *a, GVecGen2sFn32 *gvec_fn, + gen_helper_opivx *fn) +{ + if (!opivx_check(s, a)) { + return false; + } + + if (a->vm && s->vl_eq_vlmax) { + TCGv_i32 src1 = tcg_temp_new_i32(); + TCGv tmp = tcg_temp_new(); + + gen_get_gpr(tmp, a->rs1); + tcg_gen_trunc_tl_i32(src1, tmp); + tcg_gen_extract_i32(src1, src1, 0, s->sew + 3); + gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), + src1, MAXSZ(s), MAXSZ(s)); + + tcg_temp_free_i32(src1); + tcg_temp_free(tmp); + return true; + } + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); +} + +#define GEN_OPIVX_GVEC_SHIFT_TRANS(NAME, SUF) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_opivx * const fns[4] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ + }; \ + \ + return do_opivx_gvec_shift(s, a, tcg_gen_gvec_##SUF, fns[s->sew]); \ +} + +GEN_OPIVX_GVEC_SHIFT_TRANS(vsll_vx, shls) +GEN_OPIVX_GVEC_SHIFT_TRANS(vsrl_vx, shrs) +GEN_OPIVX_GVEC_SHIFT_TRANS(vsra_vx, sars) + +GEN_OPIVI_GVEC_TRANS(vsll_vi, 1, vsll_vx, shli) +GEN_OPIVI_GVEC_TRANS(vsrl_vi, 1, vsrl_vx, shri) +GEN_OPIVI_GVEC_TRANS(vsra_vi, 1, vsra_vx, sari) + +/* Vector Narrowing Integer Right Shift Instructions */ +static bool opivv_narrow_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, true) && + vext_check_reg(s, a->rs1, false) && + vext_check_overlap_group(a->rd, 1 << s->lmul, a->rs2, + 2 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3)); +} + +/* OPIVV with NARROW */ +#define GEN_OPIVV_NARROW_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (opivv_narrow_check(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_4_ptr * const fns[3] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + }; \ + TCGLabel *over = gen_new_label(); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs1), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fns[s->sew]); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} +GEN_OPIVV_NARROW_TRANS(vnsra_vv) +GEN_OPIVV_NARROW_TRANS(vnsrl_vv) + +static bool opivx_narrow_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, true) && + vext_check_overlap_group(a->rd, 1 << s->lmul, a->rs2, + 2 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3)); +} + +/* OPIVX with NARROW */ +#define GEN_OPIVX_NARROW_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (opivx_narrow_check(s, a)) { \ + static gen_helper_opivx * const fns[3] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + }; \ + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fns[s->sew], s);\ + } \ + return false; \ +} + +GEN_OPIVX_NARROW_TRANS(vnsra_vx) +GEN_OPIVX_NARROW_TRANS(vnsrl_vx) + +/* OPIVI with NARROW */ +#define GEN_OPIVI_NARROW_TRANS(NAME, ZX, OPIVX) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (opivx_narrow_check(s, a)) { \ + static gen_helper_opivx * const fns[3] = { \ + gen_helper_##OPIVX##_b, \ + gen_helper_##OPIVX##_h, \ + gen_helper_##OPIVX##_w, \ + }; \ + return opivi_trans(a->rd, a->rs1, a->rs2, a->vm, \ + fns[s->sew], s, ZX); \ + } \ + return false; \ +} + +GEN_OPIVI_NARROW_TRANS(vnsra_vi, 1, vnsra_vx) +GEN_OPIVI_NARROW_TRANS(vnsrl_vi, 1, vnsrl_vx) + +/* Vector Integer Comparison Instructions */ +/* + * For all comparison instructions, an illegal instruction exception is raised + * if the destination vector register overlaps a source vector register group + * and LMUL > 1. + */ +static bool opivv_cmp_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rs2, false) && + vext_check_reg(s, a->rs1, false) && + ((vext_check_overlap_group(a->rd, 1, a->rs1, 1 << s->lmul) && + vext_check_overlap_group(a->rd, 1, a->rs2, 1 << s->lmul)) || + (s->lmul == 0))); +} +GEN_OPIVV_TRANS(vmseq_vv, opivv_cmp_check) +GEN_OPIVV_TRANS(vmsne_vv, opivv_cmp_check) +GEN_OPIVV_TRANS(vmsltu_vv, opivv_cmp_check) +GEN_OPIVV_TRANS(vmslt_vv, opivv_cmp_check) +GEN_OPIVV_TRANS(vmsleu_vv, opivv_cmp_check) +GEN_OPIVV_TRANS(vmsle_vv, opivv_cmp_check) + +static bool opivx_cmp_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rs2, false) && + (vext_check_overlap_group(a->rd, 1, a->rs2, 1 << s->lmul) || + (s->lmul == 0))); +} + +GEN_OPIVX_TRANS(vmseq_vx, opivx_cmp_check) +GEN_OPIVX_TRANS(vmsne_vx, opivx_cmp_check) +GEN_OPIVX_TRANS(vmsltu_vx, opivx_cmp_check) +GEN_OPIVX_TRANS(vmslt_vx, opivx_cmp_check) +GEN_OPIVX_TRANS(vmsleu_vx, opivx_cmp_check) +GEN_OPIVX_TRANS(vmsle_vx, opivx_cmp_check) +GEN_OPIVX_TRANS(vmsgtu_vx, opivx_cmp_check) +GEN_OPIVX_TRANS(vmsgt_vx, opivx_cmp_check) + +GEN_OPIVI_TRANS(vmseq_vi, 0, vmseq_vx, opivx_cmp_check) +GEN_OPIVI_TRANS(vmsne_vi, 0, vmsne_vx, opivx_cmp_check) +GEN_OPIVI_TRANS(vmsleu_vi, 1, vmsleu_vx, opivx_cmp_check) +GEN_OPIVI_TRANS(vmsle_vi, 0, vmsle_vx, opivx_cmp_check) +GEN_OPIVI_TRANS(vmsgtu_vi, 1, vmsgtu_vx, opivx_cmp_check) +GEN_OPIVI_TRANS(vmsgt_vi, 0, vmsgt_vx, opivx_cmp_check) + +/* Vector Integer Min/Max Instructions */ +GEN_OPIVV_GVEC_TRANS(vminu_vv, umin) +GEN_OPIVV_GVEC_TRANS(vmin_vv, smin) +GEN_OPIVV_GVEC_TRANS(vmaxu_vv, umax) +GEN_OPIVV_GVEC_TRANS(vmax_vv, smax) +GEN_OPIVX_TRANS(vminu_vx, opivx_check) +GEN_OPIVX_TRANS(vmin_vx, opivx_check) +GEN_OPIVX_TRANS(vmaxu_vx, opivx_check) +GEN_OPIVX_TRANS(vmax_vx, opivx_check) + +/* Vector Single-Width Integer Multiply Instructions */ +GEN_OPIVV_GVEC_TRANS(vmul_vv, mul) +GEN_OPIVV_TRANS(vmulh_vv, opivv_check) +GEN_OPIVV_TRANS(vmulhu_vv, opivv_check) +GEN_OPIVV_TRANS(vmulhsu_vv, opivv_check) +GEN_OPIVX_GVEC_TRANS(vmul_vx, muls) +GEN_OPIVX_TRANS(vmulh_vx, opivx_check) +GEN_OPIVX_TRANS(vmulhu_vx, opivx_check) +GEN_OPIVX_TRANS(vmulhsu_vx, opivx_check) + +/* Vector Integer Divide Instructions */ +GEN_OPIVV_TRANS(vdivu_vv, opivv_check) +GEN_OPIVV_TRANS(vdiv_vv, opivv_check) +GEN_OPIVV_TRANS(vremu_vv, opivv_check) +GEN_OPIVV_TRANS(vrem_vv, opivv_check) +GEN_OPIVX_TRANS(vdivu_vx, opivx_check) +GEN_OPIVX_TRANS(vdiv_vx, opivx_check) +GEN_OPIVX_TRANS(vremu_vx, opivx_check) +GEN_OPIVX_TRANS(vrem_vx, opivx_check) + +/* Vector Widening Integer Multiply Instructions */ +GEN_OPIVV_WIDEN_TRANS(vwmul_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmulu_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmulsu_vv, opivv_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmul_vx) +GEN_OPIVX_WIDEN_TRANS(vwmulu_vx) +GEN_OPIVX_WIDEN_TRANS(vwmulsu_vx) + +/* Vector Single-Width Integer Multiply-Add Instructions */ +GEN_OPIVV_TRANS(vmacc_vv, opivv_check) +GEN_OPIVV_TRANS(vnmsac_vv, opivv_check) +GEN_OPIVV_TRANS(vmadd_vv, opivv_check) +GEN_OPIVV_TRANS(vnmsub_vv, opivv_check) +GEN_OPIVX_TRANS(vmacc_vx, opivx_check) +GEN_OPIVX_TRANS(vnmsac_vx, opivx_check) +GEN_OPIVX_TRANS(vmadd_vx, opivx_check) +GEN_OPIVX_TRANS(vnmsub_vx, opivx_check) + +/* Vector Widening Integer Multiply-Add Instructions */ +GEN_OPIVV_WIDEN_TRANS(vwmaccu_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmacc_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmaccsu_vv, opivv_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccu_vx) +GEN_OPIVX_WIDEN_TRANS(vwmacc_vx) +GEN_OPIVX_WIDEN_TRANS(vwmaccsu_vx) +GEN_OPIVX_WIDEN_TRANS(vwmaccus_vx) + +/* Vector Integer Merge and Move Instructions */ +static bool trans_vmv_v_v(DisasContext *s, arg_vmv_v_v *a) +{ + if (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs1, false)) { + + if (s->vl_eq_vlmax) { + tcg_gen_gvec_mov(s->sew, vreg_ofs(s, a->rd), + vreg_ofs(s, a->rs1), + MAXSZ(s), MAXSZ(s)); + } else { + uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); + static gen_helper_gvec_2_ptr * const fns[4] = { + gen_helper_vmv_v_v_b, gen_helper_vmv_v_v_h, + gen_helper_vmv_v_v_w, gen_helper_vmv_v_v_d, + }; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs1), + cpu_env, 0, s->vlen / 8, data, fns[s->sew]); + gen_set_label(over); + } + return true; + } + return false; +} + +typedef void gen_helper_vmv_vx(TCGv_ptr, TCGv_i64, TCGv_env, TCGv_i32); +static bool trans_vmv_v_x(DisasContext *s, arg_vmv_v_x *a) +{ + if (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false)) { + + TCGv s1; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + s1 = tcg_temp_new(); + gen_get_gpr(s1, a->rs1); + + if (s->vl_eq_vlmax) { + tcg_gen_gvec_dup_tl(s->sew, vreg_ofs(s, a->rd), + MAXSZ(s), MAXSZ(s), s1); + } else { + TCGv_i32 desc ; + TCGv_i64 s1_i64 = tcg_temp_new_i64(); + TCGv_ptr dest = tcg_temp_new_ptr(); + uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); + static gen_helper_vmv_vx * const fns[4] = { + gen_helper_vmv_v_x_b, gen_helper_vmv_v_x_h, + gen_helper_vmv_v_x_w, gen_helper_vmv_v_x_d, + }; + + tcg_gen_ext_tl_i64(s1_i64, s1); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); + fns[s->sew](dest, s1_i64, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_i32(desc); + tcg_temp_free_i64(s1_i64); + } + + tcg_temp_free(s1); + gen_set_label(over); + return true; + } + return false; +} + +static bool trans_vmv_v_i(DisasContext *s, arg_vmv_v_i *a) +{ + if (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false)) { + + int64_t simm = sextract64(a->rs1, 0, 5); + if (s->vl_eq_vlmax) { + tcg_gen_gvec_dup_imm(s->sew, vreg_ofs(s, a->rd), + MAXSZ(s), MAXSZ(s), simm); + } else { + TCGv_i32 desc; + TCGv_i64 s1; + TCGv_ptr dest; + uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); + static gen_helper_vmv_vx * const fns[4] = { + gen_helper_vmv_v_x_b, gen_helper_vmv_v_x_h, + gen_helper_vmv_v_x_w, gen_helper_vmv_v_x_d, + }; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + s1 = tcg_const_i64(simm); + dest = tcg_temp_new_ptr(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); + fns[s->sew](dest, s1, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_i32(desc); + tcg_temp_free_i64(s1); + gen_set_label(over); + } + return true; + } + return false; +} + +GEN_OPIVV_TRANS(vmerge_vvm, opivv_vadc_check) +GEN_OPIVX_TRANS(vmerge_vxm, opivx_vadc_check) +GEN_OPIVI_TRANS(vmerge_vim, 0, vmerge_vxm, opivx_vadc_check) + +/* + *** Vector Fixed-Point Arithmetic Instructions + */ + +/* Vector Single-Width Saturating Add and Subtract */ +GEN_OPIVV_TRANS(vsaddu_vv, opivv_check) +GEN_OPIVV_TRANS(vsadd_vv, opivv_check) +GEN_OPIVV_TRANS(vssubu_vv, opivv_check) +GEN_OPIVV_TRANS(vssub_vv, opivv_check) +GEN_OPIVX_TRANS(vsaddu_vx, opivx_check) +GEN_OPIVX_TRANS(vsadd_vx, opivx_check) +GEN_OPIVX_TRANS(vssubu_vx, opivx_check) +GEN_OPIVX_TRANS(vssub_vx, opivx_check) +GEN_OPIVI_TRANS(vsaddu_vi, 1, vsaddu_vx, opivx_check) +GEN_OPIVI_TRANS(vsadd_vi, 0, vsadd_vx, opivx_check) + +/* Vector Single-Width Averaging Add and Subtract */ +GEN_OPIVV_TRANS(vaadd_vv, opivv_check) +GEN_OPIVV_TRANS(vasub_vv, opivv_check) +GEN_OPIVX_TRANS(vaadd_vx, opivx_check) +GEN_OPIVX_TRANS(vasub_vx, opivx_check) +GEN_OPIVI_TRANS(vaadd_vi, 0, vaadd_vx, opivx_check) + +/* Vector Single-Width Fractional Multiply with Rounding and Saturation */ +GEN_OPIVV_TRANS(vsmul_vv, opivv_check) +GEN_OPIVX_TRANS(vsmul_vx, opivx_check) + +/* Vector Widening Saturating Scaled Multiply-Add */ +GEN_OPIVV_WIDEN_TRANS(vwsmaccu_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwsmacc_vv, opivv_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwsmaccsu_vv, opivv_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwsmaccu_vx) +GEN_OPIVX_WIDEN_TRANS(vwsmacc_vx) +GEN_OPIVX_WIDEN_TRANS(vwsmaccsu_vx) +GEN_OPIVX_WIDEN_TRANS(vwsmaccus_vx) + +/* Vector Single-Width Scaling Shift Instructions */ +GEN_OPIVV_TRANS(vssrl_vv, opivv_check) +GEN_OPIVV_TRANS(vssra_vv, opivv_check) +GEN_OPIVX_TRANS(vssrl_vx, opivx_check) +GEN_OPIVX_TRANS(vssra_vx, opivx_check) +GEN_OPIVI_TRANS(vssrl_vi, 1, vssrl_vx, opivx_check) +GEN_OPIVI_TRANS(vssra_vi, 0, vssra_vx, opivx_check) + +/* Vector Narrowing Fixed-Point Clip Instructions */ +GEN_OPIVV_NARROW_TRANS(vnclipu_vv) +GEN_OPIVV_NARROW_TRANS(vnclip_vv) +GEN_OPIVX_NARROW_TRANS(vnclipu_vx) +GEN_OPIVX_NARROW_TRANS(vnclip_vx) +GEN_OPIVI_NARROW_TRANS(vnclipu_vi, 1, vnclipu_vx) +GEN_OPIVI_NARROW_TRANS(vnclip_vi, 1, vnclip_vx) + +/* + *** Vector Float Point Arithmetic Instructions + */ +/* Vector Single-Width Floating-Point Add/Subtract Instructions */ + +/* + * If the current SEW does not correspond to a supported IEEE floating-point + * type, an illegal instruction exception is raised. + */ +static bool opfvv_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + vext_check_reg(s, a->rs1, false) && + (s->sew != 0)); +} + +/* OPFVV without GVEC IR */ +#define GEN_OPFVV_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_4_ptr * const fns[3] = { \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + gen_helper_##NAME##_d, \ + }; \ + TCGLabel *over = gen_new_label(); \ + gen_set_rm(s, 7); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs1), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fns[s->sew - 1]); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} +GEN_OPFVV_TRANS(vfadd_vv, opfvv_check) +GEN_OPFVV_TRANS(vfsub_vv, opfvv_check) + +typedef void gen_helper_opfvf(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_ptr, + TCGv_env, TCGv_i32); + +static bool opfvf_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, + uint32_t data, gen_helper_opfvf *fn, DisasContext *s) +{ + TCGv_ptr dest, src2, mask; + TCGv_i32 desc; + + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + src2 = tcg_temp_new_ptr(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + fn(dest, mask, cpu_fpr[rs1], src2, cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_ptr(mask); + tcg_temp_free_ptr(src2); + tcg_temp_free_i32(desc); + gen_set_label(over); + return true; +} + +static bool opfvf_check(DisasContext *s, arg_rmrr *a) +{ +/* + * If the current SEW does not correspond to a supported IEEE floating-point + * type, an illegal instruction exception is raised + */ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + (s->sew != 0)); +} + +/* OPFVF without GVEC IR */ +#define GEN_OPFVF_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_opfvf *const fns[3] = { \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + gen_helper_##NAME##_d, \ + }; \ + gen_set_rm(s, 7); \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ + fns[s->sew - 1], s); \ + } \ + return false; \ +} + +GEN_OPFVF_TRANS(vfadd_vf, opfvf_check) +GEN_OPFVF_TRANS(vfsub_vf, opfvf_check) +GEN_OPFVF_TRANS(vfrsub_vf, opfvf_check) + +/* Vector Widening Floating-Point Add/Subtract Instructions */ +static bool opfvv_widen_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, false) && + vext_check_reg(s, a->rs1, false) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs2, + 1 << s->lmul) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs1, + 1 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3) && (s->sew != 0)); +} + +/* OPFVV with WIDEN */ +#define GEN_OPFVV_WIDEN_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_4_ptr * const fns[2] = { \ + gen_helper_##NAME##_h, gen_helper_##NAME##_w, \ + }; \ + TCGLabel *over = gen_new_label(); \ + gen_set_rm(s, 7); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs1), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fns[s->sew - 1]); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} + +GEN_OPFVV_WIDEN_TRANS(vfwadd_vv, opfvv_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwsub_vv, opfvv_widen_check) + +static bool opfvf_widen_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, false) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs2, + 1 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3) && (s->sew != 0)); +} + +/* OPFVF with WIDEN */ +#define GEN_OPFVF_WIDEN_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (opfvf_widen_check(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_opfvf *const fns[2] = { \ + gen_helper_##NAME##_h, gen_helper_##NAME##_w, \ + }; \ + gen_set_rm(s, 7); \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ + fns[s->sew - 1], s); \ + } \ + return false; \ +} + +GEN_OPFVF_WIDEN_TRANS(vfwadd_vf) +GEN_OPFVF_WIDEN_TRANS(vfwsub_vf) + +static bool opfwv_widen_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, true) && + vext_check_reg(s, a->rs1, false) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs1, + 1 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3) && (s->sew != 0)); +} + +/* WIDEN OPFVV with WIDEN */ +#define GEN_OPFWV_WIDEN_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (opfwv_widen_check(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_4_ptr * const fns[2] = { \ + gen_helper_##NAME##_h, gen_helper_##NAME##_w, \ + }; \ + TCGLabel *over = gen_new_label(); \ + gen_set_rm(s, 7); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs1), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fns[s->sew - 1]); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} + +GEN_OPFWV_WIDEN_TRANS(vfwadd_wv) +GEN_OPFWV_WIDEN_TRANS(vfwsub_wv) + +static bool opfwf_widen_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, true) && + (s->lmul < 0x3) && (s->sew < 0x3) && (s->sew != 0)); +} + +/* WIDEN OPFVF with WIDEN */ +#define GEN_OPFWF_WIDEN_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (opfwf_widen_check(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_opfvf *const fns[2] = { \ + gen_helper_##NAME##_h, gen_helper_##NAME##_w, \ + }; \ + gen_set_rm(s, 7); \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ + fns[s->sew - 1], s); \ + } \ + return false; \ +} + +GEN_OPFWF_WIDEN_TRANS(vfwadd_wf) +GEN_OPFWF_WIDEN_TRANS(vfwsub_wf) + +/* Vector Single-Width Floating-Point Multiply/Divide Instructions */ +GEN_OPFVV_TRANS(vfmul_vv, opfvv_check) +GEN_OPFVV_TRANS(vfdiv_vv, opfvv_check) +GEN_OPFVF_TRANS(vfmul_vf, opfvf_check) +GEN_OPFVF_TRANS(vfdiv_vf, opfvf_check) +GEN_OPFVF_TRANS(vfrdiv_vf, opfvf_check) + +/* Vector Widening Floating-Point Multiply */ +GEN_OPFVV_WIDEN_TRANS(vfwmul_vv, opfvv_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwmul_vf) + +/* Vector Single-Width Floating-Point Fused Multiply-Add Instructions */ +GEN_OPFVV_TRANS(vfmacc_vv, opfvv_check) +GEN_OPFVV_TRANS(vfnmacc_vv, opfvv_check) +GEN_OPFVV_TRANS(vfmsac_vv, opfvv_check) +GEN_OPFVV_TRANS(vfnmsac_vv, opfvv_check) +GEN_OPFVV_TRANS(vfmadd_vv, opfvv_check) +GEN_OPFVV_TRANS(vfnmadd_vv, opfvv_check) +GEN_OPFVV_TRANS(vfmsub_vv, opfvv_check) +GEN_OPFVV_TRANS(vfnmsub_vv, opfvv_check) +GEN_OPFVF_TRANS(vfmacc_vf, opfvf_check) +GEN_OPFVF_TRANS(vfnmacc_vf, opfvf_check) +GEN_OPFVF_TRANS(vfmsac_vf, opfvf_check) +GEN_OPFVF_TRANS(vfnmsac_vf, opfvf_check) +GEN_OPFVF_TRANS(vfmadd_vf, opfvf_check) +GEN_OPFVF_TRANS(vfnmadd_vf, opfvf_check) +GEN_OPFVF_TRANS(vfmsub_vf, opfvf_check) +GEN_OPFVF_TRANS(vfnmsub_vf, opfvf_check) + +/* Vector Widening Floating-Point Fused Multiply-Add Instructions */ +GEN_OPFVV_WIDEN_TRANS(vfwmacc_vv, opfvv_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwnmacc_vv, opfvv_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwmsac_vv, opfvv_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwnmsac_vv, opfvv_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwmacc_vf) +GEN_OPFVF_WIDEN_TRANS(vfwnmacc_vf) +GEN_OPFVF_WIDEN_TRANS(vfwmsac_vf) +GEN_OPFVF_WIDEN_TRANS(vfwnmsac_vf) + +/* Vector Floating-Point Square-Root Instruction */ + +/* + * If the current SEW does not correspond to a supported IEEE floating-point + * type, an illegal instruction exception is raised + */ +static bool opfv_check(DisasContext *s, arg_rmr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + (s->sew != 0)); +} + +#define GEN_OPFV_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ +{ \ + if (CHECK(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_3_ptr * const fns[3] = { \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + gen_helper_##NAME##_d, \ + }; \ + TCGLabel *over = gen_new_label(); \ + gen_set_rm(s, 7); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fns[s->sew - 1]); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} + +GEN_OPFV_TRANS(vfsqrt_v, opfv_check) + +/* Vector Floating-Point MIN/MAX Instructions */ +GEN_OPFVV_TRANS(vfmin_vv, opfvv_check) +GEN_OPFVV_TRANS(vfmax_vv, opfvv_check) +GEN_OPFVF_TRANS(vfmin_vf, opfvf_check) +GEN_OPFVF_TRANS(vfmax_vf, opfvf_check) + +/* Vector Floating-Point Sign-Injection Instructions */ +GEN_OPFVV_TRANS(vfsgnj_vv, opfvv_check) +GEN_OPFVV_TRANS(vfsgnjn_vv, opfvv_check) +GEN_OPFVV_TRANS(vfsgnjx_vv, opfvv_check) +GEN_OPFVF_TRANS(vfsgnj_vf, opfvf_check) +GEN_OPFVF_TRANS(vfsgnjn_vf, opfvf_check) +GEN_OPFVF_TRANS(vfsgnjx_vf, opfvf_check) + +/* Vector Floating-Point Compare Instructions */ +static bool opfvv_cmp_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rs2, false) && + vext_check_reg(s, a->rs1, false) && + (s->sew != 0) && + ((vext_check_overlap_group(a->rd, 1, a->rs1, 1 << s->lmul) && + vext_check_overlap_group(a->rd, 1, a->rs2, 1 << s->lmul)) || + (s->lmul == 0))); +} + +GEN_OPFVV_TRANS(vmfeq_vv, opfvv_cmp_check) +GEN_OPFVV_TRANS(vmfne_vv, opfvv_cmp_check) +GEN_OPFVV_TRANS(vmflt_vv, opfvv_cmp_check) +GEN_OPFVV_TRANS(vmfle_vv, opfvv_cmp_check) +GEN_OPFVV_TRANS(vmford_vv, opfvv_cmp_check) + +static bool opfvf_cmp_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rs2, false) && + (s->sew != 0) && + (vext_check_overlap_group(a->rd, 1, a->rs2, 1 << s->lmul) || + (s->lmul == 0))); +} + +GEN_OPFVF_TRANS(vmfeq_vf, opfvf_cmp_check) +GEN_OPFVF_TRANS(vmfne_vf, opfvf_cmp_check) +GEN_OPFVF_TRANS(vmflt_vf, opfvf_cmp_check) +GEN_OPFVF_TRANS(vmfle_vf, opfvf_cmp_check) +GEN_OPFVF_TRANS(vmfgt_vf, opfvf_cmp_check) +GEN_OPFVF_TRANS(vmfge_vf, opfvf_cmp_check) +GEN_OPFVF_TRANS(vmford_vf, opfvf_cmp_check) + +/* Vector Floating-Point Classify Instruction */ +GEN_OPFV_TRANS(vfclass_v, opfv_check) + +/* Vector Floating-Point Merge Instruction */ +GEN_OPFVF_TRANS(vfmerge_vfm, opfvf_check) + +static bool trans_vfmv_v_f(DisasContext *s, arg_vfmv_v_f *a) +{ + if (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + (s->sew != 0)) { + + if (s->vl_eq_vlmax) { + tcg_gen_gvec_dup_i64(s->sew, vreg_ofs(s, a->rd), + MAXSZ(s), MAXSZ(s), cpu_fpr[a->rs1]); + } else { + TCGv_ptr dest; + TCGv_i32 desc; + uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); + static gen_helper_vmv_vx * const fns[3] = { + gen_helper_vmv_v_x_h, + gen_helper_vmv_v_x_w, + gen_helper_vmv_v_x_d, + }; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + dest = tcg_temp_new_ptr(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); + fns[s->sew - 1](dest, cpu_fpr[a->rs1], cpu_env, desc); + + tcg_temp_free_ptr(dest); + tcg_temp_free_i32(desc); + gen_set_label(over); + } + return true; + } + return false; +} + +/* Single-Width Floating-Point/Integer Type-Convert Instructions */ +GEN_OPFV_TRANS(vfcvt_xu_f_v, opfv_check) +GEN_OPFV_TRANS(vfcvt_x_f_v, opfv_check) +GEN_OPFV_TRANS(vfcvt_f_xu_v, opfv_check) +GEN_OPFV_TRANS(vfcvt_f_x_v, opfv_check) + +/* Widening Floating-Point/Integer Type-Convert Instructions */ + +/* + * If the current SEW does not correspond to a supported IEEE floating-point + * type, an illegal instruction exception is raised + */ +static bool opfv_widen_check(DisasContext *s, arg_rmr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, true) && + vext_check_reg(s, a->rs2, false) && + vext_check_overlap_group(a->rd, 2 << s->lmul, a->rs2, + 1 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3) && (s->sew != 0)); +} + +#define GEN_OPFV_WIDEN_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ +{ \ + if (opfv_widen_check(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_3_ptr * const fns[2] = { \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + }; \ + TCGLabel *over = gen_new_label(); \ + gen_set_rm(s, 7); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fns[s->sew - 1]); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} + +GEN_OPFV_WIDEN_TRANS(vfwcvt_xu_f_v) +GEN_OPFV_WIDEN_TRANS(vfwcvt_x_f_v) +GEN_OPFV_WIDEN_TRANS(vfwcvt_f_xu_v) +GEN_OPFV_WIDEN_TRANS(vfwcvt_f_x_v) +GEN_OPFV_WIDEN_TRANS(vfwcvt_f_f_v) + +/* Narrowing Floating-Point/Integer Type-Convert Instructions */ + +/* + * If the current SEW does not correspond to a supported IEEE floating-point + * type, an illegal instruction exception is raised + */ +static bool opfv_narrow_check(DisasContext *s, arg_rmr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, false) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, true) && + vext_check_overlap_group(a->rd, 1 << s->lmul, a->rs2, + 2 << s->lmul) && + (s->lmul < 0x3) && (s->sew < 0x3) && (s->sew != 0)); +} + +#define GEN_OPFV_NARROW_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ +{ \ + if (opfv_narrow_check(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_3_ptr * const fns[2] = { \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + }; \ + TCGLabel *over = gen_new_label(); \ + gen_set_rm(s, 7); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fns[s->sew - 1]); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} + +GEN_OPFV_NARROW_TRANS(vfncvt_xu_f_v) +GEN_OPFV_NARROW_TRANS(vfncvt_x_f_v) +GEN_OPFV_NARROW_TRANS(vfncvt_f_xu_v) +GEN_OPFV_NARROW_TRANS(vfncvt_f_x_v) +GEN_OPFV_NARROW_TRANS(vfncvt_f_f_v) + +/* + *** Vector Reduction Operations + */ +/* Vector Single-Width Integer Reduction Instructions */ +static bool reduction_check(DisasContext *s, arg_rmrr *a) +{ + return vext_check_isa_ill(s) && vext_check_reg(s, a->rs2, false); +} + +GEN_OPIVV_TRANS(vredsum_vs, reduction_check) +GEN_OPIVV_TRANS(vredmaxu_vs, reduction_check) +GEN_OPIVV_TRANS(vredmax_vs, reduction_check) +GEN_OPIVV_TRANS(vredminu_vs, reduction_check) +GEN_OPIVV_TRANS(vredmin_vs, reduction_check) +GEN_OPIVV_TRANS(vredand_vs, reduction_check) +GEN_OPIVV_TRANS(vredor_vs, reduction_check) +GEN_OPIVV_TRANS(vredxor_vs, reduction_check) + +/* Vector Widening Integer Reduction Instructions */ +GEN_OPIVV_WIDEN_TRANS(vwredsum_vs, reduction_check) +GEN_OPIVV_WIDEN_TRANS(vwredsumu_vs, reduction_check) + +/* Vector Single-Width Floating-Point Reduction Instructions */ +GEN_OPFVV_TRANS(vfredsum_vs, reduction_check) +GEN_OPFVV_TRANS(vfredmax_vs, reduction_check) +GEN_OPFVV_TRANS(vfredmin_vs, reduction_check) + +/* Vector Widening Floating-Point Reduction Instructions */ +GEN_OPFVV_WIDEN_TRANS(vfwredsum_vs, reduction_check) + +/* + *** Vector Mask Operations + */ + +/* Vector Mask-Register Logical Instructions */ +#define GEN_MM_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_r *a) \ +{ \ + if (vext_check_isa_ill(s)) { \ + uint32_t data = 0; \ + gen_helper_gvec_4_ptr *fn = gen_helper_##NAME; \ + TCGLabel *over = gen_new_label(); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs1), \ + vreg_ofs(s, a->rs2), cpu_env, 0, \ + s->vlen / 8, data, fn); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} + +GEN_MM_TRANS(vmand_mm) +GEN_MM_TRANS(vmnand_mm) +GEN_MM_TRANS(vmandnot_mm) +GEN_MM_TRANS(vmxor_mm) +GEN_MM_TRANS(vmor_mm) +GEN_MM_TRANS(vmnor_mm) +GEN_MM_TRANS(vmornot_mm) +GEN_MM_TRANS(vmxnor_mm) + +/* Vector mask population count vmpopc */ +static bool trans_vmpopc_m(DisasContext *s, arg_rmr *a) +{ + if (vext_check_isa_ill(s)) { + TCGv_ptr src2, mask; + TCGv dst; + TCGv_i32 desc; + uint32_t data = 0; + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + + mask = tcg_temp_new_ptr(); + src2 = tcg_temp_new_ptr(); + dst = tcg_temp_new(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, a->rs2)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + gen_helper_vmpopc_m(dst, mask, src2, cpu_env, desc); + gen_set_gpr(a->rd, dst); + + tcg_temp_free_ptr(mask); + tcg_temp_free_ptr(src2); + tcg_temp_free(dst); + tcg_temp_free_i32(desc); + return true; + } + return false; +} + +/* vmfirst find-first-set mask bit */ +static bool trans_vmfirst_m(DisasContext *s, arg_rmr *a) +{ + if (vext_check_isa_ill(s)) { + TCGv_ptr src2, mask; + TCGv dst; + TCGv_i32 desc; + uint32_t data = 0; + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + + mask = tcg_temp_new_ptr(); + src2 = tcg_temp_new_ptr(); + dst = tcg_temp_new(); + desc = tcg_const_i32(simd_desc(0, s->vlen / 8, data)); + + tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, a->rs2)); + tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + + gen_helper_vmfirst_m(dst, mask, src2, cpu_env, desc); + gen_set_gpr(a->rd, dst); + + tcg_temp_free_ptr(mask); + tcg_temp_free_ptr(src2); + tcg_temp_free(dst); + tcg_temp_free_i32(desc); + return true; + } + return false; +} + +/* vmsbf.m set-before-first mask bit */ +/* vmsif.m set-includ-first mask bit */ +/* vmsof.m set-only-first mask bit */ +#define GEN_M_TRANS(NAME) \ +static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ +{ \ + if (vext_check_isa_ill(s)) { \ + uint32_t data = 0; \ + gen_helper_gvec_3_ptr *fn = gen_helper_##NAME; \ + TCGLabel *over = gen_new_label(); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + \ + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), \ + vreg_ofs(s, 0), vreg_ofs(s, a->rs2), \ + cpu_env, 0, s->vlen / 8, data, fn); \ + gen_set_label(over); \ + return true; \ + } \ + return false; \ +} + +GEN_M_TRANS(vmsbf_m) +GEN_M_TRANS(vmsif_m) +GEN_M_TRANS(vmsof_m) + +/* Vector Iota Instruction */ +static bool trans_viota_m(DisasContext *s, arg_viota_m *a) +{ + if (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_overlap_group(a->rd, 1 << s->lmul, a->rs2, 1) && + (a->vm != 0 || a->rd != 0)) { + uint32_t data = 0; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + static gen_helper_gvec_3_ptr * const fns[4] = { + gen_helper_viota_m_b, gen_helper_viota_m_h, + gen_helper_viota_m_w, gen_helper_viota_m_d, + }; + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), + vreg_ofs(s, a->rs2), cpu_env, 0, + s->vlen / 8, data, fns[s->sew]); + gen_set_label(over); + return true; + } + return false; +} + +/* Vector Element Index Instruction */ +static bool trans_vid_v(DisasContext *s, arg_vid_v *a) +{ + if (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_overlap_mask(s, a->rd, a->vm, false)) { + uint32_t data = 0; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + static gen_helper_gvec_2_ptr * const fns[4] = { + gen_helper_vid_v_b, gen_helper_vid_v_h, + gen_helper_vid_v_w, gen_helper_vid_v_d, + }; + tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), + cpu_env, 0, s->vlen / 8, data, fns[s->sew]); + gen_set_label(over); + return true; + } + return false; +} + +/* + *** Vector Permutation Instructions + */ + +/* Integer Extract Instruction */ + +static void load_element(TCGv_i64 dest, TCGv_ptr base, + int ofs, int sew) +{ + switch (sew) { + case MO_8: + tcg_gen_ld8u_i64(dest, base, ofs); + break; + case MO_16: + tcg_gen_ld16u_i64(dest, base, ofs); + break; + case MO_32: + tcg_gen_ld32u_i64(dest, base, ofs); + break; + case MO_64: + tcg_gen_ld_i64(dest, base, ofs); + break; + default: + g_assert_not_reached(); + break; + } +} + +/* offset of the idx element with base regsiter r */ +static uint32_t endian_ofs(DisasContext *s, int r, int idx) +{ +#ifdef HOST_WORDS_BIGENDIAN + return vreg_ofs(s, r) + ((idx ^ (7 >> s->sew)) << s->sew); +#else + return vreg_ofs(s, r) + (idx << s->sew); +#endif +} + +/* adjust the index according to the endian */ +static void endian_adjust(TCGv_i32 ofs, int sew) +{ +#ifdef HOST_WORDS_BIGENDIAN + tcg_gen_xori_i32(ofs, ofs, 7 >> sew); +#endif +} + +/* Load idx >= VLMAX ? 0 : vreg[idx] */ +static void vec_element_loadx(DisasContext *s, TCGv_i64 dest, + int vreg, TCGv idx, int vlmax) +{ + TCGv_i32 ofs = tcg_temp_new_i32(); + TCGv_ptr base = tcg_temp_new_ptr(); + TCGv_i64 t_idx = tcg_temp_new_i64(); + TCGv_i64 t_vlmax, t_zero; + + /* + * Mask the index to the length so that we do + * not produce an out-of-range load. + */ + tcg_gen_trunc_tl_i32(ofs, idx); + tcg_gen_andi_i32(ofs, ofs, vlmax - 1); + + /* Convert the index to an offset. */ + endian_adjust(ofs, s->sew); + tcg_gen_shli_i32(ofs, ofs, s->sew); + + /* Convert the index to a pointer. */ + tcg_gen_ext_i32_ptr(base, ofs); + tcg_gen_add_ptr(base, base, cpu_env); + + /* Perform the load. */ + load_element(dest, base, + vreg_ofs(s, vreg), s->sew); + tcg_temp_free_ptr(base); + tcg_temp_free_i32(ofs); + + /* Flush out-of-range indexing to zero. */ + t_vlmax = tcg_const_i64(vlmax); + t_zero = tcg_const_i64(0); + tcg_gen_extu_tl_i64(t_idx, idx); + + tcg_gen_movcond_i64(TCG_COND_LTU, dest, t_idx, + t_vlmax, dest, t_zero); + + tcg_temp_free_i64(t_vlmax); + tcg_temp_free_i64(t_zero); + tcg_temp_free_i64(t_idx); +} + +static void vec_element_loadi(DisasContext *s, TCGv_i64 dest, + int vreg, int idx) +{ + load_element(dest, cpu_env, endian_ofs(s, vreg, idx), s->sew); +} + +static bool trans_vext_x_v(DisasContext *s, arg_r *a) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv dest = tcg_temp_new(); + + if (a->rs1 == 0) { + /* Special case vmv.x.s rd, vs2. */ + vec_element_loadi(s, tmp, a->rs2, 0); + } else { + /* This instruction ignores LMUL and vector register groups */ + int vlmax = s->vlen >> (3 + s->sew); + vec_element_loadx(s, tmp, a->rs2, cpu_gpr[a->rs1], vlmax); + } + tcg_gen_trunc_i64_tl(dest, tmp); + gen_set_gpr(a->rd, dest); + + tcg_temp_free(dest); + tcg_temp_free_i64(tmp); + return true; +} + +/* Integer Scalar Move Instruction */ + +static void store_element(TCGv_i64 val, TCGv_ptr base, + int ofs, int sew) +{ + switch (sew) { + case MO_8: + tcg_gen_st8_i64(val, base, ofs); + break; + case MO_16: + tcg_gen_st16_i64(val, base, ofs); + break; + case MO_32: + tcg_gen_st32_i64(val, base, ofs); + break; + case MO_64: + tcg_gen_st_i64(val, base, ofs); + break; + default: + g_assert_not_reached(); + break; + } +} + +/* + * Store vreg[idx] = val. + * The index must be in range of VLMAX. + */ +static void vec_element_storei(DisasContext *s, int vreg, + int idx, TCGv_i64 val) +{ + store_element(val, cpu_env, endian_ofs(s, vreg, idx), s->sew); +} + +/* vmv.s.x vd, rs1 # vd[0] = rs1 */ +static bool trans_vmv_s_x(DisasContext *s, arg_vmv_s_x *a) +{ + if (vext_check_isa_ill(s)) { + /* This instruction ignores LMUL and vector register groups */ + int maxsz = s->vlen >> 3; + TCGv_i64 t1; + TCGLabel *over = gen_new_label(); + + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + tcg_gen_gvec_dup_imm(SEW64, vreg_ofs(s, a->rd), maxsz, maxsz, 0); + if (a->rs1 == 0) { + goto done; + } + + t1 = tcg_temp_new_i64(); + tcg_gen_extu_tl_i64(t1, cpu_gpr[a->rs1]); + vec_element_storei(s, a->rd, 0, t1); + tcg_temp_free_i64(t1); + done: + gen_set_label(over); + return true; + } + return false; +} + +/* Floating-Point Scalar Move Instructions */ +static bool trans_vfmv_f_s(DisasContext *s, arg_vfmv_f_s *a) +{ + if (!s->vill && has_ext(s, RVF) && + (s->mstatus_fs != 0) && (s->sew != 0)) { + unsigned int len = 8 << s->sew; + + vec_element_loadi(s, cpu_fpr[a->rd], a->rs2, 0); + if (len < 64) { + tcg_gen_ori_i64(cpu_fpr[a->rd], cpu_fpr[a->rd], + MAKE_64BIT_MASK(len, 64 - len)); + } + + mark_fs_dirty(s); + return true; + } + return false; +} + +/* vfmv.s.f vd, rs1 # vd[0] = rs1 (vs2=0) */ +static bool trans_vfmv_s_f(DisasContext *s, arg_vfmv_s_f *a) +{ + if (!s->vill && has_ext(s, RVF) && (s->sew != 0)) { + TCGv_i64 t1; + /* The instructions ignore LMUL and vector register group. */ + uint32_t vlmax = s->vlen >> 3; + + /* if vl == 0, skip vector register write back */ + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + /* zeroed all elements */ + tcg_gen_gvec_dup_imm(SEW64, vreg_ofs(s, a->rd), vlmax, vlmax, 0); + + /* NaN-box f[rs1] as necessary for SEW */ + t1 = tcg_temp_new_i64(); + if (s->sew == MO_64 && !has_ext(s, RVD)) { + tcg_gen_ori_i64(t1, cpu_fpr[a->rs1], MAKE_64BIT_MASK(32, 32)); + } else { + tcg_gen_mov_i64(t1, cpu_fpr[a->rs1]); + } + vec_element_storei(s, a->rd, 0, t1); + tcg_temp_free_i64(t1); + gen_set_label(over); + return true; + } + return false; +} + +/* Vector Slide Instructions */ +static bool slideup_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + (a->rd != a->rs2)); +} + +GEN_OPIVX_TRANS(vslideup_vx, slideup_check) +GEN_OPIVX_TRANS(vslide1up_vx, slideup_check) +GEN_OPIVI_TRANS(vslideup_vi, 1, vslideup_vx, slideup_check) + +GEN_OPIVX_TRANS(vslidedown_vx, opivx_check) +GEN_OPIVX_TRANS(vslide1down_vx, opivx_check) +GEN_OPIVI_TRANS(vslidedown_vi, 1, vslidedown_vx, opivx_check) + +/* Vector Register Gather Instruction */ +static bool vrgather_vv_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs1, false) && + vext_check_reg(s, a->rs2, false) && + (a->rd != a->rs2) && (a->rd != a->rs1)); +} + +GEN_OPIVV_TRANS(vrgather_vv, vrgather_vv_check) + +static bool vrgather_vx_check(DisasContext *s, arg_rmrr *a) +{ + return (vext_check_isa_ill(s) && + vext_check_overlap_mask(s, a->rd, a->vm, true) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + (a->rd != a->rs2)); +} + +/* vrgather.vx vd, vs2, rs1, vm # vd[i] = (x[rs1] >= VLMAX) ? 0 : vs2[rs1] */ +static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a) +{ + if (!vrgather_vx_check(s, a)) { + return false; + } + + if (a->vm && s->vl_eq_vlmax) { + int vlmax = s->vlen / s->mlen; + TCGv_i64 dest = tcg_temp_new_i64(); + + if (a->rs1 == 0) { + vec_element_loadi(s, dest, a->rs2, 0); + } else { + vec_element_loadx(s, dest, a->rs2, cpu_gpr[a->rs1], vlmax); + } + + tcg_gen_gvec_dup_i64(s->sew, vreg_ofs(s, a->rd), + MAXSZ(s), MAXSZ(s), dest); + tcg_temp_free_i64(dest); + } else { + static gen_helper_opivx * const fns[4] = { + gen_helper_vrgather_vx_b, gen_helper_vrgather_vx_h, + gen_helper_vrgather_vx_w, gen_helper_vrgather_vx_d + }; + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fns[s->sew], s); + } + return true; +} + +/* vrgather.vi vd, vs2, imm, vm # vd[i] = (imm >= VLMAX) ? 0 : vs2[imm] */ +static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a) +{ + if (!vrgather_vx_check(s, a)) { + return false; + } + + if (a->vm && s->vl_eq_vlmax) { + if (a->rs1 >= s->vlen / s->mlen) { + tcg_gen_gvec_dup_imm(SEW64, vreg_ofs(s, a->rd), + MAXSZ(s), MAXSZ(s), 0); + } else { + tcg_gen_gvec_dup_mem(s->sew, vreg_ofs(s, a->rd), + endian_ofs(s, a->rs2, a->rs1), + MAXSZ(s), MAXSZ(s)); + } + } else { + static gen_helper_opivx * const fns[4] = { + gen_helper_vrgather_vx_b, gen_helper_vrgather_vx_h, + gen_helper_vrgather_vx_w, gen_helper_vrgather_vx_d + }; + return opivi_trans(a->rd, a->rs1, a->rs2, a->vm, fns[s->sew], s, 1); + } + return true; +} + +/* Vector Compress Instruction */ +static bool vcompress_vm_check(DisasContext *s, arg_r *a) +{ + return (vext_check_isa_ill(s) && + vext_check_reg(s, a->rd, false) && + vext_check_reg(s, a->rs2, false) && + vext_check_overlap_group(a->rd, 1 << s->lmul, a->rs1, 1) && + (a->rd != a->rs2)); +} + +static bool trans_vcompress_vm(DisasContext *s, arg_r *a) +{ + if (vcompress_vm_check(s, a)) { + uint32_t data = 0; + static gen_helper_gvec_4_ptr * const fns[4] = { + gen_helper_vcompress_vm_b, gen_helper_vcompress_vm_h, + gen_helper_vcompress_vm_w, gen_helper_vcompress_vm_d, + }; + TCGLabel *over = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + + data = FIELD_DP32(data, VDATA, MLEN, s->mlen); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), + vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), + cpu_env, 0, s->vlen / 8, data, fns[s->sew]); + gen_set_label(over); + return true; + } + return false; +} diff --git a/target/riscv/internals.h b/target/riscv/internals.h new file mode 100644 index 0000000000..37d33820ad --- /dev/null +++ b/target/riscv/internals.h @@ -0,0 +1,41 @@ +/* + * QEMU RISC-V CPU -- internal functions and types + * + * Copyright (c) 2020 T-Head Semiconductor Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RISCV_CPU_INTERNALS_H +#define RISCV_CPU_INTERNALS_H + +#include "hw/registerfields.h" + +/* share data between vector helpers and decode code */ +FIELD(VDATA, MLEN, 0, 8) +FIELD(VDATA, VM, 8, 1) +FIELD(VDATA, LMUL, 9, 2) +FIELD(VDATA, NF, 11, 4) +FIELD(VDATA, WD, 11, 1) + +/* float point classify helpers */ +target_ulong fclass_h(uint64_t frs1); +target_ulong fclass_s(uint64_t frs1); +target_ulong fclass_d(uint64_t frs1); + +#define SEW8 0 +#define SEW16 1 +#define SEW32 2 +#define SEW64 3 + +#endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c index ce71ca7a92..9632e79cf3 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -32,7 +32,7 @@ #include "instmap.h" /* global register indices */ -static TCGv cpu_gpr[32], cpu_pc; +static TCGv cpu_gpr[32], cpu_pc, cpu_vl; static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ static TCGv load_res; static TCGv load_val; @@ -56,6 +56,13 @@ typedef struct DisasContext { to reset this known value. */ int frm; bool ext_ifencei; + /* vector extension */ + bool vill; + uint8_t lmul; + uint8_t sew; + uint16_t vlen; + uint16_t mlen; + bool vl_eq_vlmax; } DisasContext; #ifdef TARGET_RISCV64 @@ -542,6 +549,11 @@ static void decode_RV32_64C(DisasContext *ctx, uint16_t opcode) } } +static int ex_plus_1(DisasContext *ctx, int nf) +{ + return nf + 1; +} + #define EX_SH(amount) \ static int ex_shift_##amount(DisasContext *ctx, int imm) \ { \ @@ -712,6 +724,7 @@ static bool gen_shift(DisasContext *ctx, arg_r *a, #include "insn_trans/trans_rvf.inc.c" #include "insn_trans/trans_rvd.inc.c" #include "insn_trans/trans_rvh.inc.c" +#include "insn_trans/trans_rvv.inc.c" #include "insn_trans/trans_privileged.inc.c" /* Include the auto-generated decoder for 16 bit insn */ @@ -746,10 +759,11 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cs->env_ptr; RISCVCPU *cpu = RISCV_CPU(cs); + uint32_t tb_flags = ctx->base.tb->flags; ctx->pc_succ_insn = ctx->base.pc_first; - ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; - ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS; + ctx->mem_idx = tb_flags & TB_FLAGS_MMU_MASK; + ctx->mstatus_fs = tb_flags & TB_FLAGS_MSTATUS_FS; ctx->priv_ver = env->priv_ver; #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVH)) { @@ -773,6 +787,12 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->misa = env->misa; ctx->frm = -1; /* unknown rounding mode */ ctx->ext_ifencei = cpu->cfg.ext_ifencei; + ctx->vlen = cpu->cfg.vlen; + ctx->vill = FIELD_EX32(tb_flags, TB_FLAGS, VILL); + ctx->sew = FIELD_EX32(tb_flags, TB_FLAGS, SEW); + ctx->lmul = FIELD_EX32(tb_flags, TB_FLAGS, LMUL); + ctx->mlen = 1 << (ctx->sew + 3 - ctx->lmul); + ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); } static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -887,6 +907,7 @@ void riscv_translate_init(void) } cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, pc), "pc"); + cpu_vl = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, vl), "vl"); load_res = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_res), "load_res"); load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val), diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c new file mode 100644 index 0000000000..39f44d1029 --- /dev/null +++ b/target/riscv/vector_helper.c @@ -0,0 +1,4899 @@ +/* + * RISC-V Vector Extension Helpers for QEMU. + * + * Copyright (c) 2020 T-Head Semiconductor Co., Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/memop.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "fpu/softfloat.h" +#include "tcg/tcg-gvec-desc.h" +#include "internals.h" +#include <math.h> + +target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, + target_ulong s2) +{ + int vlmax, vl; + RISCVCPU *cpu = env_archcpu(env); + uint16_t sew = 8 << FIELD_EX64(s2, VTYPE, VSEW); + uint8_t ediv = FIELD_EX64(s2, VTYPE, VEDIV); + bool vill = FIELD_EX64(s2, VTYPE, VILL); + target_ulong reserved = FIELD_EX64(s2, VTYPE, RESERVED); + + if ((sew > cpu->cfg.elen) || vill || (ediv != 0) || (reserved != 0)) { + /* only set vill bit. */ + env->vtype = FIELD_DP64(0, VTYPE, VILL, 1); + env->vl = 0; + env->vstart = 0; + return 0; + } + + vlmax = vext_get_vlmax(cpu, s2); + if (s1 <= vlmax) { + vl = s1; + } else { + vl = vlmax; + } + env->vl = vl; + env->vtype = s2; + env->vstart = 0; + return vl; +} + +/* + * Note that vector data is stored in host-endian 64-bit chunks, + * so addressing units smaller than that needs a host-endian fixup. + */ +#ifdef HOST_WORDS_BIGENDIAN +#define H1(x) ((x) ^ 7) +#define H1_2(x) ((x) ^ 6) +#define H1_4(x) ((x) ^ 4) +#define H2(x) ((x) ^ 3) +#define H4(x) ((x) ^ 1) +#define H8(x) ((x)) +#else +#define H1(x) (x) +#define H1_2(x) (x) +#define H1_4(x) (x) +#define H2(x) (x) +#define H4(x) (x) +#define H8(x) (x) +#endif + +static inline uint32_t vext_nf(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, NF); +} + +static inline uint32_t vext_mlen(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, MLEN); +} + +static inline uint32_t vext_vm(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, VM); +} + +static inline uint32_t vext_lmul(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, LMUL); +} + +static uint32_t vext_wd(uint32_t desc) +{ + return (simd_data(desc) >> 11) & 0x1; +} + +/* + * Get vector group length in bytes. Its range is [64, 2048]. + * + * As simd_desc support at most 256, the max vlen is 512 bits. + * So vlen in bytes is encoded as maxsz. + */ +static inline uint32_t vext_maxsz(uint32_t desc) +{ + return simd_maxsz(desc) << vext_lmul(desc); +} + +/* + * This function checks watchpoint before real load operation. + * + * In softmmu mode, the TLB API probe_access is enough for watchpoint check. + * In user mode, there is no watchpoint support now. + * + * It will trigger an exception if there is no mapping in TLB + * and page table walk can't fill the TLB entry. Then the guest + * software can return here after process the exception or never return. + */ +static void probe_pages(CPURISCVState *env, target_ulong addr, + target_ulong len, uintptr_t ra, + MMUAccessType access_type) +{ + target_ulong pagelen = -(addr | TARGET_PAGE_MASK); + target_ulong curlen = MIN(pagelen, len); + + probe_access(env, addr, curlen, access_type, + cpu_mmu_index(env, false), ra); + if (len > curlen) { + addr += curlen; + curlen = len - curlen; + probe_access(env, addr, curlen, access_type, + cpu_mmu_index(env, false), ra); + } +} + +#ifdef HOST_WORDS_BIGENDIAN +static void vext_clear(void *tail, uint32_t cnt, uint32_t tot) +{ + /* + * Split the remaining range to two parts. + * The first part is in the last uint64_t unit. + * The second part start from the next uint64_t unit. + */ + int part1 = 0, part2 = tot - cnt; + if (cnt % 8) { + part1 = 8 - (cnt % 8); + part2 = tot - cnt - part1; + memset((void *)((uintptr_t)tail & ~(7ULL)), 0, part1); + memset((void *)(((uintptr_t)tail + 8) & ~(7ULL)), 0, part2); + } else { + memset(tail, 0, part2); + } +} +#else +static void vext_clear(void *tail, uint32_t cnt, uint32_t tot) +{ + memset(tail, 0, tot - cnt); +} +#endif + +static void clearb(void *vd, uint32_t idx, uint32_t cnt, uint32_t tot) +{ + int8_t *cur = ((int8_t *)vd + H1(idx)); + vext_clear(cur, cnt, tot); +} + +static void clearh(void *vd, uint32_t idx, uint32_t cnt, uint32_t tot) +{ + int16_t *cur = ((int16_t *)vd + H2(idx)); + vext_clear(cur, cnt, tot); +} + +static void clearl(void *vd, uint32_t idx, uint32_t cnt, uint32_t tot) +{ + int32_t *cur = ((int32_t *)vd + H4(idx)); + vext_clear(cur, cnt, tot); +} + +static void clearq(void *vd, uint32_t idx, uint32_t cnt, uint32_t tot) +{ + int64_t *cur = (int64_t *)vd + idx; + vext_clear(cur, cnt, tot); +} + +static inline void vext_set_elem_mask(void *v0, int mlen, int index, + uint8_t value) +{ + int idx = (index * mlen) / 64; + int pos = (index * mlen) % 64; + uint64_t old = ((uint64_t *)v0)[idx]; + ((uint64_t *)v0)[idx] = deposit64(old, pos, mlen, value); +} + +static inline int vext_elem_mask(void *v0, int mlen, int index) +{ + int idx = (index * mlen) / 64; + int pos = (index * mlen) % 64; + return (((uint64_t *)v0)[idx] >> pos) & 1; +} + +/* elements operations for load and store */ +typedef void vext_ldst_elem_fn(CPURISCVState *env, target_ulong addr, + uint32_t idx, void *vd, uintptr_t retaddr); +typedef void clear_fn(void *vd, uint32_t idx, uint32_t cnt, uint32_t tot); + +#define GEN_VEXT_LD_ELEM(NAME, MTYPE, ETYPE, H, LDSUF) \ +static void NAME(CPURISCVState *env, abi_ptr addr, \ + uint32_t idx, void *vd, uintptr_t retaddr)\ +{ \ + MTYPE data; \ + ETYPE *cur = ((ETYPE *)vd + H(idx)); \ + data = cpu_##LDSUF##_data_ra(env, addr, retaddr); \ + *cur = data; \ +} \ + +GEN_VEXT_LD_ELEM(ldb_b, int8_t, int8_t, H1, ldsb) +GEN_VEXT_LD_ELEM(ldb_h, int8_t, int16_t, H2, ldsb) +GEN_VEXT_LD_ELEM(ldb_w, int8_t, int32_t, H4, ldsb) +GEN_VEXT_LD_ELEM(ldb_d, int8_t, int64_t, H8, ldsb) +GEN_VEXT_LD_ELEM(ldh_h, int16_t, int16_t, H2, ldsw) +GEN_VEXT_LD_ELEM(ldh_w, int16_t, int32_t, H4, ldsw) +GEN_VEXT_LD_ELEM(ldh_d, int16_t, int64_t, H8, ldsw) +GEN_VEXT_LD_ELEM(ldw_w, int32_t, int32_t, H4, ldl) +GEN_VEXT_LD_ELEM(ldw_d, int32_t, int64_t, H8, ldl) +GEN_VEXT_LD_ELEM(lde_b, int8_t, int8_t, H1, ldsb) +GEN_VEXT_LD_ELEM(lde_h, int16_t, int16_t, H2, ldsw) +GEN_VEXT_LD_ELEM(lde_w, int32_t, int32_t, H4, ldl) +GEN_VEXT_LD_ELEM(lde_d, int64_t, int64_t, H8, ldq) +GEN_VEXT_LD_ELEM(ldbu_b, uint8_t, uint8_t, H1, ldub) +GEN_VEXT_LD_ELEM(ldbu_h, uint8_t, uint16_t, H2, ldub) +GEN_VEXT_LD_ELEM(ldbu_w, uint8_t, uint32_t, H4, ldub) +GEN_VEXT_LD_ELEM(ldbu_d, uint8_t, uint64_t, H8, ldub) +GEN_VEXT_LD_ELEM(ldhu_h, uint16_t, uint16_t, H2, lduw) +GEN_VEXT_LD_ELEM(ldhu_w, uint16_t, uint32_t, H4, lduw) +GEN_VEXT_LD_ELEM(ldhu_d, uint16_t, uint64_t, H8, lduw) +GEN_VEXT_LD_ELEM(ldwu_w, uint32_t, uint32_t, H4, ldl) +GEN_VEXT_LD_ELEM(ldwu_d, uint32_t, uint64_t, H8, ldl) + +#define GEN_VEXT_ST_ELEM(NAME, ETYPE, H, STSUF) \ +static void NAME(CPURISCVState *env, abi_ptr addr, \ + uint32_t idx, void *vd, uintptr_t retaddr)\ +{ \ + ETYPE data = *((ETYPE *)vd + H(idx)); \ + cpu_##STSUF##_data_ra(env, addr, data, retaddr); \ +} + +GEN_VEXT_ST_ELEM(stb_b, int8_t, H1, stb) +GEN_VEXT_ST_ELEM(stb_h, int16_t, H2, stb) +GEN_VEXT_ST_ELEM(stb_w, int32_t, H4, stb) +GEN_VEXT_ST_ELEM(stb_d, int64_t, H8, stb) +GEN_VEXT_ST_ELEM(sth_h, int16_t, H2, stw) +GEN_VEXT_ST_ELEM(sth_w, int32_t, H4, stw) +GEN_VEXT_ST_ELEM(sth_d, int64_t, H8, stw) +GEN_VEXT_ST_ELEM(stw_w, int32_t, H4, stl) +GEN_VEXT_ST_ELEM(stw_d, int64_t, H8, stl) +GEN_VEXT_ST_ELEM(ste_b, int8_t, H1, stb) +GEN_VEXT_ST_ELEM(ste_h, int16_t, H2, stw) +GEN_VEXT_ST_ELEM(ste_w, int32_t, H4, stl) +GEN_VEXT_ST_ELEM(ste_d, int64_t, H8, stq) + +/* + *** stride: access vector element from strided memory + */ +static void +vext_ldst_stride(void *vd, void *v0, target_ulong base, + target_ulong stride, CPURISCVState *env, + uint32_t desc, uint32_t vm, + vext_ldst_elem_fn *ldst_elem, clear_fn *clear_elem, + uint32_t esz, uint32_t msz, uintptr_t ra, + MMUAccessType access_type) +{ + uint32_t i, k; + uint32_t nf = vext_nf(desc); + uint32_t mlen = vext_mlen(desc); + uint32_t vlmax = vext_maxsz(desc) / esz; + + /* probe every access*/ + for (i = 0; i < env->vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + probe_pages(env, base + stride * i, nf * msz, ra, access_type); + } + /* do real access */ + for (i = 0; i < env->vl; i++) { + k = 0; + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + while (k < nf) { + target_ulong addr = base + stride * i + k * msz; + ldst_elem(env, addr, i + k * vlmax, vd, ra); + k++; + } + } + /* clear tail elements */ + if (clear_elem) { + for (k = 0; k < nf; k++) { + clear_elem(vd, env->vl + k * vlmax, env->vl * esz, vlmax * esz); + } + } +} + +#define GEN_VEXT_LD_STRIDE(NAME, MTYPE, ETYPE, LOAD_FN, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void * v0, target_ulong base, \ + target_ulong stride, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t vm = vext_vm(desc); \ + vext_ldst_stride(vd, v0, base, stride, env, desc, vm, LOAD_FN, \ + CLEAR_FN, sizeof(ETYPE), sizeof(MTYPE), \ + GETPC(), MMU_DATA_LOAD); \ +} + +GEN_VEXT_LD_STRIDE(vlsb_v_b, int8_t, int8_t, ldb_b, clearb) +GEN_VEXT_LD_STRIDE(vlsb_v_h, int8_t, int16_t, ldb_h, clearh) +GEN_VEXT_LD_STRIDE(vlsb_v_w, int8_t, int32_t, ldb_w, clearl) +GEN_VEXT_LD_STRIDE(vlsb_v_d, int8_t, int64_t, ldb_d, clearq) +GEN_VEXT_LD_STRIDE(vlsh_v_h, int16_t, int16_t, ldh_h, clearh) +GEN_VEXT_LD_STRIDE(vlsh_v_w, int16_t, int32_t, ldh_w, clearl) +GEN_VEXT_LD_STRIDE(vlsh_v_d, int16_t, int64_t, ldh_d, clearq) +GEN_VEXT_LD_STRIDE(vlsw_v_w, int32_t, int32_t, ldw_w, clearl) +GEN_VEXT_LD_STRIDE(vlsw_v_d, int32_t, int64_t, ldw_d, clearq) +GEN_VEXT_LD_STRIDE(vlse_v_b, int8_t, int8_t, lde_b, clearb) +GEN_VEXT_LD_STRIDE(vlse_v_h, int16_t, int16_t, lde_h, clearh) +GEN_VEXT_LD_STRIDE(vlse_v_w, int32_t, int32_t, lde_w, clearl) +GEN_VEXT_LD_STRIDE(vlse_v_d, int64_t, int64_t, lde_d, clearq) +GEN_VEXT_LD_STRIDE(vlsbu_v_b, uint8_t, uint8_t, ldbu_b, clearb) +GEN_VEXT_LD_STRIDE(vlsbu_v_h, uint8_t, uint16_t, ldbu_h, clearh) +GEN_VEXT_LD_STRIDE(vlsbu_v_w, uint8_t, uint32_t, ldbu_w, clearl) +GEN_VEXT_LD_STRIDE(vlsbu_v_d, uint8_t, uint64_t, ldbu_d, clearq) +GEN_VEXT_LD_STRIDE(vlshu_v_h, uint16_t, uint16_t, ldhu_h, clearh) +GEN_VEXT_LD_STRIDE(vlshu_v_w, uint16_t, uint32_t, ldhu_w, clearl) +GEN_VEXT_LD_STRIDE(vlshu_v_d, uint16_t, uint64_t, ldhu_d, clearq) +GEN_VEXT_LD_STRIDE(vlswu_v_w, uint32_t, uint32_t, ldwu_w, clearl) +GEN_VEXT_LD_STRIDE(vlswu_v_d, uint32_t, uint64_t, ldwu_d, clearq) + +#define GEN_VEXT_ST_STRIDE(NAME, MTYPE, ETYPE, STORE_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ + target_ulong stride, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t vm = vext_vm(desc); \ + vext_ldst_stride(vd, v0, base, stride, env, desc, vm, STORE_FN, \ + NULL, sizeof(ETYPE), sizeof(MTYPE), \ + GETPC(), MMU_DATA_STORE); \ +} + +GEN_VEXT_ST_STRIDE(vssb_v_b, int8_t, int8_t, stb_b) +GEN_VEXT_ST_STRIDE(vssb_v_h, int8_t, int16_t, stb_h) +GEN_VEXT_ST_STRIDE(vssb_v_w, int8_t, int32_t, stb_w) +GEN_VEXT_ST_STRIDE(vssb_v_d, int8_t, int64_t, stb_d) +GEN_VEXT_ST_STRIDE(vssh_v_h, int16_t, int16_t, sth_h) +GEN_VEXT_ST_STRIDE(vssh_v_w, int16_t, int32_t, sth_w) +GEN_VEXT_ST_STRIDE(vssh_v_d, int16_t, int64_t, sth_d) +GEN_VEXT_ST_STRIDE(vssw_v_w, int32_t, int32_t, stw_w) +GEN_VEXT_ST_STRIDE(vssw_v_d, int32_t, int64_t, stw_d) +GEN_VEXT_ST_STRIDE(vsse_v_b, int8_t, int8_t, ste_b) +GEN_VEXT_ST_STRIDE(vsse_v_h, int16_t, int16_t, ste_h) +GEN_VEXT_ST_STRIDE(vsse_v_w, int32_t, int32_t, ste_w) +GEN_VEXT_ST_STRIDE(vsse_v_d, int64_t, int64_t, ste_d) + +/* + *** unit-stride: access elements stored contiguously in memory + */ + +/* unmasked unit-stride load and store operation*/ +static void +vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, + vext_ldst_elem_fn *ldst_elem, clear_fn *clear_elem, + uint32_t esz, uint32_t msz, uintptr_t ra, + MMUAccessType access_type) +{ + uint32_t i, k; + uint32_t nf = vext_nf(desc); + uint32_t vlmax = vext_maxsz(desc) / esz; + + /* probe every access */ + probe_pages(env, base, env->vl * nf * msz, ra, access_type); + /* load bytes from guest memory */ + for (i = 0; i < env->vl; i++) { + k = 0; + while (k < nf) { + target_ulong addr = base + (i * nf + k) * msz; + ldst_elem(env, addr, i + k * vlmax, vd, ra); + k++; + } + } + /* clear tail elements */ + if (clear_elem) { + for (k = 0; k < nf; k++) { + clear_elem(vd, env->vl + k * vlmax, env->vl * esz, vlmax * esz); + } + } +} + +/* + * masked unit-stride load and store operation will be a special case of stride, + * stride = NF * sizeof (MTYPE) + */ + +#define GEN_VEXT_LD_US(NAME, MTYPE, ETYPE, LOAD_FN, CLEAR_FN) \ +void HELPER(NAME##_mask)(void *vd, void *v0, target_ulong base, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t stride = vext_nf(desc) * sizeof(MTYPE); \ + vext_ldst_stride(vd, v0, base, stride, env, desc, false, LOAD_FN, \ + CLEAR_FN, sizeof(ETYPE), sizeof(MTYPE), \ + GETPC(), MMU_DATA_LOAD); \ +} \ + \ +void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vext_ldst_us(vd, base, env, desc, LOAD_FN, CLEAR_FN, \ + sizeof(ETYPE), sizeof(MTYPE), GETPC(), MMU_DATA_LOAD); \ +} + +GEN_VEXT_LD_US(vlb_v_b, int8_t, int8_t, ldb_b, clearb) +GEN_VEXT_LD_US(vlb_v_h, int8_t, int16_t, ldb_h, clearh) +GEN_VEXT_LD_US(vlb_v_w, int8_t, int32_t, ldb_w, clearl) +GEN_VEXT_LD_US(vlb_v_d, int8_t, int64_t, ldb_d, clearq) +GEN_VEXT_LD_US(vlh_v_h, int16_t, int16_t, ldh_h, clearh) +GEN_VEXT_LD_US(vlh_v_w, int16_t, int32_t, ldh_w, clearl) +GEN_VEXT_LD_US(vlh_v_d, int16_t, int64_t, ldh_d, clearq) +GEN_VEXT_LD_US(vlw_v_w, int32_t, int32_t, ldw_w, clearl) +GEN_VEXT_LD_US(vlw_v_d, int32_t, int64_t, ldw_d, clearq) +GEN_VEXT_LD_US(vle_v_b, int8_t, int8_t, lde_b, clearb) +GEN_VEXT_LD_US(vle_v_h, int16_t, int16_t, lde_h, clearh) +GEN_VEXT_LD_US(vle_v_w, int32_t, int32_t, lde_w, clearl) +GEN_VEXT_LD_US(vle_v_d, int64_t, int64_t, lde_d, clearq) +GEN_VEXT_LD_US(vlbu_v_b, uint8_t, uint8_t, ldbu_b, clearb) +GEN_VEXT_LD_US(vlbu_v_h, uint8_t, uint16_t, ldbu_h, clearh) +GEN_VEXT_LD_US(vlbu_v_w, uint8_t, uint32_t, ldbu_w, clearl) +GEN_VEXT_LD_US(vlbu_v_d, uint8_t, uint64_t, ldbu_d, clearq) +GEN_VEXT_LD_US(vlhu_v_h, uint16_t, uint16_t, ldhu_h, clearh) +GEN_VEXT_LD_US(vlhu_v_w, uint16_t, uint32_t, ldhu_w, clearl) +GEN_VEXT_LD_US(vlhu_v_d, uint16_t, uint64_t, ldhu_d, clearq) +GEN_VEXT_LD_US(vlwu_v_w, uint32_t, uint32_t, ldwu_w, clearl) +GEN_VEXT_LD_US(vlwu_v_d, uint32_t, uint64_t, ldwu_d, clearq) + +#define GEN_VEXT_ST_US(NAME, MTYPE, ETYPE, STORE_FN) \ +void HELPER(NAME##_mask)(void *vd, void *v0, target_ulong base, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t stride = vext_nf(desc) * sizeof(MTYPE); \ + vext_ldst_stride(vd, v0, base, stride, env, desc, false, STORE_FN, \ + NULL, sizeof(ETYPE), sizeof(MTYPE), \ + GETPC(), MMU_DATA_STORE); \ +} \ + \ +void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vext_ldst_us(vd, base, env, desc, STORE_FN, NULL, \ + sizeof(ETYPE), sizeof(MTYPE), GETPC(), MMU_DATA_STORE);\ +} + +GEN_VEXT_ST_US(vsb_v_b, int8_t, int8_t , stb_b) +GEN_VEXT_ST_US(vsb_v_h, int8_t, int16_t, stb_h) +GEN_VEXT_ST_US(vsb_v_w, int8_t, int32_t, stb_w) +GEN_VEXT_ST_US(vsb_v_d, int8_t, int64_t, stb_d) +GEN_VEXT_ST_US(vsh_v_h, int16_t, int16_t, sth_h) +GEN_VEXT_ST_US(vsh_v_w, int16_t, int32_t, sth_w) +GEN_VEXT_ST_US(vsh_v_d, int16_t, int64_t, sth_d) +GEN_VEXT_ST_US(vsw_v_w, int32_t, int32_t, stw_w) +GEN_VEXT_ST_US(vsw_v_d, int32_t, int64_t, stw_d) +GEN_VEXT_ST_US(vse_v_b, int8_t, int8_t , ste_b) +GEN_VEXT_ST_US(vse_v_h, int16_t, int16_t, ste_h) +GEN_VEXT_ST_US(vse_v_w, int32_t, int32_t, ste_w) +GEN_VEXT_ST_US(vse_v_d, int64_t, int64_t, ste_d) + +/* + *** index: access vector element from indexed memory + */ +typedef target_ulong vext_get_index_addr(target_ulong base, + uint32_t idx, void *vs2); + +#define GEN_VEXT_GET_INDEX_ADDR(NAME, ETYPE, H) \ +static target_ulong NAME(target_ulong base, \ + uint32_t idx, void *vs2) \ +{ \ + return (base + *((ETYPE *)vs2 + H(idx))); \ +} + +GEN_VEXT_GET_INDEX_ADDR(idx_b, int8_t, H1) +GEN_VEXT_GET_INDEX_ADDR(idx_h, int16_t, H2) +GEN_VEXT_GET_INDEX_ADDR(idx_w, int32_t, H4) +GEN_VEXT_GET_INDEX_ADDR(idx_d, int64_t, H8) + +static inline void +vext_ldst_index(void *vd, void *v0, target_ulong base, + void *vs2, CPURISCVState *env, uint32_t desc, + vext_get_index_addr get_index_addr, + vext_ldst_elem_fn *ldst_elem, + clear_fn *clear_elem, + uint32_t esz, uint32_t msz, uintptr_t ra, + MMUAccessType access_type) +{ + uint32_t i, k; + uint32_t nf = vext_nf(desc); + uint32_t vm = vext_vm(desc); + uint32_t mlen = vext_mlen(desc); + uint32_t vlmax = vext_maxsz(desc) / esz; + + /* probe every access*/ + for (i = 0; i < env->vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + probe_pages(env, get_index_addr(base, i, vs2), nf * msz, ra, + access_type); + } + /* load bytes from guest memory */ + for (i = 0; i < env->vl; i++) { + k = 0; + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + while (k < nf) { + abi_ptr addr = get_index_addr(base, i, vs2) + k * msz; + ldst_elem(env, addr, i + k * vlmax, vd, ra); + k++; + } + } + /* clear tail elements */ + if (clear_elem) { + for (k = 0; k < nf; k++) { + clear_elem(vd, env->vl + k * vlmax, env->vl * esz, vlmax * esz); + } + } +} + +#define GEN_VEXT_LD_INDEX(NAME, MTYPE, ETYPE, INDEX_FN, LOAD_FN, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + vext_ldst_index(vd, v0, base, vs2, env, desc, INDEX_FN, \ + LOAD_FN, CLEAR_FN, sizeof(ETYPE), sizeof(MTYPE), \ + GETPC(), MMU_DATA_LOAD); \ +} + +GEN_VEXT_LD_INDEX(vlxb_v_b, int8_t, int8_t, idx_b, ldb_b, clearb) +GEN_VEXT_LD_INDEX(vlxb_v_h, int8_t, int16_t, idx_h, ldb_h, clearh) +GEN_VEXT_LD_INDEX(vlxb_v_w, int8_t, int32_t, idx_w, ldb_w, clearl) +GEN_VEXT_LD_INDEX(vlxb_v_d, int8_t, int64_t, idx_d, ldb_d, clearq) +GEN_VEXT_LD_INDEX(vlxh_v_h, int16_t, int16_t, idx_h, ldh_h, clearh) +GEN_VEXT_LD_INDEX(vlxh_v_w, int16_t, int32_t, idx_w, ldh_w, clearl) +GEN_VEXT_LD_INDEX(vlxh_v_d, int16_t, int64_t, idx_d, ldh_d, clearq) +GEN_VEXT_LD_INDEX(vlxw_v_w, int32_t, int32_t, idx_w, ldw_w, clearl) +GEN_VEXT_LD_INDEX(vlxw_v_d, int32_t, int64_t, idx_d, ldw_d, clearq) +GEN_VEXT_LD_INDEX(vlxe_v_b, int8_t, int8_t, idx_b, lde_b, clearb) +GEN_VEXT_LD_INDEX(vlxe_v_h, int16_t, int16_t, idx_h, lde_h, clearh) +GEN_VEXT_LD_INDEX(vlxe_v_w, int32_t, int32_t, idx_w, lde_w, clearl) +GEN_VEXT_LD_INDEX(vlxe_v_d, int64_t, int64_t, idx_d, lde_d, clearq) +GEN_VEXT_LD_INDEX(vlxbu_v_b, uint8_t, uint8_t, idx_b, ldbu_b, clearb) +GEN_VEXT_LD_INDEX(vlxbu_v_h, uint8_t, uint16_t, idx_h, ldbu_h, clearh) +GEN_VEXT_LD_INDEX(vlxbu_v_w, uint8_t, uint32_t, idx_w, ldbu_w, clearl) +GEN_VEXT_LD_INDEX(vlxbu_v_d, uint8_t, uint64_t, idx_d, ldbu_d, clearq) +GEN_VEXT_LD_INDEX(vlxhu_v_h, uint16_t, uint16_t, idx_h, ldhu_h, clearh) +GEN_VEXT_LD_INDEX(vlxhu_v_w, uint16_t, uint32_t, idx_w, ldhu_w, clearl) +GEN_VEXT_LD_INDEX(vlxhu_v_d, uint16_t, uint64_t, idx_d, ldhu_d, clearq) +GEN_VEXT_LD_INDEX(vlxwu_v_w, uint32_t, uint32_t, idx_w, ldwu_w, clearl) +GEN_VEXT_LD_INDEX(vlxwu_v_d, uint32_t, uint64_t, idx_d, ldwu_d, clearq) + +#define GEN_VEXT_ST_INDEX(NAME, MTYPE, ETYPE, INDEX_FN, STORE_FN)\ +void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + vext_ldst_index(vd, v0, base, vs2, env, desc, INDEX_FN, \ + STORE_FN, NULL, sizeof(ETYPE), sizeof(MTYPE),\ + GETPC(), MMU_DATA_STORE); \ +} + +GEN_VEXT_ST_INDEX(vsxb_v_b, int8_t, int8_t, idx_b, stb_b) +GEN_VEXT_ST_INDEX(vsxb_v_h, int8_t, int16_t, idx_h, stb_h) +GEN_VEXT_ST_INDEX(vsxb_v_w, int8_t, int32_t, idx_w, stb_w) +GEN_VEXT_ST_INDEX(vsxb_v_d, int8_t, int64_t, idx_d, stb_d) +GEN_VEXT_ST_INDEX(vsxh_v_h, int16_t, int16_t, idx_h, sth_h) +GEN_VEXT_ST_INDEX(vsxh_v_w, int16_t, int32_t, idx_w, sth_w) +GEN_VEXT_ST_INDEX(vsxh_v_d, int16_t, int64_t, idx_d, sth_d) +GEN_VEXT_ST_INDEX(vsxw_v_w, int32_t, int32_t, idx_w, stw_w) +GEN_VEXT_ST_INDEX(vsxw_v_d, int32_t, int64_t, idx_d, stw_d) +GEN_VEXT_ST_INDEX(vsxe_v_b, int8_t, int8_t, idx_b, ste_b) +GEN_VEXT_ST_INDEX(vsxe_v_h, int16_t, int16_t, idx_h, ste_h) +GEN_VEXT_ST_INDEX(vsxe_v_w, int32_t, int32_t, idx_w, ste_w) +GEN_VEXT_ST_INDEX(vsxe_v_d, int64_t, int64_t, idx_d, ste_d) + +/* + *** unit-stride fault-only-fisrt load instructions + */ +static inline void +vext_ldff(void *vd, void *v0, target_ulong base, + CPURISCVState *env, uint32_t desc, + vext_ldst_elem_fn *ldst_elem, + clear_fn *clear_elem, + uint32_t esz, uint32_t msz, uintptr_t ra) +{ + void *host; + uint32_t i, k, vl = 0; + uint32_t mlen = vext_mlen(desc); + uint32_t nf = vext_nf(desc); + uint32_t vm = vext_vm(desc); + uint32_t vlmax = vext_maxsz(desc) / esz; + target_ulong addr, offset, remain; + + /* probe every access*/ + for (i = 0; i < env->vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + addr = base + nf * i * msz; + if (i == 0) { + probe_pages(env, addr, nf * msz, ra, MMU_DATA_LOAD); + } else { + /* if it triggers an exception, no need to check watchpoint */ + remain = nf * msz; + while (remain > 0) { + offset = -(addr | TARGET_PAGE_MASK); + host = tlb_vaddr_to_host(env, addr, MMU_DATA_LOAD, + cpu_mmu_index(env, false)); + if (host) { +#ifdef CONFIG_USER_ONLY + if (page_check_range(addr, nf * msz, PAGE_READ) < 0) { + vl = i; + goto ProbeSuccess; + } +#else + probe_pages(env, addr, nf * msz, ra, MMU_DATA_LOAD); +#endif + } else { + vl = i; + goto ProbeSuccess; + } + if (remain <= offset) { + break; + } + remain -= offset; + addr += offset; + } + } + } +ProbeSuccess: + /* load bytes from guest memory */ + if (vl != 0) { + env->vl = vl; + } + for (i = 0; i < env->vl; i++) { + k = 0; + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + while (k < nf) { + target_ulong addr = base + (i * nf + k) * msz; + ldst_elem(env, addr, i + k * vlmax, vd, ra); + k++; + } + } + /* clear tail elements */ + if (vl != 0) { + return; + } + for (k = 0; k < nf; k++) { + clear_elem(vd, env->vl + k * vlmax, env->vl * esz, vlmax * esz); + } +} + +#define GEN_VEXT_LDFF(NAME, MTYPE, ETYPE, LOAD_FN, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vext_ldff(vd, v0, base, env, desc, LOAD_FN, CLEAR_FN, \ + sizeof(ETYPE), sizeof(MTYPE), GETPC()); \ +} + +GEN_VEXT_LDFF(vlbff_v_b, int8_t, int8_t, ldb_b, clearb) +GEN_VEXT_LDFF(vlbff_v_h, int8_t, int16_t, ldb_h, clearh) +GEN_VEXT_LDFF(vlbff_v_w, int8_t, int32_t, ldb_w, clearl) +GEN_VEXT_LDFF(vlbff_v_d, int8_t, int64_t, ldb_d, clearq) +GEN_VEXT_LDFF(vlhff_v_h, int16_t, int16_t, ldh_h, clearh) +GEN_VEXT_LDFF(vlhff_v_w, int16_t, int32_t, ldh_w, clearl) +GEN_VEXT_LDFF(vlhff_v_d, int16_t, int64_t, ldh_d, clearq) +GEN_VEXT_LDFF(vlwff_v_w, int32_t, int32_t, ldw_w, clearl) +GEN_VEXT_LDFF(vlwff_v_d, int32_t, int64_t, ldw_d, clearq) +GEN_VEXT_LDFF(vleff_v_b, int8_t, int8_t, lde_b, clearb) +GEN_VEXT_LDFF(vleff_v_h, int16_t, int16_t, lde_h, clearh) +GEN_VEXT_LDFF(vleff_v_w, int32_t, int32_t, lde_w, clearl) +GEN_VEXT_LDFF(vleff_v_d, int64_t, int64_t, lde_d, clearq) +GEN_VEXT_LDFF(vlbuff_v_b, uint8_t, uint8_t, ldbu_b, clearb) +GEN_VEXT_LDFF(vlbuff_v_h, uint8_t, uint16_t, ldbu_h, clearh) +GEN_VEXT_LDFF(vlbuff_v_w, uint8_t, uint32_t, ldbu_w, clearl) +GEN_VEXT_LDFF(vlbuff_v_d, uint8_t, uint64_t, ldbu_d, clearq) +GEN_VEXT_LDFF(vlhuff_v_h, uint16_t, uint16_t, ldhu_h, clearh) +GEN_VEXT_LDFF(vlhuff_v_w, uint16_t, uint32_t, ldhu_w, clearl) +GEN_VEXT_LDFF(vlhuff_v_d, uint16_t, uint64_t, ldhu_d, clearq) +GEN_VEXT_LDFF(vlwuff_v_w, uint32_t, uint32_t, ldwu_w, clearl) +GEN_VEXT_LDFF(vlwuff_v_d, uint32_t, uint64_t, ldwu_d, clearq) + +/* + *** Vector AMO Operations (Zvamo) + */ +typedef void vext_amo_noatomic_fn(void *vs3, target_ulong addr, + uint32_t wd, uint32_t idx, CPURISCVState *env, + uintptr_t retaddr); + +/* no atomic opreation for vector atomic insructions */ +#define DO_SWAP(N, M) (M) +#define DO_AND(N, M) (N & M) +#define DO_XOR(N, M) (N ^ M) +#define DO_OR(N, M) (N | M) +#define DO_ADD(N, M) (N + M) + +#define GEN_VEXT_AMO_NOATOMIC_OP(NAME, ESZ, MSZ, H, DO_OP, SUF) \ +static void \ +vext_##NAME##_noatomic_op(void *vs3, target_ulong addr, \ + uint32_t wd, uint32_t idx, \ + CPURISCVState *env, uintptr_t retaddr)\ +{ \ + typedef int##ESZ##_t ETYPE; \ + typedef int##MSZ##_t MTYPE; \ + typedef uint##MSZ##_t UMTYPE __attribute__((unused)); \ + ETYPE *pe3 = (ETYPE *)vs3 + H(idx); \ + MTYPE a = cpu_ld##SUF##_data(env, addr), b = *pe3; \ + \ + cpu_st##SUF##_data(env, addr, DO_OP(a, b)); \ + if (wd) { \ + *pe3 = a; \ + } \ +} + +/* Signed min/max */ +#define DO_MAX(N, M) ((N) >= (M) ? (N) : (M)) +#define DO_MIN(N, M) ((N) >= (M) ? (M) : (N)) + +/* Unsigned min/max */ +#define DO_MAXU(N, M) DO_MAX((UMTYPE)N, (UMTYPE)M) +#define DO_MINU(N, M) DO_MIN((UMTYPE)N, (UMTYPE)M) + +GEN_VEXT_AMO_NOATOMIC_OP(vamoswapw_v_w, 32, 32, H4, DO_SWAP, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoaddw_v_w, 32, 32, H4, DO_ADD, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoxorw_v_w, 32, 32, H4, DO_XOR, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoandw_v_w, 32, 32, H4, DO_AND, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoorw_v_w, 32, 32, H4, DO_OR, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamominw_v_w, 32, 32, H4, DO_MIN, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamomaxw_v_w, 32, 32, H4, DO_MAX, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamominuw_v_w, 32, 32, H4, DO_MINU, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamomaxuw_v_w, 32, 32, H4, DO_MAXU, l) +#ifdef TARGET_RISCV64 +GEN_VEXT_AMO_NOATOMIC_OP(vamoswapw_v_d, 64, 32, H8, DO_SWAP, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoswapd_v_d, 64, 64, H8, DO_SWAP, q) +GEN_VEXT_AMO_NOATOMIC_OP(vamoaddw_v_d, 64, 32, H8, DO_ADD, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoaddd_v_d, 64, 64, H8, DO_ADD, q) +GEN_VEXT_AMO_NOATOMIC_OP(vamoxorw_v_d, 64, 32, H8, DO_XOR, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoxord_v_d, 64, 64, H8, DO_XOR, q) +GEN_VEXT_AMO_NOATOMIC_OP(vamoandw_v_d, 64, 32, H8, DO_AND, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoandd_v_d, 64, 64, H8, DO_AND, q) +GEN_VEXT_AMO_NOATOMIC_OP(vamoorw_v_d, 64, 32, H8, DO_OR, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamoord_v_d, 64, 64, H8, DO_OR, q) +GEN_VEXT_AMO_NOATOMIC_OP(vamominw_v_d, 64, 32, H8, DO_MIN, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamomind_v_d, 64, 64, H8, DO_MIN, q) +GEN_VEXT_AMO_NOATOMIC_OP(vamomaxw_v_d, 64, 32, H8, DO_MAX, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamomaxd_v_d, 64, 64, H8, DO_MAX, q) +GEN_VEXT_AMO_NOATOMIC_OP(vamominuw_v_d, 64, 32, H8, DO_MINU, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamominud_v_d, 64, 64, H8, DO_MINU, q) +GEN_VEXT_AMO_NOATOMIC_OP(vamomaxuw_v_d, 64, 32, H8, DO_MAXU, l) +GEN_VEXT_AMO_NOATOMIC_OP(vamomaxud_v_d, 64, 64, H8, DO_MAXU, q) +#endif + +static inline void +vext_amo_noatomic(void *vs3, void *v0, target_ulong base, + void *vs2, CPURISCVState *env, uint32_t desc, + vext_get_index_addr get_index_addr, + vext_amo_noatomic_fn *noatomic_op, + clear_fn *clear_elem, + uint32_t esz, uint32_t msz, uintptr_t ra) +{ + uint32_t i; + target_long addr; + uint32_t wd = vext_wd(desc); + uint32_t vm = vext_vm(desc); + uint32_t mlen = vext_mlen(desc); + uint32_t vlmax = vext_maxsz(desc) / esz; + + for (i = 0; i < env->vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + probe_pages(env, get_index_addr(base, i, vs2), msz, ra, MMU_DATA_LOAD); + probe_pages(env, get_index_addr(base, i, vs2), msz, ra, MMU_DATA_STORE); + } + for (i = 0; i < env->vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + addr = get_index_addr(base, i, vs2); + noatomic_op(vs3, addr, wd, i, env, ra); + } + clear_elem(vs3, env->vl, env->vl * esz, vlmax * esz); +} + +#define GEN_VEXT_AMO(NAME, MTYPE, ETYPE, INDEX_FN, CLEAR_FN) \ +void HELPER(NAME)(void *vs3, void *v0, target_ulong base, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + vext_amo_noatomic(vs3, v0, base, vs2, env, desc, \ + INDEX_FN, vext_##NAME##_noatomic_op, \ + CLEAR_FN, sizeof(ETYPE), sizeof(MTYPE), \ + GETPC()); \ +} + +#ifdef TARGET_RISCV64 +GEN_VEXT_AMO(vamoswapw_v_d, int32_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoswapd_v_d, int64_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoaddw_v_d, int32_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoaddd_v_d, int64_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoxorw_v_d, int32_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoxord_v_d, int64_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoandw_v_d, int32_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoandd_v_d, int64_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoorw_v_d, int32_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamoord_v_d, int64_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamominw_v_d, int32_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamomind_v_d, int64_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamomaxw_v_d, int32_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamomaxd_v_d, int64_t, int64_t, idx_d, clearq) +GEN_VEXT_AMO(vamominuw_v_d, uint32_t, uint64_t, idx_d, clearq) +GEN_VEXT_AMO(vamominud_v_d, uint64_t, uint64_t, idx_d, clearq) +GEN_VEXT_AMO(vamomaxuw_v_d, uint32_t, uint64_t, idx_d, clearq) +GEN_VEXT_AMO(vamomaxud_v_d, uint64_t, uint64_t, idx_d, clearq) +#endif +GEN_VEXT_AMO(vamoswapw_v_w, int32_t, int32_t, idx_w, clearl) +GEN_VEXT_AMO(vamoaddw_v_w, int32_t, int32_t, idx_w, clearl) +GEN_VEXT_AMO(vamoxorw_v_w, int32_t, int32_t, idx_w, clearl) +GEN_VEXT_AMO(vamoandw_v_w, int32_t, int32_t, idx_w, clearl) +GEN_VEXT_AMO(vamoorw_v_w, int32_t, int32_t, idx_w, clearl) +GEN_VEXT_AMO(vamominw_v_w, int32_t, int32_t, idx_w, clearl) +GEN_VEXT_AMO(vamomaxw_v_w, int32_t, int32_t, idx_w, clearl) +GEN_VEXT_AMO(vamominuw_v_w, uint32_t, uint32_t, idx_w, clearl) +GEN_VEXT_AMO(vamomaxuw_v_w, uint32_t, uint32_t, idx_w, clearl) + +/* + *** Vector Integer Arithmetic Instructions + */ + +/* expand macro args before macro */ +#define RVVCALL(macro, ...) macro(__VA_ARGS__) + +/* (TD, T1, T2, TX1, TX2) */ +#define OP_SSS_B int8_t, int8_t, int8_t, int8_t, int8_t +#define OP_SSS_H int16_t, int16_t, int16_t, int16_t, int16_t +#define OP_SSS_W int32_t, int32_t, int32_t, int32_t, int32_t +#define OP_SSS_D int64_t, int64_t, int64_t, int64_t, int64_t +#define OP_UUU_B uint8_t, uint8_t, uint8_t, uint8_t, uint8_t +#define OP_UUU_H uint16_t, uint16_t, uint16_t, uint16_t, uint16_t +#define OP_UUU_W uint32_t, uint32_t, uint32_t, uint32_t, uint32_t +#define OP_UUU_D uint64_t, uint64_t, uint64_t, uint64_t, uint64_t +#define OP_SUS_B int8_t, uint8_t, int8_t, uint8_t, int8_t +#define OP_SUS_H int16_t, uint16_t, int16_t, uint16_t, int16_t +#define OP_SUS_W int32_t, uint32_t, int32_t, uint32_t, int32_t +#define OP_SUS_D int64_t, uint64_t, int64_t, uint64_t, int64_t +#define WOP_UUU_B uint16_t, uint8_t, uint8_t, uint16_t, uint16_t +#define WOP_UUU_H uint32_t, uint16_t, uint16_t, uint32_t, uint32_t +#define WOP_UUU_W uint64_t, uint32_t, uint32_t, uint64_t, uint64_t +#define WOP_SSS_B int16_t, int8_t, int8_t, int16_t, int16_t +#define WOP_SSS_H int32_t, int16_t, int16_t, int32_t, int32_t +#define WOP_SSS_W int64_t, int32_t, int32_t, int64_t, int64_t +#define WOP_SUS_B int16_t, uint8_t, int8_t, uint16_t, int16_t +#define WOP_SUS_H int32_t, uint16_t, int16_t, uint32_t, int32_t +#define WOP_SUS_W int64_t, uint32_t, int32_t, uint64_t, int64_t +#define WOP_SSU_B int16_t, int8_t, uint8_t, int16_t, uint16_t +#define WOP_SSU_H int32_t, int16_t, uint16_t, int32_t, uint32_t +#define WOP_SSU_W int64_t, int32_t, uint32_t, int64_t, uint64_t +#define NOP_SSS_B int8_t, int8_t, int16_t, int8_t, int16_t +#define NOP_SSS_H int16_t, int16_t, int32_t, int16_t, int32_t +#define NOP_SSS_W int32_t, int32_t, int64_t, int32_t, int64_t +#define NOP_UUU_B uint8_t, uint8_t, uint16_t, uint8_t, uint16_t +#define NOP_UUU_H uint16_t, uint16_t, uint32_t, uint16_t, uint32_t +#define NOP_UUU_W uint32_t, uint32_t, uint64_t, uint32_t, uint64_t + +/* operation of two vector elements */ +typedef void opivv2_fn(void *vd, void *vs1, void *vs2, int i); + +#define OPIVV2(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ +static void do_##NAME(void *vd, void *vs1, void *vs2, int i) \ +{ \ + TX1 s1 = *((T1 *)vs1 + HS1(i)); \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2, s1); \ +} +#define DO_SUB(N, M) (N - M) +#define DO_RSUB(N, M) (M - N) + +RVVCALL(OPIVV2, vadd_vv_b, OP_SSS_B, H1, H1, H1, DO_ADD) +RVVCALL(OPIVV2, vadd_vv_h, OP_SSS_H, H2, H2, H2, DO_ADD) +RVVCALL(OPIVV2, vadd_vv_w, OP_SSS_W, H4, H4, H4, DO_ADD) +RVVCALL(OPIVV2, vadd_vv_d, OP_SSS_D, H8, H8, H8, DO_ADD) +RVVCALL(OPIVV2, vsub_vv_b, OP_SSS_B, H1, H1, H1, DO_SUB) +RVVCALL(OPIVV2, vsub_vv_h, OP_SSS_H, H2, H2, H2, DO_SUB) +RVVCALL(OPIVV2, vsub_vv_w, OP_SSS_W, H4, H4, H4, DO_SUB) +RVVCALL(OPIVV2, vsub_vv_d, OP_SSS_D, H8, H8, H8, DO_SUB) + +static void do_vext_vv(void *vd, void *v0, void *vs1, void *vs2, + CPURISCVState *env, uint32_t desc, + uint32_t esz, uint32_t dsz, + opivv2_fn *fn, clear_fn *clearfn) +{ + uint32_t vlmax = vext_maxsz(desc) / esz; + uint32_t mlen = vext_mlen(desc); + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + uint32_t i; + + for (i = 0; i < vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + fn(vd, vs1, vs2, i); + } + clearfn(vd, vl, vl * dsz, vlmax * dsz); +} + +/* generate the helpers for OPIVV */ +#define GEN_VEXT_VV(NAME, ESZ, DSZ, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + do_vext_vv(vd, v0, vs1, vs2, env, desc, ESZ, DSZ, \ + do_##NAME, CLEAR_FN); \ +} + +GEN_VEXT_VV(vadd_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vadd_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vadd_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vadd_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vsub_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vsub_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vsub_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vsub_vv_d, 8, 8, clearq) + +typedef void opivx2_fn(void *vd, target_long s1, void *vs2, int i); + +/* + * (T1)s1 gives the real operator type. + * (TX1)(T1)s1 expands the operator type of widen or narrow operations. + */ +#define OPIVX2(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ +static void do_##NAME(void *vd, target_long s1, void *vs2, int i) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2, (TX1)(T1)s1); \ +} + +RVVCALL(OPIVX2, vadd_vx_b, OP_SSS_B, H1, H1, DO_ADD) +RVVCALL(OPIVX2, vadd_vx_h, OP_SSS_H, H2, H2, DO_ADD) +RVVCALL(OPIVX2, vadd_vx_w, OP_SSS_W, H4, H4, DO_ADD) +RVVCALL(OPIVX2, vadd_vx_d, OP_SSS_D, H8, H8, DO_ADD) +RVVCALL(OPIVX2, vsub_vx_b, OP_SSS_B, H1, H1, DO_SUB) +RVVCALL(OPIVX2, vsub_vx_h, OP_SSS_H, H2, H2, DO_SUB) +RVVCALL(OPIVX2, vsub_vx_w, OP_SSS_W, H4, H4, DO_SUB) +RVVCALL(OPIVX2, vsub_vx_d, OP_SSS_D, H8, H8, DO_SUB) +RVVCALL(OPIVX2, vrsub_vx_b, OP_SSS_B, H1, H1, DO_RSUB) +RVVCALL(OPIVX2, vrsub_vx_h, OP_SSS_H, H2, H2, DO_RSUB) +RVVCALL(OPIVX2, vrsub_vx_w, OP_SSS_W, H4, H4, DO_RSUB) +RVVCALL(OPIVX2, vrsub_vx_d, OP_SSS_D, H8, H8, DO_RSUB) + +static void do_vext_vx(void *vd, void *v0, target_long s1, void *vs2, + CPURISCVState *env, uint32_t desc, + uint32_t esz, uint32_t dsz, + opivx2_fn fn, clear_fn *clearfn) +{ + uint32_t vlmax = vext_maxsz(desc) / esz; + uint32_t mlen = vext_mlen(desc); + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + uint32_t i; + + for (i = 0; i < vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + fn(vd, s1, vs2, i); + } + clearfn(vd, vl, vl * dsz, vlmax * dsz); +} + +/* generate the helpers for OPIVX */ +#define GEN_VEXT_VX(NAME, ESZ, DSZ, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + do_vext_vx(vd, v0, s1, vs2, env, desc, ESZ, DSZ, \ + do_##NAME, CLEAR_FN); \ +} + +GEN_VEXT_VX(vadd_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vadd_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vadd_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vadd_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vsub_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vsub_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vsub_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vsub_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vrsub_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vrsub_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vrsub_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vrsub_vx_d, 8, 8, clearq) + +void HELPER(vec_rsubs8)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = (uint8_t)b - *(uint8_t *)(a + i); + } +} + +void HELPER(vec_rsubs16)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = (uint16_t)b - *(uint16_t *)(a + i); + } +} + +void HELPER(vec_rsubs32)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = (uint32_t)b - *(uint32_t *)(a + i); + } +} + +void HELPER(vec_rsubs64)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = b - *(uint64_t *)(a + i); + } +} + +/* Vector Widening Integer Add/Subtract */ +#define WOP_UUU_B uint16_t, uint8_t, uint8_t, uint16_t, uint16_t +#define WOP_UUU_H uint32_t, uint16_t, uint16_t, uint32_t, uint32_t +#define WOP_UUU_W uint64_t, uint32_t, uint32_t, uint64_t, uint64_t +#define WOP_SSS_B int16_t, int8_t, int8_t, int16_t, int16_t +#define WOP_SSS_H int32_t, int16_t, int16_t, int32_t, int32_t +#define WOP_SSS_W int64_t, int32_t, int32_t, int64_t, int64_t +#define WOP_WUUU_B uint16_t, uint8_t, uint16_t, uint16_t, uint16_t +#define WOP_WUUU_H uint32_t, uint16_t, uint32_t, uint32_t, uint32_t +#define WOP_WUUU_W uint64_t, uint32_t, uint64_t, uint64_t, uint64_t +#define WOP_WSSS_B int16_t, int8_t, int16_t, int16_t, int16_t +#define WOP_WSSS_H int32_t, int16_t, int32_t, int32_t, int32_t +#define WOP_WSSS_W int64_t, int32_t, int64_t, int64_t, int64_t +RVVCALL(OPIVV2, vwaddu_vv_b, WOP_UUU_B, H2, H1, H1, DO_ADD) +RVVCALL(OPIVV2, vwaddu_vv_h, WOP_UUU_H, H4, H2, H2, DO_ADD) +RVVCALL(OPIVV2, vwaddu_vv_w, WOP_UUU_W, H8, H4, H4, DO_ADD) +RVVCALL(OPIVV2, vwsubu_vv_b, WOP_UUU_B, H2, H1, H1, DO_SUB) +RVVCALL(OPIVV2, vwsubu_vv_h, WOP_UUU_H, H4, H2, H2, DO_SUB) +RVVCALL(OPIVV2, vwsubu_vv_w, WOP_UUU_W, H8, H4, H4, DO_SUB) +RVVCALL(OPIVV2, vwadd_vv_b, WOP_SSS_B, H2, H1, H1, DO_ADD) +RVVCALL(OPIVV2, vwadd_vv_h, WOP_SSS_H, H4, H2, H2, DO_ADD) +RVVCALL(OPIVV2, vwadd_vv_w, WOP_SSS_W, H8, H4, H4, DO_ADD) +RVVCALL(OPIVV2, vwsub_vv_b, WOP_SSS_B, H2, H1, H1, DO_SUB) +RVVCALL(OPIVV2, vwsub_vv_h, WOP_SSS_H, H4, H2, H2, DO_SUB) +RVVCALL(OPIVV2, vwsub_vv_w, WOP_SSS_W, H8, H4, H4, DO_SUB) +RVVCALL(OPIVV2, vwaddu_wv_b, WOP_WUUU_B, H2, H1, H1, DO_ADD) +RVVCALL(OPIVV2, vwaddu_wv_h, WOP_WUUU_H, H4, H2, H2, DO_ADD) +RVVCALL(OPIVV2, vwaddu_wv_w, WOP_WUUU_W, H8, H4, H4, DO_ADD) +RVVCALL(OPIVV2, vwsubu_wv_b, WOP_WUUU_B, H2, H1, H1, DO_SUB) +RVVCALL(OPIVV2, vwsubu_wv_h, WOP_WUUU_H, H4, H2, H2, DO_SUB) +RVVCALL(OPIVV2, vwsubu_wv_w, WOP_WUUU_W, H8, H4, H4, DO_SUB) +RVVCALL(OPIVV2, vwadd_wv_b, WOP_WSSS_B, H2, H1, H1, DO_ADD) +RVVCALL(OPIVV2, vwadd_wv_h, WOP_WSSS_H, H4, H2, H2, DO_ADD) +RVVCALL(OPIVV2, vwadd_wv_w, WOP_WSSS_W, H8, H4, H4, DO_ADD) +RVVCALL(OPIVV2, vwsub_wv_b, WOP_WSSS_B, H2, H1, H1, DO_SUB) +RVVCALL(OPIVV2, vwsub_wv_h, WOP_WSSS_H, H4, H2, H2, DO_SUB) +RVVCALL(OPIVV2, vwsub_wv_w, WOP_WSSS_W, H8, H4, H4, DO_SUB) +GEN_VEXT_VV(vwaddu_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwaddu_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwaddu_vv_w, 4, 8, clearq) +GEN_VEXT_VV(vwsubu_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwsubu_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwsubu_vv_w, 4, 8, clearq) +GEN_VEXT_VV(vwadd_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwadd_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwadd_vv_w, 4, 8, clearq) +GEN_VEXT_VV(vwsub_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwsub_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwsub_vv_w, 4, 8, clearq) +GEN_VEXT_VV(vwaddu_wv_b, 1, 2, clearh) +GEN_VEXT_VV(vwaddu_wv_h, 2, 4, clearl) +GEN_VEXT_VV(vwaddu_wv_w, 4, 8, clearq) +GEN_VEXT_VV(vwsubu_wv_b, 1, 2, clearh) +GEN_VEXT_VV(vwsubu_wv_h, 2, 4, clearl) +GEN_VEXT_VV(vwsubu_wv_w, 4, 8, clearq) +GEN_VEXT_VV(vwadd_wv_b, 1, 2, clearh) +GEN_VEXT_VV(vwadd_wv_h, 2, 4, clearl) +GEN_VEXT_VV(vwadd_wv_w, 4, 8, clearq) +GEN_VEXT_VV(vwsub_wv_b, 1, 2, clearh) +GEN_VEXT_VV(vwsub_wv_h, 2, 4, clearl) +GEN_VEXT_VV(vwsub_wv_w, 4, 8, clearq) + +RVVCALL(OPIVX2, vwaddu_vx_b, WOP_UUU_B, H2, H1, DO_ADD) +RVVCALL(OPIVX2, vwaddu_vx_h, WOP_UUU_H, H4, H2, DO_ADD) +RVVCALL(OPIVX2, vwaddu_vx_w, WOP_UUU_W, H8, H4, DO_ADD) +RVVCALL(OPIVX2, vwsubu_vx_b, WOP_UUU_B, H2, H1, DO_SUB) +RVVCALL(OPIVX2, vwsubu_vx_h, WOP_UUU_H, H4, H2, DO_SUB) +RVVCALL(OPIVX2, vwsubu_vx_w, WOP_UUU_W, H8, H4, DO_SUB) +RVVCALL(OPIVX2, vwadd_vx_b, WOP_SSS_B, H2, H1, DO_ADD) +RVVCALL(OPIVX2, vwadd_vx_h, WOP_SSS_H, H4, H2, DO_ADD) +RVVCALL(OPIVX2, vwadd_vx_w, WOP_SSS_W, H8, H4, DO_ADD) +RVVCALL(OPIVX2, vwsub_vx_b, WOP_SSS_B, H2, H1, DO_SUB) +RVVCALL(OPIVX2, vwsub_vx_h, WOP_SSS_H, H4, H2, DO_SUB) +RVVCALL(OPIVX2, vwsub_vx_w, WOP_SSS_W, H8, H4, DO_SUB) +RVVCALL(OPIVX2, vwaddu_wx_b, WOP_WUUU_B, H2, H1, DO_ADD) +RVVCALL(OPIVX2, vwaddu_wx_h, WOP_WUUU_H, H4, H2, DO_ADD) +RVVCALL(OPIVX2, vwaddu_wx_w, WOP_WUUU_W, H8, H4, DO_ADD) +RVVCALL(OPIVX2, vwsubu_wx_b, WOP_WUUU_B, H2, H1, DO_SUB) +RVVCALL(OPIVX2, vwsubu_wx_h, WOP_WUUU_H, H4, H2, DO_SUB) +RVVCALL(OPIVX2, vwsubu_wx_w, WOP_WUUU_W, H8, H4, DO_SUB) +RVVCALL(OPIVX2, vwadd_wx_b, WOP_WSSS_B, H2, H1, DO_ADD) +RVVCALL(OPIVX2, vwadd_wx_h, WOP_WSSS_H, H4, H2, DO_ADD) +RVVCALL(OPIVX2, vwadd_wx_w, WOP_WSSS_W, H8, H4, DO_ADD) +RVVCALL(OPIVX2, vwsub_wx_b, WOP_WSSS_B, H2, H1, DO_SUB) +RVVCALL(OPIVX2, vwsub_wx_h, WOP_WSSS_H, H4, H2, DO_SUB) +RVVCALL(OPIVX2, vwsub_wx_w, WOP_WSSS_W, H8, H4, DO_SUB) +GEN_VEXT_VX(vwaddu_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwaddu_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwaddu_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwsubu_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwsubu_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwsubu_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwadd_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwadd_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwadd_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwsub_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwsub_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwsub_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwaddu_wx_b, 1, 2, clearh) +GEN_VEXT_VX(vwaddu_wx_h, 2, 4, clearl) +GEN_VEXT_VX(vwaddu_wx_w, 4, 8, clearq) +GEN_VEXT_VX(vwsubu_wx_b, 1, 2, clearh) +GEN_VEXT_VX(vwsubu_wx_h, 2, 4, clearl) +GEN_VEXT_VX(vwsubu_wx_w, 4, 8, clearq) +GEN_VEXT_VX(vwadd_wx_b, 1, 2, clearh) +GEN_VEXT_VX(vwadd_wx_h, 2, 4, clearl) +GEN_VEXT_VX(vwadd_wx_w, 4, 8, clearq) +GEN_VEXT_VX(vwsub_wx_b, 1, 2, clearh) +GEN_VEXT_VX(vwsub_wx_h, 2, 4, clearl) +GEN_VEXT_VX(vwsub_wx_w, 4, 8, clearq) + +/* Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions */ +#define DO_VADC(N, M, C) (N + M + C) +#define DO_VSBC(N, M, C) (N - M - C) + +#define GEN_VEXT_VADC_VVM(NAME, ETYPE, H, DO_OP, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + uint8_t carry = vext_elem_mask(v0, mlen, i); \ + \ + *((ETYPE *)vd + H(i)) = DO_OP(s2, s1, carry); \ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VEXT_VADC_VVM(vadc_vvm_b, uint8_t, H1, DO_VADC, clearb) +GEN_VEXT_VADC_VVM(vadc_vvm_h, uint16_t, H2, DO_VADC, clearh) +GEN_VEXT_VADC_VVM(vadc_vvm_w, uint32_t, H4, DO_VADC, clearl) +GEN_VEXT_VADC_VVM(vadc_vvm_d, uint64_t, H8, DO_VADC, clearq) + +GEN_VEXT_VADC_VVM(vsbc_vvm_b, uint8_t, H1, DO_VSBC, clearb) +GEN_VEXT_VADC_VVM(vsbc_vvm_h, uint16_t, H2, DO_VSBC, clearh) +GEN_VEXT_VADC_VVM(vsbc_vvm_w, uint32_t, H4, DO_VSBC, clearl) +GEN_VEXT_VADC_VVM(vsbc_vvm_d, uint64_t, H8, DO_VSBC, clearq) + +#define GEN_VEXT_VADC_VXM(NAME, ETYPE, H, DO_OP, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + uint8_t carry = vext_elem_mask(v0, mlen, i); \ + \ + *((ETYPE *)vd + H(i)) = DO_OP(s2, (ETYPE)(target_long)s1, carry);\ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VEXT_VADC_VXM(vadc_vxm_b, uint8_t, H1, DO_VADC, clearb) +GEN_VEXT_VADC_VXM(vadc_vxm_h, uint16_t, H2, DO_VADC, clearh) +GEN_VEXT_VADC_VXM(vadc_vxm_w, uint32_t, H4, DO_VADC, clearl) +GEN_VEXT_VADC_VXM(vadc_vxm_d, uint64_t, H8, DO_VADC, clearq) + +GEN_VEXT_VADC_VXM(vsbc_vxm_b, uint8_t, H1, DO_VSBC, clearb) +GEN_VEXT_VADC_VXM(vsbc_vxm_h, uint16_t, H2, DO_VSBC, clearh) +GEN_VEXT_VADC_VXM(vsbc_vxm_w, uint32_t, H4, DO_VSBC, clearl) +GEN_VEXT_VADC_VXM(vsbc_vxm_d, uint64_t, H8, DO_VSBC, clearq) + +#define DO_MADC(N, M, C) (C ? (__typeof(N))(N + M + 1) <= N : \ + (__typeof(N))(N + M) < N) +#define DO_MSBC(N, M, C) (C ? N <= M : N < M) + +#define GEN_VEXT_VMADC_VVM(NAME, ETYPE, H, DO_OP) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vl = env->vl; \ + uint32_t vlmax = vext_maxsz(desc) / sizeof(ETYPE); \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + uint8_t carry = vext_elem_mask(v0, mlen, i); \ + \ + vext_set_elem_mask(vd, mlen, i, DO_OP(s2, s1, carry));\ + } \ + for (; i < vlmax; i++) { \ + vext_set_elem_mask(vd, mlen, i, 0); \ + } \ +} + +GEN_VEXT_VMADC_VVM(vmadc_vvm_b, uint8_t, H1, DO_MADC) +GEN_VEXT_VMADC_VVM(vmadc_vvm_h, uint16_t, H2, DO_MADC) +GEN_VEXT_VMADC_VVM(vmadc_vvm_w, uint32_t, H4, DO_MADC) +GEN_VEXT_VMADC_VVM(vmadc_vvm_d, uint64_t, H8, DO_MADC) + +GEN_VEXT_VMADC_VVM(vmsbc_vvm_b, uint8_t, H1, DO_MSBC) +GEN_VEXT_VMADC_VVM(vmsbc_vvm_h, uint16_t, H2, DO_MSBC) +GEN_VEXT_VMADC_VVM(vmsbc_vvm_w, uint32_t, H4, DO_MSBC) +GEN_VEXT_VMADC_VVM(vmsbc_vvm_d, uint64_t, H8, DO_MSBC) + +#define GEN_VEXT_VMADC_VXM(NAME, ETYPE, H, DO_OP) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vl = env->vl; \ + uint32_t vlmax = vext_maxsz(desc) / sizeof(ETYPE); \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + uint8_t carry = vext_elem_mask(v0, mlen, i); \ + \ + vext_set_elem_mask(vd, mlen, i, \ + DO_OP(s2, (ETYPE)(target_long)s1, carry)); \ + } \ + for (; i < vlmax; i++) { \ + vext_set_elem_mask(vd, mlen, i, 0); \ + } \ +} + +GEN_VEXT_VMADC_VXM(vmadc_vxm_b, uint8_t, H1, DO_MADC) +GEN_VEXT_VMADC_VXM(vmadc_vxm_h, uint16_t, H2, DO_MADC) +GEN_VEXT_VMADC_VXM(vmadc_vxm_w, uint32_t, H4, DO_MADC) +GEN_VEXT_VMADC_VXM(vmadc_vxm_d, uint64_t, H8, DO_MADC) + +GEN_VEXT_VMADC_VXM(vmsbc_vxm_b, uint8_t, H1, DO_MSBC) +GEN_VEXT_VMADC_VXM(vmsbc_vxm_h, uint16_t, H2, DO_MSBC) +GEN_VEXT_VMADC_VXM(vmsbc_vxm_w, uint32_t, H4, DO_MSBC) +GEN_VEXT_VMADC_VXM(vmsbc_vxm_d, uint64_t, H8, DO_MSBC) + +/* Vector Bitwise Logical Instructions */ +RVVCALL(OPIVV2, vand_vv_b, OP_SSS_B, H1, H1, H1, DO_AND) +RVVCALL(OPIVV2, vand_vv_h, OP_SSS_H, H2, H2, H2, DO_AND) +RVVCALL(OPIVV2, vand_vv_w, OP_SSS_W, H4, H4, H4, DO_AND) +RVVCALL(OPIVV2, vand_vv_d, OP_SSS_D, H8, H8, H8, DO_AND) +RVVCALL(OPIVV2, vor_vv_b, OP_SSS_B, H1, H1, H1, DO_OR) +RVVCALL(OPIVV2, vor_vv_h, OP_SSS_H, H2, H2, H2, DO_OR) +RVVCALL(OPIVV2, vor_vv_w, OP_SSS_W, H4, H4, H4, DO_OR) +RVVCALL(OPIVV2, vor_vv_d, OP_SSS_D, H8, H8, H8, DO_OR) +RVVCALL(OPIVV2, vxor_vv_b, OP_SSS_B, H1, H1, H1, DO_XOR) +RVVCALL(OPIVV2, vxor_vv_h, OP_SSS_H, H2, H2, H2, DO_XOR) +RVVCALL(OPIVV2, vxor_vv_w, OP_SSS_W, H4, H4, H4, DO_XOR) +RVVCALL(OPIVV2, vxor_vv_d, OP_SSS_D, H8, H8, H8, DO_XOR) +GEN_VEXT_VV(vand_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vand_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vand_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vand_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vor_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vor_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vor_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vor_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vxor_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vxor_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vxor_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vxor_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2, vand_vx_b, OP_SSS_B, H1, H1, DO_AND) +RVVCALL(OPIVX2, vand_vx_h, OP_SSS_H, H2, H2, DO_AND) +RVVCALL(OPIVX2, vand_vx_w, OP_SSS_W, H4, H4, DO_AND) +RVVCALL(OPIVX2, vand_vx_d, OP_SSS_D, H8, H8, DO_AND) +RVVCALL(OPIVX2, vor_vx_b, OP_SSS_B, H1, H1, DO_OR) +RVVCALL(OPIVX2, vor_vx_h, OP_SSS_H, H2, H2, DO_OR) +RVVCALL(OPIVX2, vor_vx_w, OP_SSS_W, H4, H4, DO_OR) +RVVCALL(OPIVX2, vor_vx_d, OP_SSS_D, H8, H8, DO_OR) +RVVCALL(OPIVX2, vxor_vx_b, OP_SSS_B, H1, H1, DO_XOR) +RVVCALL(OPIVX2, vxor_vx_h, OP_SSS_H, H2, H2, DO_XOR) +RVVCALL(OPIVX2, vxor_vx_w, OP_SSS_W, H4, H4, DO_XOR) +RVVCALL(OPIVX2, vxor_vx_d, OP_SSS_D, H8, H8, DO_XOR) +GEN_VEXT_VX(vand_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vand_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vand_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vand_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vor_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vor_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vor_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vor_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vxor_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vxor_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vxor_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vxor_vx_d, 8, 8, clearq) + +/* Vector Single-Width Bit Shift Instructions */ +#define DO_SLL(N, M) (N << (M)) +#define DO_SRL(N, M) (N >> (M)) + +/* generate the helpers for shift instructions with two vector operators */ +#define GEN_VEXT_SHIFT_VV(NAME, TS1, TS2, HS1, HS2, OP, MASK, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(TS1); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + TS1 s1 = *((TS1 *)vs1 + HS1(i)); \ + TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ + *((TS1 *)vd + HS1(i)) = OP(s2, s1 & MASK); \ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VEXT_SHIFT_VV(vsll_vv_b, uint8_t, uint8_t, H1, H1, DO_SLL, 0x7, clearb) +GEN_VEXT_SHIFT_VV(vsll_vv_h, uint16_t, uint16_t, H2, H2, DO_SLL, 0xf, clearh) +GEN_VEXT_SHIFT_VV(vsll_vv_w, uint32_t, uint32_t, H4, H4, DO_SLL, 0x1f, clearl) +GEN_VEXT_SHIFT_VV(vsll_vv_d, uint64_t, uint64_t, H8, H8, DO_SLL, 0x3f, clearq) + +GEN_VEXT_SHIFT_VV(vsrl_vv_b, uint8_t, uint8_t, H1, H1, DO_SRL, 0x7, clearb) +GEN_VEXT_SHIFT_VV(vsrl_vv_h, uint16_t, uint16_t, H2, H2, DO_SRL, 0xf, clearh) +GEN_VEXT_SHIFT_VV(vsrl_vv_w, uint32_t, uint32_t, H4, H4, DO_SRL, 0x1f, clearl) +GEN_VEXT_SHIFT_VV(vsrl_vv_d, uint64_t, uint64_t, H8, H8, DO_SRL, 0x3f, clearq) + +GEN_VEXT_SHIFT_VV(vsra_vv_b, uint8_t, int8_t, H1, H1, DO_SRL, 0x7, clearb) +GEN_VEXT_SHIFT_VV(vsra_vv_h, uint16_t, int16_t, H2, H2, DO_SRL, 0xf, clearh) +GEN_VEXT_SHIFT_VV(vsra_vv_w, uint32_t, int32_t, H4, H4, DO_SRL, 0x1f, clearl) +GEN_VEXT_SHIFT_VV(vsra_vv_d, uint64_t, int64_t, H8, H8, DO_SRL, 0x3f, clearq) + +/* generate the helpers for shift instructions with one vector and one scalar */ +#define GEN_VEXT_SHIFT_VX(NAME, TD, TS2, HD, HS2, OP, MASK, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(TD); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2, s1 & MASK); \ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VEXT_SHIFT_VX(vsll_vx_b, uint8_t, int8_t, H1, H1, DO_SLL, 0x7, clearb) +GEN_VEXT_SHIFT_VX(vsll_vx_h, uint16_t, int16_t, H2, H2, DO_SLL, 0xf, clearh) +GEN_VEXT_SHIFT_VX(vsll_vx_w, uint32_t, int32_t, H4, H4, DO_SLL, 0x1f, clearl) +GEN_VEXT_SHIFT_VX(vsll_vx_d, uint64_t, int64_t, H8, H8, DO_SLL, 0x3f, clearq) + +GEN_VEXT_SHIFT_VX(vsrl_vx_b, uint8_t, uint8_t, H1, H1, DO_SRL, 0x7, clearb) +GEN_VEXT_SHIFT_VX(vsrl_vx_h, uint16_t, uint16_t, H2, H2, DO_SRL, 0xf, clearh) +GEN_VEXT_SHIFT_VX(vsrl_vx_w, uint32_t, uint32_t, H4, H4, DO_SRL, 0x1f, clearl) +GEN_VEXT_SHIFT_VX(vsrl_vx_d, uint64_t, uint64_t, H8, H8, DO_SRL, 0x3f, clearq) + +GEN_VEXT_SHIFT_VX(vsra_vx_b, int8_t, int8_t, H1, H1, DO_SRL, 0x7, clearb) +GEN_VEXT_SHIFT_VX(vsra_vx_h, int16_t, int16_t, H2, H2, DO_SRL, 0xf, clearh) +GEN_VEXT_SHIFT_VX(vsra_vx_w, int32_t, int32_t, H4, H4, DO_SRL, 0x1f, clearl) +GEN_VEXT_SHIFT_VX(vsra_vx_d, int64_t, int64_t, H8, H8, DO_SRL, 0x3f, clearq) + +/* Vector Narrowing Integer Right Shift Instructions */ +GEN_VEXT_SHIFT_VV(vnsrl_vv_b, uint8_t, uint16_t, H1, H2, DO_SRL, 0xf, clearb) +GEN_VEXT_SHIFT_VV(vnsrl_vv_h, uint16_t, uint32_t, H2, H4, DO_SRL, 0x1f, clearh) +GEN_VEXT_SHIFT_VV(vnsrl_vv_w, uint32_t, uint64_t, H4, H8, DO_SRL, 0x3f, clearl) +GEN_VEXT_SHIFT_VV(vnsra_vv_b, uint8_t, int16_t, H1, H2, DO_SRL, 0xf, clearb) +GEN_VEXT_SHIFT_VV(vnsra_vv_h, uint16_t, int32_t, H2, H4, DO_SRL, 0x1f, clearh) +GEN_VEXT_SHIFT_VV(vnsra_vv_w, uint32_t, int64_t, H4, H8, DO_SRL, 0x3f, clearl) +GEN_VEXT_SHIFT_VX(vnsrl_vx_b, uint8_t, uint16_t, H1, H2, DO_SRL, 0xf, clearb) +GEN_VEXT_SHIFT_VX(vnsrl_vx_h, uint16_t, uint32_t, H2, H4, DO_SRL, 0x1f, clearh) +GEN_VEXT_SHIFT_VX(vnsrl_vx_w, uint32_t, uint64_t, H4, H8, DO_SRL, 0x3f, clearl) +GEN_VEXT_SHIFT_VX(vnsra_vx_b, int8_t, int16_t, H1, H2, DO_SRL, 0xf, clearb) +GEN_VEXT_SHIFT_VX(vnsra_vx_h, int16_t, int32_t, H2, H4, DO_SRL, 0x1f, clearh) +GEN_VEXT_SHIFT_VX(vnsra_vx_w, int32_t, int64_t, H4, H8, DO_SRL, 0x3f, clearl) + +/* Vector Integer Comparison Instructions */ +#define DO_MSEQ(N, M) (N == M) +#define DO_MSNE(N, M) (N != M) +#define DO_MSLT(N, M) (N < M) +#define DO_MSLE(N, M) (N <= M) +#define DO_MSGT(N, M) (N > M) + +#define GEN_VEXT_CMP_VV(NAME, ETYPE, H, DO_OP) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t vlmax = vext_maxsz(desc) / sizeof(ETYPE); \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + vext_set_elem_mask(vd, mlen, i, DO_OP(s2, s1)); \ + } \ + for (; i < vlmax; i++) { \ + vext_set_elem_mask(vd, mlen, i, 0); \ + } \ +} + +GEN_VEXT_CMP_VV(vmseq_vv_b, uint8_t, H1, DO_MSEQ) +GEN_VEXT_CMP_VV(vmseq_vv_h, uint16_t, H2, DO_MSEQ) +GEN_VEXT_CMP_VV(vmseq_vv_w, uint32_t, H4, DO_MSEQ) +GEN_VEXT_CMP_VV(vmseq_vv_d, uint64_t, H8, DO_MSEQ) + +GEN_VEXT_CMP_VV(vmsne_vv_b, uint8_t, H1, DO_MSNE) +GEN_VEXT_CMP_VV(vmsne_vv_h, uint16_t, H2, DO_MSNE) +GEN_VEXT_CMP_VV(vmsne_vv_w, uint32_t, H4, DO_MSNE) +GEN_VEXT_CMP_VV(vmsne_vv_d, uint64_t, H8, DO_MSNE) + +GEN_VEXT_CMP_VV(vmsltu_vv_b, uint8_t, H1, DO_MSLT) +GEN_VEXT_CMP_VV(vmsltu_vv_h, uint16_t, H2, DO_MSLT) +GEN_VEXT_CMP_VV(vmsltu_vv_w, uint32_t, H4, DO_MSLT) +GEN_VEXT_CMP_VV(vmsltu_vv_d, uint64_t, H8, DO_MSLT) + +GEN_VEXT_CMP_VV(vmslt_vv_b, int8_t, H1, DO_MSLT) +GEN_VEXT_CMP_VV(vmslt_vv_h, int16_t, H2, DO_MSLT) +GEN_VEXT_CMP_VV(vmslt_vv_w, int32_t, H4, DO_MSLT) +GEN_VEXT_CMP_VV(vmslt_vv_d, int64_t, H8, DO_MSLT) + +GEN_VEXT_CMP_VV(vmsleu_vv_b, uint8_t, H1, DO_MSLE) +GEN_VEXT_CMP_VV(vmsleu_vv_h, uint16_t, H2, DO_MSLE) +GEN_VEXT_CMP_VV(vmsleu_vv_w, uint32_t, H4, DO_MSLE) +GEN_VEXT_CMP_VV(vmsleu_vv_d, uint64_t, H8, DO_MSLE) + +GEN_VEXT_CMP_VV(vmsle_vv_b, int8_t, H1, DO_MSLE) +GEN_VEXT_CMP_VV(vmsle_vv_h, int16_t, H2, DO_MSLE) +GEN_VEXT_CMP_VV(vmsle_vv_w, int32_t, H4, DO_MSLE) +GEN_VEXT_CMP_VV(vmsle_vv_d, int64_t, H8, DO_MSLE) + +#define GEN_VEXT_CMP_VX(NAME, ETYPE, H, DO_OP) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t vlmax = vext_maxsz(desc) / sizeof(ETYPE); \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + vext_set_elem_mask(vd, mlen, i, \ + DO_OP(s2, (ETYPE)(target_long)s1)); \ + } \ + for (; i < vlmax; i++) { \ + vext_set_elem_mask(vd, mlen, i, 0); \ + } \ +} + +GEN_VEXT_CMP_VX(vmseq_vx_b, uint8_t, H1, DO_MSEQ) +GEN_VEXT_CMP_VX(vmseq_vx_h, uint16_t, H2, DO_MSEQ) +GEN_VEXT_CMP_VX(vmseq_vx_w, uint32_t, H4, DO_MSEQ) +GEN_VEXT_CMP_VX(vmseq_vx_d, uint64_t, H8, DO_MSEQ) + +GEN_VEXT_CMP_VX(vmsne_vx_b, uint8_t, H1, DO_MSNE) +GEN_VEXT_CMP_VX(vmsne_vx_h, uint16_t, H2, DO_MSNE) +GEN_VEXT_CMP_VX(vmsne_vx_w, uint32_t, H4, DO_MSNE) +GEN_VEXT_CMP_VX(vmsne_vx_d, uint64_t, H8, DO_MSNE) + +GEN_VEXT_CMP_VX(vmsltu_vx_b, uint8_t, H1, DO_MSLT) +GEN_VEXT_CMP_VX(vmsltu_vx_h, uint16_t, H2, DO_MSLT) +GEN_VEXT_CMP_VX(vmsltu_vx_w, uint32_t, H4, DO_MSLT) +GEN_VEXT_CMP_VX(vmsltu_vx_d, uint64_t, H8, DO_MSLT) + +GEN_VEXT_CMP_VX(vmslt_vx_b, int8_t, H1, DO_MSLT) +GEN_VEXT_CMP_VX(vmslt_vx_h, int16_t, H2, DO_MSLT) +GEN_VEXT_CMP_VX(vmslt_vx_w, int32_t, H4, DO_MSLT) +GEN_VEXT_CMP_VX(vmslt_vx_d, int64_t, H8, DO_MSLT) + +GEN_VEXT_CMP_VX(vmsleu_vx_b, uint8_t, H1, DO_MSLE) +GEN_VEXT_CMP_VX(vmsleu_vx_h, uint16_t, H2, DO_MSLE) +GEN_VEXT_CMP_VX(vmsleu_vx_w, uint32_t, H4, DO_MSLE) +GEN_VEXT_CMP_VX(vmsleu_vx_d, uint64_t, H8, DO_MSLE) + +GEN_VEXT_CMP_VX(vmsle_vx_b, int8_t, H1, DO_MSLE) +GEN_VEXT_CMP_VX(vmsle_vx_h, int16_t, H2, DO_MSLE) +GEN_VEXT_CMP_VX(vmsle_vx_w, int32_t, H4, DO_MSLE) +GEN_VEXT_CMP_VX(vmsle_vx_d, int64_t, H8, DO_MSLE) + +GEN_VEXT_CMP_VX(vmsgtu_vx_b, uint8_t, H1, DO_MSGT) +GEN_VEXT_CMP_VX(vmsgtu_vx_h, uint16_t, H2, DO_MSGT) +GEN_VEXT_CMP_VX(vmsgtu_vx_w, uint32_t, H4, DO_MSGT) +GEN_VEXT_CMP_VX(vmsgtu_vx_d, uint64_t, H8, DO_MSGT) + +GEN_VEXT_CMP_VX(vmsgt_vx_b, int8_t, H1, DO_MSGT) +GEN_VEXT_CMP_VX(vmsgt_vx_h, int16_t, H2, DO_MSGT) +GEN_VEXT_CMP_VX(vmsgt_vx_w, int32_t, H4, DO_MSGT) +GEN_VEXT_CMP_VX(vmsgt_vx_d, int64_t, H8, DO_MSGT) + +/* Vector Integer Min/Max Instructions */ +RVVCALL(OPIVV2, vminu_vv_b, OP_UUU_B, H1, H1, H1, DO_MIN) +RVVCALL(OPIVV2, vminu_vv_h, OP_UUU_H, H2, H2, H2, DO_MIN) +RVVCALL(OPIVV2, vminu_vv_w, OP_UUU_W, H4, H4, H4, DO_MIN) +RVVCALL(OPIVV2, vminu_vv_d, OP_UUU_D, H8, H8, H8, DO_MIN) +RVVCALL(OPIVV2, vmin_vv_b, OP_SSS_B, H1, H1, H1, DO_MIN) +RVVCALL(OPIVV2, vmin_vv_h, OP_SSS_H, H2, H2, H2, DO_MIN) +RVVCALL(OPIVV2, vmin_vv_w, OP_SSS_W, H4, H4, H4, DO_MIN) +RVVCALL(OPIVV2, vmin_vv_d, OP_SSS_D, H8, H8, H8, DO_MIN) +RVVCALL(OPIVV2, vmaxu_vv_b, OP_UUU_B, H1, H1, H1, DO_MAX) +RVVCALL(OPIVV2, vmaxu_vv_h, OP_UUU_H, H2, H2, H2, DO_MAX) +RVVCALL(OPIVV2, vmaxu_vv_w, OP_UUU_W, H4, H4, H4, DO_MAX) +RVVCALL(OPIVV2, vmaxu_vv_d, OP_UUU_D, H8, H8, H8, DO_MAX) +RVVCALL(OPIVV2, vmax_vv_b, OP_SSS_B, H1, H1, H1, DO_MAX) +RVVCALL(OPIVV2, vmax_vv_h, OP_SSS_H, H2, H2, H2, DO_MAX) +RVVCALL(OPIVV2, vmax_vv_w, OP_SSS_W, H4, H4, H4, DO_MAX) +RVVCALL(OPIVV2, vmax_vv_d, OP_SSS_D, H8, H8, H8, DO_MAX) +GEN_VEXT_VV(vminu_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vminu_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vminu_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vminu_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vmin_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmin_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmin_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmin_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vmaxu_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmaxu_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmaxu_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmaxu_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vmax_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmax_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmax_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmax_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2, vminu_vx_b, OP_UUU_B, H1, H1, DO_MIN) +RVVCALL(OPIVX2, vminu_vx_h, OP_UUU_H, H2, H2, DO_MIN) +RVVCALL(OPIVX2, vminu_vx_w, OP_UUU_W, H4, H4, DO_MIN) +RVVCALL(OPIVX2, vminu_vx_d, OP_UUU_D, H8, H8, DO_MIN) +RVVCALL(OPIVX2, vmin_vx_b, OP_SSS_B, H1, H1, DO_MIN) +RVVCALL(OPIVX2, vmin_vx_h, OP_SSS_H, H2, H2, DO_MIN) +RVVCALL(OPIVX2, vmin_vx_w, OP_SSS_W, H4, H4, DO_MIN) +RVVCALL(OPIVX2, vmin_vx_d, OP_SSS_D, H8, H8, DO_MIN) +RVVCALL(OPIVX2, vmaxu_vx_b, OP_UUU_B, H1, H1, DO_MAX) +RVVCALL(OPIVX2, vmaxu_vx_h, OP_UUU_H, H2, H2, DO_MAX) +RVVCALL(OPIVX2, vmaxu_vx_w, OP_UUU_W, H4, H4, DO_MAX) +RVVCALL(OPIVX2, vmaxu_vx_d, OP_UUU_D, H8, H8, DO_MAX) +RVVCALL(OPIVX2, vmax_vx_b, OP_SSS_B, H1, H1, DO_MAX) +RVVCALL(OPIVX2, vmax_vx_h, OP_SSS_H, H2, H2, DO_MAX) +RVVCALL(OPIVX2, vmax_vx_w, OP_SSS_W, H4, H4, DO_MAX) +RVVCALL(OPIVX2, vmax_vx_d, OP_SSS_D, H8, H8, DO_MAX) +GEN_VEXT_VX(vminu_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vminu_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vminu_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vminu_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vmin_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmin_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmin_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmin_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vmaxu_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmaxu_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmaxu_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmaxu_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vmax_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmax_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmax_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmax_vx_d, 8, 8, clearq) + +/* Vector Single-Width Integer Multiply Instructions */ +#define DO_MUL(N, M) (N * M) +RVVCALL(OPIVV2, vmul_vv_b, OP_SSS_B, H1, H1, H1, DO_MUL) +RVVCALL(OPIVV2, vmul_vv_h, OP_SSS_H, H2, H2, H2, DO_MUL) +RVVCALL(OPIVV2, vmul_vv_w, OP_SSS_W, H4, H4, H4, DO_MUL) +RVVCALL(OPIVV2, vmul_vv_d, OP_SSS_D, H8, H8, H8, DO_MUL) +GEN_VEXT_VV(vmul_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmul_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmul_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmul_vv_d, 8, 8, clearq) + +static int8_t do_mulh_b(int8_t s2, int8_t s1) +{ + return (int16_t)s2 * (int16_t)s1 >> 8; +} + +static int16_t do_mulh_h(int16_t s2, int16_t s1) +{ + return (int32_t)s2 * (int32_t)s1 >> 16; +} + +static int32_t do_mulh_w(int32_t s2, int32_t s1) +{ + return (int64_t)s2 * (int64_t)s1 >> 32; +} + +static int64_t do_mulh_d(int64_t s2, int64_t s1) +{ + uint64_t hi_64, lo_64; + + muls64(&lo_64, &hi_64, s1, s2); + return hi_64; +} + +static uint8_t do_mulhu_b(uint8_t s2, uint8_t s1) +{ + return (uint16_t)s2 * (uint16_t)s1 >> 8; +} + +static uint16_t do_mulhu_h(uint16_t s2, uint16_t s1) +{ + return (uint32_t)s2 * (uint32_t)s1 >> 16; +} + +static uint32_t do_mulhu_w(uint32_t s2, uint32_t s1) +{ + return (uint64_t)s2 * (uint64_t)s1 >> 32; +} + +static uint64_t do_mulhu_d(uint64_t s2, uint64_t s1) +{ + uint64_t hi_64, lo_64; + + mulu64(&lo_64, &hi_64, s2, s1); + return hi_64; +} + +static int8_t do_mulhsu_b(int8_t s2, uint8_t s1) +{ + return (int16_t)s2 * (uint16_t)s1 >> 8; +} + +static int16_t do_mulhsu_h(int16_t s2, uint16_t s1) +{ + return (int32_t)s2 * (uint32_t)s1 >> 16; +} + +static int32_t do_mulhsu_w(int32_t s2, uint32_t s1) +{ + return (int64_t)s2 * (uint64_t)s1 >> 32; +} + +/* + * Let A = signed operand, + * B = unsigned operand + * P = mulu64(A, B), unsigned product + * + * LET X = 2 ** 64 - A, 2's complement of A + * SP = signed product + * THEN + * IF A < 0 + * SP = -X * B + * = -(2 ** 64 - A) * B + * = A * B - 2 ** 64 * B + * = P - 2 ** 64 * B + * ELSE + * SP = P + * THEN + * HI_P -= (A < 0 ? B : 0) + */ + +static int64_t do_mulhsu_d(int64_t s2, uint64_t s1) +{ + uint64_t hi_64, lo_64; + + mulu64(&lo_64, &hi_64, s2, s1); + + hi_64 -= s2 < 0 ? s1 : 0; + return hi_64; +} + +RVVCALL(OPIVV2, vmulh_vv_b, OP_SSS_B, H1, H1, H1, do_mulh_b) +RVVCALL(OPIVV2, vmulh_vv_h, OP_SSS_H, H2, H2, H2, do_mulh_h) +RVVCALL(OPIVV2, vmulh_vv_w, OP_SSS_W, H4, H4, H4, do_mulh_w) +RVVCALL(OPIVV2, vmulh_vv_d, OP_SSS_D, H8, H8, H8, do_mulh_d) +RVVCALL(OPIVV2, vmulhu_vv_b, OP_UUU_B, H1, H1, H1, do_mulhu_b) +RVVCALL(OPIVV2, vmulhu_vv_h, OP_UUU_H, H2, H2, H2, do_mulhu_h) +RVVCALL(OPIVV2, vmulhu_vv_w, OP_UUU_W, H4, H4, H4, do_mulhu_w) +RVVCALL(OPIVV2, vmulhu_vv_d, OP_UUU_D, H8, H8, H8, do_mulhu_d) +RVVCALL(OPIVV2, vmulhsu_vv_b, OP_SUS_B, H1, H1, H1, do_mulhsu_b) +RVVCALL(OPIVV2, vmulhsu_vv_h, OP_SUS_H, H2, H2, H2, do_mulhsu_h) +RVVCALL(OPIVV2, vmulhsu_vv_w, OP_SUS_W, H4, H4, H4, do_mulhsu_w) +RVVCALL(OPIVV2, vmulhsu_vv_d, OP_SUS_D, H8, H8, H8, do_mulhsu_d) +GEN_VEXT_VV(vmulh_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmulh_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmulh_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmulh_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vmulhu_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmulhu_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmulhu_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmulhu_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vmulhsu_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmulhsu_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmulhsu_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmulhsu_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2, vmul_vx_b, OP_SSS_B, H1, H1, DO_MUL) +RVVCALL(OPIVX2, vmul_vx_h, OP_SSS_H, H2, H2, DO_MUL) +RVVCALL(OPIVX2, vmul_vx_w, OP_SSS_W, H4, H4, DO_MUL) +RVVCALL(OPIVX2, vmul_vx_d, OP_SSS_D, H8, H8, DO_MUL) +RVVCALL(OPIVX2, vmulh_vx_b, OP_SSS_B, H1, H1, do_mulh_b) +RVVCALL(OPIVX2, vmulh_vx_h, OP_SSS_H, H2, H2, do_mulh_h) +RVVCALL(OPIVX2, vmulh_vx_w, OP_SSS_W, H4, H4, do_mulh_w) +RVVCALL(OPIVX2, vmulh_vx_d, OP_SSS_D, H8, H8, do_mulh_d) +RVVCALL(OPIVX2, vmulhu_vx_b, OP_UUU_B, H1, H1, do_mulhu_b) +RVVCALL(OPIVX2, vmulhu_vx_h, OP_UUU_H, H2, H2, do_mulhu_h) +RVVCALL(OPIVX2, vmulhu_vx_w, OP_UUU_W, H4, H4, do_mulhu_w) +RVVCALL(OPIVX2, vmulhu_vx_d, OP_UUU_D, H8, H8, do_mulhu_d) +RVVCALL(OPIVX2, vmulhsu_vx_b, OP_SUS_B, H1, H1, do_mulhsu_b) +RVVCALL(OPIVX2, vmulhsu_vx_h, OP_SUS_H, H2, H2, do_mulhsu_h) +RVVCALL(OPIVX2, vmulhsu_vx_w, OP_SUS_W, H4, H4, do_mulhsu_w) +RVVCALL(OPIVX2, vmulhsu_vx_d, OP_SUS_D, H8, H8, do_mulhsu_d) +GEN_VEXT_VX(vmul_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmul_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmul_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmul_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vmulh_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmulh_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmulh_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmulh_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vmulhu_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmulhu_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmulhu_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmulhu_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vmulhsu_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmulhsu_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmulhsu_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmulhsu_vx_d, 8, 8, clearq) + +/* Vector Integer Divide Instructions */ +#define DO_DIVU(N, M) (unlikely(M == 0) ? (__typeof(N))(-1) : N / M) +#define DO_REMU(N, M) (unlikely(M == 0) ? N : N % M) +#define DO_DIV(N, M) (unlikely(M == 0) ? (__typeof(N))(-1) :\ + unlikely((N == -N) && (M == (__typeof(N))(-1))) ? N : N / M) +#define DO_REM(N, M) (unlikely(M == 0) ? N :\ + unlikely((N == -N) && (M == (__typeof(N))(-1))) ? 0 : N % M) + +RVVCALL(OPIVV2, vdivu_vv_b, OP_UUU_B, H1, H1, H1, DO_DIVU) +RVVCALL(OPIVV2, vdivu_vv_h, OP_UUU_H, H2, H2, H2, DO_DIVU) +RVVCALL(OPIVV2, vdivu_vv_w, OP_UUU_W, H4, H4, H4, DO_DIVU) +RVVCALL(OPIVV2, vdivu_vv_d, OP_UUU_D, H8, H8, H8, DO_DIVU) +RVVCALL(OPIVV2, vdiv_vv_b, OP_SSS_B, H1, H1, H1, DO_DIV) +RVVCALL(OPIVV2, vdiv_vv_h, OP_SSS_H, H2, H2, H2, DO_DIV) +RVVCALL(OPIVV2, vdiv_vv_w, OP_SSS_W, H4, H4, H4, DO_DIV) +RVVCALL(OPIVV2, vdiv_vv_d, OP_SSS_D, H8, H8, H8, DO_DIV) +RVVCALL(OPIVV2, vremu_vv_b, OP_UUU_B, H1, H1, H1, DO_REMU) +RVVCALL(OPIVV2, vremu_vv_h, OP_UUU_H, H2, H2, H2, DO_REMU) +RVVCALL(OPIVV2, vremu_vv_w, OP_UUU_W, H4, H4, H4, DO_REMU) +RVVCALL(OPIVV2, vremu_vv_d, OP_UUU_D, H8, H8, H8, DO_REMU) +RVVCALL(OPIVV2, vrem_vv_b, OP_SSS_B, H1, H1, H1, DO_REM) +RVVCALL(OPIVV2, vrem_vv_h, OP_SSS_H, H2, H2, H2, DO_REM) +RVVCALL(OPIVV2, vrem_vv_w, OP_SSS_W, H4, H4, H4, DO_REM) +RVVCALL(OPIVV2, vrem_vv_d, OP_SSS_D, H8, H8, H8, DO_REM) +GEN_VEXT_VV(vdivu_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vdivu_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vdivu_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vdivu_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vdiv_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vdiv_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vdiv_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vdiv_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vremu_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vremu_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vremu_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vremu_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vrem_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vrem_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vrem_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vrem_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2, vdivu_vx_b, OP_UUU_B, H1, H1, DO_DIVU) +RVVCALL(OPIVX2, vdivu_vx_h, OP_UUU_H, H2, H2, DO_DIVU) +RVVCALL(OPIVX2, vdivu_vx_w, OP_UUU_W, H4, H4, DO_DIVU) +RVVCALL(OPIVX2, vdivu_vx_d, OP_UUU_D, H8, H8, DO_DIVU) +RVVCALL(OPIVX2, vdiv_vx_b, OP_SSS_B, H1, H1, DO_DIV) +RVVCALL(OPIVX2, vdiv_vx_h, OP_SSS_H, H2, H2, DO_DIV) +RVVCALL(OPIVX2, vdiv_vx_w, OP_SSS_W, H4, H4, DO_DIV) +RVVCALL(OPIVX2, vdiv_vx_d, OP_SSS_D, H8, H8, DO_DIV) +RVVCALL(OPIVX2, vremu_vx_b, OP_UUU_B, H1, H1, DO_REMU) +RVVCALL(OPIVX2, vremu_vx_h, OP_UUU_H, H2, H2, DO_REMU) +RVVCALL(OPIVX2, vremu_vx_w, OP_UUU_W, H4, H4, DO_REMU) +RVVCALL(OPIVX2, vremu_vx_d, OP_UUU_D, H8, H8, DO_REMU) +RVVCALL(OPIVX2, vrem_vx_b, OP_SSS_B, H1, H1, DO_REM) +RVVCALL(OPIVX2, vrem_vx_h, OP_SSS_H, H2, H2, DO_REM) +RVVCALL(OPIVX2, vrem_vx_w, OP_SSS_W, H4, H4, DO_REM) +RVVCALL(OPIVX2, vrem_vx_d, OP_SSS_D, H8, H8, DO_REM) +GEN_VEXT_VX(vdivu_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vdivu_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vdivu_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vdivu_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vdiv_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vdiv_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vdiv_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vdiv_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vremu_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vremu_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vremu_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vremu_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vrem_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vrem_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vrem_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vrem_vx_d, 8, 8, clearq) + +/* Vector Widening Integer Multiply Instructions */ +RVVCALL(OPIVV2, vwmul_vv_b, WOP_SSS_B, H2, H1, H1, DO_MUL) +RVVCALL(OPIVV2, vwmul_vv_h, WOP_SSS_H, H4, H2, H2, DO_MUL) +RVVCALL(OPIVV2, vwmul_vv_w, WOP_SSS_W, H8, H4, H4, DO_MUL) +RVVCALL(OPIVV2, vwmulu_vv_b, WOP_UUU_B, H2, H1, H1, DO_MUL) +RVVCALL(OPIVV2, vwmulu_vv_h, WOP_UUU_H, H4, H2, H2, DO_MUL) +RVVCALL(OPIVV2, vwmulu_vv_w, WOP_UUU_W, H8, H4, H4, DO_MUL) +RVVCALL(OPIVV2, vwmulsu_vv_b, WOP_SUS_B, H2, H1, H1, DO_MUL) +RVVCALL(OPIVV2, vwmulsu_vv_h, WOP_SUS_H, H4, H2, H2, DO_MUL) +RVVCALL(OPIVV2, vwmulsu_vv_w, WOP_SUS_W, H8, H4, H4, DO_MUL) +GEN_VEXT_VV(vwmul_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwmul_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwmul_vv_w, 4, 8, clearq) +GEN_VEXT_VV(vwmulu_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwmulu_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwmulu_vv_w, 4, 8, clearq) +GEN_VEXT_VV(vwmulsu_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwmulsu_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwmulsu_vv_w, 4, 8, clearq) + +RVVCALL(OPIVX2, vwmul_vx_b, WOP_SSS_B, H2, H1, DO_MUL) +RVVCALL(OPIVX2, vwmul_vx_h, WOP_SSS_H, H4, H2, DO_MUL) +RVVCALL(OPIVX2, vwmul_vx_w, WOP_SSS_W, H8, H4, DO_MUL) +RVVCALL(OPIVX2, vwmulu_vx_b, WOP_UUU_B, H2, H1, DO_MUL) +RVVCALL(OPIVX2, vwmulu_vx_h, WOP_UUU_H, H4, H2, DO_MUL) +RVVCALL(OPIVX2, vwmulu_vx_w, WOP_UUU_W, H8, H4, DO_MUL) +RVVCALL(OPIVX2, vwmulsu_vx_b, WOP_SUS_B, H2, H1, DO_MUL) +RVVCALL(OPIVX2, vwmulsu_vx_h, WOP_SUS_H, H4, H2, DO_MUL) +RVVCALL(OPIVX2, vwmulsu_vx_w, WOP_SUS_W, H8, H4, DO_MUL) +GEN_VEXT_VX(vwmul_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwmul_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwmul_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwmulu_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwmulu_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwmulu_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwmulsu_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwmulsu_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwmulsu_vx_w, 4, 8, clearq) + +/* Vector Single-Width Integer Multiply-Add Instructions */ +#define OPIVV3(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ +static void do_##NAME(void *vd, void *vs1, void *vs2, int i) \ +{ \ + TX1 s1 = *((T1 *)vs1 + HS1(i)); \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + TD d = *((TD *)vd + HD(i)); \ + *((TD *)vd + HD(i)) = OP(s2, s1, d); \ +} + +#define DO_MACC(N, M, D) (M * N + D) +#define DO_NMSAC(N, M, D) (-(M * N) + D) +#define DO_MADD(N, M, D) (M * D + N) +#define DO_NMSUB(N, M, D) (-(M * D) + N) +RVVCALL(OPIVV3, vmacc_vv_b, OP_SSS_B, H1, H1, H1, DO_MACC) +RVVCALL(OPIVV3, vmacc_vv_h, OP_SSS_H, H2, H2, H2, DO_MACC) +RVVCALL(OPIVV3, vmacc_vv_w, OP_SSS_W, H4, H4, H4, DO_MACC) +RVVCALL(OPIVV3, vmacc_vv_d, OP_SSS_D, H8, H8, H8, DO_MACC) +RVVCALL(OPIVV3, vnmsac_vv_b, OP_SSS_B, H1, H1, H1, DO_NMSAC) +RVVCALL(OPIVV3, vnmsac_vv_h, OP_SSS_H, H2, H2, H2, DO_NMSAC) +RVVCALL(OPIVV3, vnmsac_vv_w, OP_SSS_W, H4, H4, H4, DO_NMSAC) +RVVCALL(OPIVV3, vnmsac_vv_d, OP_SSS_D, H8, H8, H8, DO_NMSAC) +RVVCALL(OPIVV3, vmadd_vv_b, OP_SSS_B, H1, H1, H1, DO_MADD) +RVVCALL(OPIVV3, vmadd_vv_h, OP_SSS_H, H2, H2, H2, DO_MADD) +RVVCALL(OPIVV3, vmadd_vv_w, OP_SSS_W, H4, H4, H4, DO_MADD) +RVVCALL(OPIVV3, vmadd_vv_d, OP_SSS_D, H8, H8, H8, DO_MADD) +RVVCALL(OPIVV3, vnmsub_vv_b, OP_SSS_B, H1, H1, H1, DO_NMSUB) +RVVCALL(OPIVV3, vnmsub_vv_h, OP_SSS_H, H2, H2, H2, DO_NMSUB) +RVVCALL(OPIVV3, vnmsub_vv_w, OP_SSS_W, H4, H4, H4, DO_NMSUB) +RVVCALL(OPIVV3, vnmsub_vv_d, OP_SSS_D, H8, H8, H8, DO_NMSUB) +GEN_VEXT_VV(vmacc_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmacc_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmacc_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmacc_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vnmsac_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vnmsac_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vnmsac_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vnmsac_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vmadd_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vmadd_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vmadd_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vmadd_vv_d, 8, 8, clearq) +GEN_VEXT_VV(vnmsub_vv_b, 1, 1, clearb) +GEN_VEXT_VV(vnmsub_vv_h, 2, 2, clearh) +GEN_VEXT_VV(vnmsub_vv_w, 4, 4, clearl) +GEN_VEXT_VV(vnmsub_vv_d, 8, 8, clearq) + +#define OPIVX3(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ +static void do_##NAME(void *vd, target_long s1, void *vs2, int i) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + TD d = *((TD *)vd + HD(i)); \ + *((TD *)vd + HD(i)) = OP(s2, (TX1)(T1)s1, d); \ +} + +RVVCALL(OPIVX3, vmacc_vx_b, OP_SSS_B, H1, H1, DO_MACC) +RVVCALL(OPIVX3, vmacc_vx_h, OP_SSS_H, H2, H2, DO_MACC) +RVVCALL(OPIVX3, vmacc_vx_w, OP_SSS_W, H4, H4, DO_MACC) +RVVCALL(OPIVX3, vmacc_vx_d, OP_SSS_D, H8, H8, DO_MACC) +RVVCALL(OPIVX3, vnmsac_vx_b, OP_SSS_B, H1, H1, DO_NMSAC) +RVVCALL(OPIVX3, vnmsac_vx_h, OP_SSS_H, H2, H2, DO_NMSAC) +RVVCALL(OPIVX3, vnmsac_vx_w, OP_SSS_W, H4, H4, DO_NMSAC) +RVVCALL(OPIVX3, vnmsac_vx_d, OP_SSS_D, H8, H8, DO_NMSAC) +RVVCALL(OPIVX3, vmadd_vx_b, OP_SSS_B, H1, H1, DO_MADD) +RVVCALL(OPIVX3, vmadd_vx_h, OP_SSS_H, H2, H2, DO_MADD) +RVVCALL(OPIVX3, vmadd_vx_w, OP_SSS_W, H4, H4, DO_MADD) +RVVCALL(OPIVX3, vmadd_vx_d, OP_SSS_D, H8, H8, DO_MADD) +RVVCALL(OPIVX3, vnmsub_vx_b, OP_SSS_B, H1, H1, DO_NMSUB) +RVVCALL(OPIVX3, vnmsub_vx_h, OP_SSS_H, H2, H2, DO_NMSUB) +RVVCALL(OPIVX3, vnmsub_vx_w, OP_SSS_W, H4, H4, DO_NMSUB) +RVVCALL(OPIVX3, vnmsub_vx_d, OP_SSS_D, H8, H8, DO_NMSUB) +GEN_VEXT_VX(vmacc_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmacc_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmacc_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmacc_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vnmsac_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vnmsac_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vnmsac_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vnmsac_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vmadd_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vmadd_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vmadd_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vmadd_vx_d, 8, 8, clearq) +GEN_VEXT_VX(vnmsub_vx_b, 1, 1, clearb) +GEN_VEXT_VX(vnmsub_vx_h, 2, 2, clearh) +GEN_VEXT_VX(vnmsub_vx_w, 4, 4, clearl) +GEN_VEXT_VX(vnmsub_vx_d, 8, 8, clearq) + +/* Vector Widening Integer Multiply-Add Instructions */ +RVVCALL(OPIVV3, vwmaccu_vv_b, WOP_UUU_B, H2, H1, H1, DO_MACC) +RVVCALL(OPIVV3, vwmaccu_vv_h, WOP_UUU_H, H4, H2, H2, DO_MACC) +RVVCALL(OPIVV3, vwmaccu_vv_w, WOP_UUU_W, H8, H4, H4, DO_MACC) +RVVCALL(OPIVV3, vwmacc_vv_b, WOP_SSS_B, H2, H1, H1, DO_MACC) +RVVCALL(OPIVV3, vwmacc_vv_h, WOP_SSS_H, H4, H2, H2, DO_MACC) +RVVCALL(OPIVV3, vwmacc_vv_w, WOP_SSS_W, H8, H4, H4, DO_MACC) +RVVCALL(OPIVV3, vwmaccsu_vv_b, WOP_SSU_B, H2, H1, H1, DO_MACC) +RVVCALL(OPIVV3, vwmaccsu_vv_h, WOP_SSU_H, H4, H2, H2, DO_MACC) +RVVCALL(OPIVV3, vwmaccsu_vv_w, WOP_SSU_W, H8, H4, H4, DO_MACC) +GEN_VEXT_VV(vwmaccu_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwmaccu_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwmaccu_vv_w, 4, 8, clearq) +GEN_VEXT_VV(vwmacc_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwmacc_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwmacc_vv_w, 4, 8, clearq) +GEN_VEXT_VV(vwmaccsu_vv_b, 1, 2, clearh) +GEN_VEXT_VV(vwmaccsu_vv_h, 2, 4, clearl) +GEN_VEXT_VV(vwmaccsu_vv_w, 4, 8, clearq) + +RVVCALL(OPIVX3, vwmaccu_vx_b, WOP_UUU_B, H2, H1, DO_MACC) +RVVCALL(OPIVX3, vwmaccu_vx_h, WOP_UUU_H, H4, H2, DO_MACC) +RVVCALL(OPIVX3, vwmaccu_vx_w, WOP_UUU_W, H8, H4, DO_MACC) +RVVCALL(OPIVX3, vwmacc_vx_b, WOP_SSS_B, H2, H1, DO_MACC) +RVVCALL(OPIVX3, vwmacc_vx_h, WOP_SSS_H, H4, H2, DO_MACC) +RVVCALL(OPIVX3, vwmacc_vx_w, WOP_SSS_W, H8, H4, DO_MACC) +RVVCALL(OPIVX3, vwmaccsu_vx_b, WOP_SSU_B, H2, H1, DO_MACC) +RVVCALL(OPIVX3, vwmaccsu_vx_h, WOP_SSU_H, H4, H2, DO_MACC) +RVVCALL(OPIVX3, vwmaccsu_vx_w, WOP_SSU_W, H8, H4, DO_MACC) +RVVCALL(OPIVX3, vwmaccus_vx_b, WOP_SUS_B, H2, H1, DO_MACC) +RVVCALL(OPIVX3, vwmaccus_vx_h, WOP_SUS_H, H4, H2, DO_MACC) +RVVCALL(OPIVX3, vwmaccus_vx_w, WOP_SUS_W, H8, H4, DO_MACC) +GEN_VEXT_VX(vwmaccu_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwmaccu_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwmaccu_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwmacc_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwmacc_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwmacc_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwmaccsu_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwmaccsu_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwmaccsu_vx_w, 4, 8, clearq) +GEN_VEXT_VX(vwmaccus_vx_b, 1, 2, clearh) +GEN_VEXT_VX(vwmaccus_vx_h, 2, 4, clearl) +GEN_VEXT_VX(vwmaccus_vx_w, 4, 8, clearq) + +/* Vector Integer Merge and Move Instructions */ +#define GEN_VEXT_VMV_VV(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *vs1, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ + *((ETYPE *)vd + H(i)) = s1; \ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VEXT_VMV_VV(vmv_v_v_b, int8_t, H1, clearb) +GEN_VEXT_VMV_VV(vmv_v_v_h, int16_t, H2, clearh) +GEN_VEXT_VMV_VV(vmv_v_v_w, int32_t, H4, clearl) +GEN_VEXT_VMV_VV(vmv_v_v_d, int64_t, H8, clearq) + +#define GEN_VEXT_VMV_VX(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, uint64_t s1, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + *((ETYPE *)vd + H(i)) = (ETYPE)s1; \ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VEXT_VMV_VX(vmv_v_x_b, int8_t, H1, clearb) +GEN_VEXT_VMV_VX(vmv_v_x_h, int16_t, H2, clearh) +GEN_VEXT_VMV_VX(vmv_v_x_w, int32_t, H4, clearl) +GEN_VEXT_VMV_VX(vmv_v_x_d, int64_t, H8, clearq) + +#define GEN_VEXT_VMERGE_VV(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE *vt = (!vext_elem_mask(v0, mlen, i) ? vs2 : vs1); \ + *((ETYPE *)vd + H(i)) = *(vt + H(i)); \ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VEXT_VMERGE_VV(vmerge_vvm_b, int8_t, H1, clearb) +GEN_VEXT_VMERGE_VV(vmerge_vvm_h, int16_t, H2, clearh) +GEN_VEXT_VMERGE_VV(vmerge_vvm_w, int32_t, H4, clearl) +GEN_VEXT_VMERGE_VV(vmerge_vvm_d, int64_t, H8, clearq) + +#define GEN_VEXT_VMERGE_VX(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + ETYPE d = (!vext_elem_mask(v0, mlen, i) ? s2 : \ + (ETYPE)(target_long)s1); \ + *((ETYPE *)vd + H(i)) = d; \ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VEXT_VMERGE_VX(vmerge_vxm_b, int8_t, H1, clearb) +GEN_VEXT_VMERGE_VX(vmerge_vxm_h, int16_t, H2, clearh) +GEN_VEXT_VMERGE_VX(vmerge_vxm_w, int32_t, H4, clearl) +GEN_VEXT_VMERGE_VX(vmerge_vxm_d, int64_t, H8, clearq) + +/* + *** Vector Fixed-Point Arithmetic Instructions + */ + +/* Vector Single-Width Saturating Add and Subtract */ + +/* + * As fixed point instructions probably have round mode and saturation, + * define common macros for fixed point here. + */ +typedef void opivv2_rm_fn(void *vd, void *vs1, void *vs2, int i, + CPURISCVState *env, int vxrm); + +#define OPIVV2_RM(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ +static inline void \ +do_##NAME(void *vd, void *vs1, void *vs2, int i, \ + CPURISCVState *env, int vxrm) \ +{ \ + TX1 s1 = *((T1 *)vs1 + HS1(i)); \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(env, vxrm, s2, s1); \ +} + +static inline void +vext_vv_rm_1(void *vd, void *v0, void *vs1, void *vs2, + CPURISCVState *env, + uint32_t vl, uint32_t vm, uint32_t mlen, int vxrm, + opivv2_rm_fn *fn) +{ + for (uint32_t i = 0; i < vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + fn(vd, vs1, vs2, i, env, vxrm); + } +} + +static inline void +vext_vv_rm_2(void *vd, void *v0, void *vs1, void *vs2, + CPURISCVState *env, + uint32_t desc, uint32_t esz, uint32_t dsz, + opivv2_rm_fn *fn, clear_fn *clearfn) +{ + uint32_t vlmax = vext_maxsz(desc) / esz; + uint32_t mlen = vext_mlen(desc); + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + + switch (env->vxrm) { + case 0: /* rnu */ + vext_vv_rm_1(vd, v0, vs1, vs2, + env, vl, vm, mlen, 0, fn); + break; + case 1: /* rne */ + vext_vv_rm_1(vd, v0, vs1, vs2, + env, vl, vm, mlen, 1, fn); + break; + case 2: /* rdn */ + vext_vv_rm_1(vd, v0, vs1, vs2, + env, vl, vm, mlen, 2, fn); + break; + default: /* rod */ + vext_vv_rm_1(vd, v0, vs1, vs2, + env, vl, vm, mlen, 3, fn); + break; + } + + clearfn(vd, vl, vl * dsz, vlmax * dsz); +} + +/* generate helpers for fixed point instructions with OPIVV format */ +#define GEN_VEXT_VV_RM(NAME, ESZ, DSZ, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vext_vv_rm_2(vd, v0, vs1, vs2, env, desc, ESZ, DSZ, \ + do_##NAME, CLEAR_FN); \ +} + +static inline uint8_t saddu8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b) +{ + uint8_t res = a + b; + if (res < a) { + res = UINT8_MAX; + env->vxsat = 0x1; + } + return res; +} + +static inline uint16_t saddu16(CPURISCVState *env, int vxrm, uint16_t a, + uint16_t b) +{ + uint16_t res = a + b; + if (res < a) { + res = UINT16_MAX; + env->vxsat = 0x1; + } + return res; +} + +static inline uint32_t saddu32(CPURISCVState *env, int vxrm, uint32_t a, + uint32_t b) +{ + uint32_t res = a + b; + if (res < a) { + res = UINT32_MAX; + env->vxsat = 0x1; + } + return res; +} + +static inline uint64_t saddu64(CPURISCVState *env, int vxrm, uint64_t a, + uint64_t b) +{ + uint64_t res = a + b; + if (res < a) { + res = UINT64_MAX; + env->vxsat = 0x1; + } + return res; +} + +RVVCALL(OPIVV2_RM, vsaddu_vv_b, OP_UUU_B, H1, H1, H1, saddu8) +RVVCALL(OPIVV2_RM, vsaddu_vv_h, OP_UUU_H, H2, H2, H2, saddu16) +RVVCALL(OPIVV2_RM, vsaddu_vv_w, OP_UUU_W, H4, H4, H4, saddu32) +RVVCALL(OPIVV2_RM, vsaddu_vv_d, OP_UUU_D, H8, H8, H8, saddu64) +GEN_VEXT_VV_RM(vsaddu_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vsaddu_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vsaddu_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vsaddu_vv_d, 8, 8, clearq) + +typedef void opivx2_rm_fn(void *vd, target_long s1, void *vs2, int i, + CPURISCVState *env, int vxrm); + +#define OPIVX2_RM(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ +static inline void \ +do_##NAME(void *vd, target_long s1, void *vs2, int i, \ + CPURISCVState *env, int vxrm) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(env, vxrm, s2, (TX1)(T1)s1); \ +} + +static inline void +vext_vx_rm_1(void *vd, void *v0, target_long s1, void *vs2, + CPURISCVState *env, + uint32_t vl, uint32_t vm, uint32_t mlen, int vxrm, + opivx2_rm_fn *fn) +{ + for (uint32_t i = 0; i < vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + fn(vd, s1, vs2, i, env, vxrm); + } +} + +static inline void +vext_vx_rm_2(void *vd, void *v0, target_long s1, void *vs2, + CPURISCVState *env, + uint32_t desc, uint32_t esz, uint32_t dsz, + opivx2_rm_fn *fn, clear_fn *clearfn) +{ + uint32_t vlmax = vext_maxsz(desc) / esz; + uint32_t mlen = vext_mlen(desc); + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + + switch (env->vxrm) { + case 0: /* rnu */ + vext_vx_rm_1(vd, v0, s1, vs2, + env, vl, vm, mlen, 0, fn); + break; + case 1: /* rne */ + vext_vx_rm_1(vd, v0, s1, vs2, + env, vl, vm, mlen, 1, fn); + break; + case 2: /* rdn */ + vext_vx_rm_1(vd, v0, s1, vs2, + env, vl, vm, mlen, 2, fn); + break; + default: /* rod */ + vext_vx_rm_1(vd, v0, s1, vs2, + env, vl, vm, mlen, 3, fn); + break; + } + + clearfn(vd, vl, vl * dsz, vlmax * dsz); +} + +/* generate helpers for fixed point instructions with OPIVX format */ +#define GEN_VEXT_VX_RM(NAME, ESZ, DSZ, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + vext_vx_rm_2(vd, v0, s1, vs2, env, desc, ESZ, DSZ, \ + do_##NAME, CLEAR_FN); \ +} + +RVVCALL(OPIVX2_RM, vsaddu_vx_b, OP_UUU_B, H1, H1, saddu8) +RVVCALL(OPIVX2_RM, vsaddu_vx_h, OP_UUU_H, H2, H2, saddu16) +RVVCALL(OPIVX2_RM, vsaddu_vx_w, OP_UUU_W, H4, H4, saddu32) +RVVCALL(OPIVX2_RM, vsaddu_vx_d, OP_UUU_D, H8, H8, saddu64) +GEN_VEXT_VX_RM(vsaddu_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vsaddu_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vsaddu_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vsaddu_vx_d, 8, 8, clearq) + +static inline int8_t sadd8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) +{ + int8_t res = a + b; + if ((res ^ a) & (res ^ b) & INT8_MIN) { + res = a > 0 ? INT8_MAX : INT8_MIN; + env->vxsat = 0x1; + } + return res; +} + +static inline int16_t sadd16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) +{ + int16_t res = a + b; + if ((res ^ a) & (res ^ b) & INT16_MIN) { + res = a > 0 ? INT16_MAX : INT16_MIN; + env->vxsat = 0x1; + } + return res; +} + +static inline int32_t sadd32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +{ + int32_t res = a + b; + if ((res ^ a) & (res ^ b) & INT32_MIN) { + res = a > 0 ? INT32_MAX : INT32_MIN; + env->vxsat = 0x1; + } + return res; +} + +static inline int64_t sadd64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +{ + int64_t res = a + b; + if ((res ^ a) & (res ^ b) & INT64_MIN) { + res = a > 0 ? INT64_MAX : INT64_MIN; + env->vxsat = 0x1; + } + return res; +} + +RVVCALL(OPIVV2_RM, vsadd_vv_b, OP_SSS_B, H1, H1, H1, sadd8) +RVVCALL(OPIVV2_RM, vsadd_vv_h, OP_SSS_H, H2, H2, H2, sadd16) +RVVCALL(OPIVV2_RM, vsadd_vv_w, OP_SSS_W, H4, H4, H4, sadd32) +RVVCALL(OPIVV2_RM, vsadd_vv_d, OP_SSS_D, H8, H8, H8, sadd64) +GEN_VEXT_VV_RM(vsadd_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vsadd_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vsadd_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vsadd_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2_RM, vsadd_vx_b, OP_SSS_B, H1, H1, sadd8) +RVVCALL(OPIVX2_RM, vsadd_vx_h, OP_SSS_H, H2, H2, sadd16) +RVVCALL(OPIVX2_RM, vsadd_vx_w, OP_SSS_W, H4, H4, sadd32) +RVVCALL(OPIVX2_RM, vsadd_vx_d, OP_SSS_D, H8, H8, sadd64) +GEN_VEXT_VX_RM(vsadd_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vsadd_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vsadd_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vsadd_vx_d, 8, 8, clearq) + +static inline uint8_t ssubu8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b) +{ + uint8_t res = a - b; + if (res > a) { + res = 0; + env->vxsat = 0x1; + } + return res; +} + +static inline uint16_t ssubu16(CPURISCVState *env, int vxrm, uint16_t a, + uint16_t b) +{ + uint16_t res = a - b; + if (res > a) { + res = 0; + env->vxsat = 0x1; + } + return res; +} + +static inline uint32_t ssubu32(CPURISCVState *env, int vxrm, uint32_t a, + uint32_t b) +{ + uint32_t res = a - b; + if (res > a) { + res = 0; + env->vxsat = 0x1; + } + return res; +} + +static inline uint64_t ssubu64(CPURISCVState *env, int vxrm, uint64_t a, + uint64_t b) +{ + uint64_t res = a - b; + if (res > a) { + res = 0; + env->vxsat = 0x1; + } + return res; +} + +RVVCALL(OPIVV2_RM, vssubu_vv_b, OP_UUU_B, H1, H1, H1, ssubu8) +RVVCALL(OPIVV2_RM, vssubu_vv_h, OP_UUU_H, H2, H2, H2, ssubu16) +RVVCALL(OPIVV2_RM, vssubu_vv_w, OP_UUU_W, H4, H4, H4, ssubu32) +RVVCALL(OPIVV2_RM, vssubu_vv_d, OP_UUU_D, H8, H8, H8, ssubu64) +GEN_VEXT_VV_RM(vssubu_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vssubu_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vssubu_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vssubu_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2_RM, vssubu_vx_b, OP_UUU_B, H1, H1, ssubu8) +RVVCALL(OPIVX2_RM, vssubu_vx_h, OP_UUU_H, H2, H2, ssubu16) +RVVCALL(OPIVX2_RM, vssubu_vx_w, OP_UUU_W, H4, H4, ssubu32) +RVVCALL(OPIVX2_RM, vssubu_vx_d, OP_UUU_D, H8, H8, ssubu64) +GEN_VEXT_VX_RM(vssubu_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vssubu_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vssubu_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vssubu_vx_d, 8, 8, clearq) + +static inline int8_t ssub8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) +{ + int8_t res = a - b; + if ((res ^ a) & (a ^ b) & INT8_MIN) { + res = a > 0 ? INT8_MAX : INT8_MIN; + env->vxsat = 0x1; + } + return res; +} + +static inline int16_t ssub16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) +{ + int16_t res = a - b; + if ((res ^ a) & (a ^ b) & INT16_MIN) { + res = a > 0 ? INT16_MAX : INT16_MIN; + env->vxsat = 0x1; + } + return res; +} + +static inline int32_t ssub32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +{ + int32_t res = a - b; + if ((res ^ a) & (a ^ b) & INT32_MIN) { + res = a > 0 ? INT32_MAX : INT32_MIN; + env->vxsat = 0x1; + } + return res; +} + +static inline int64_t ssub64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +{ + int64_t res = a - b; + if ((res ^ a) & (a ^ b) & INT64_MIN) { + res = a > 0 ? INT64_MAX : INT64_MIN; + env->vxsat = 0x1; + } + return res; +} + +RVVCALL(OPIVV2_RM, vssub_vv_b, OP_SSS_B, H1, H1, H1, ssub8) +RVVCALL(OPIVV2_RM, vssub_vv_h, OP_SSS_H, H2, H2, H2, ssub16) +RVVCALL(OPIVV2_RM, vssub_vv_w, OP_SSS_W, H4, H4, H4, ssub32) +RVVCALL(OPIVV2_RM, vssub_vv_d, OP_SSS_D, H8, H8, H8, ssub64) +GEN_VEXT_VV_RM(vssub_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vssub_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vssub_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vssub_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2_RM, vssub_vx_b, OP_SSS_B, H1, H1, ssub8) +RVVCALL(OPIVX2_RM, vssub_vx_h, OP_SSS_H, H2, H2, ssub16) +RVVCALL(OPIVX2_RM, vssub_vx_w, OP_SSS_W, H4, H4, ssub32) +RVVCALL(OPIVX2_RM, vssub_vx_d, OP_SSS_D, H8, H8, ssub64) +GEN_VEXT_VX_RM(vssub_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vssub_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vssub_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vssub_vx_d, 8, 8, clearq) + +/* Vector Single-Width Averaging Add and Subtract */ +static inline uint8_t get_round(int vxrm, uint64_t v, uint8_t shift) +{ + uint8_t d = extract64(v, shift, 1); + uint8_t d1; + uint64_t D1, D2; + + if (shift == 0 || shift > 64) { + return 0; + } + + d1 = extract64(v, shift - 1, 1); + D1 = extract64(v, 0, shift); + if (vxrm == 0) { /* round-to-nearest-up (add +0.5 LSB) */ + return d1; + } else if (vxrm == 1) { /* round-to-nearest-even */ + if (shift > 1) { + D2 = extract64(v, 0, shift - 1); + return d1 & ((D2 != 0) | d); + } else { + return d1 & d; + } + } else if (vxrm == 3) { /* round-to-odd (OR bits into LSB, aka "jam") */ + return !d & (D1 != 0); + } + return 0; /* round-down (truncate) */ +} + +static inline int32_t aadd32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +{ + int64_t res = (int64_t)a + b; + uint8_t round = get_round(vxrm, res, 1); + + return (res >> 1) + round; +} + +static inline int64_t aadd64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +{ + int64_t res = a + b; + uint8_t round = get_round(vxrm, res, 1); + int64_t over = (res ^ a) & (res ^ b) & INT64_MIN; + + /* With signed overflow, bit 64 is inverse of bit 63. */ + return ((res >> 1) ^ over) + round; +} + +RVVCALL(OPIVV2_RM, vaadd_vv_b, OP_SSS_B, H1, H1, H1, aadd32) +RVVCALL(OPIVV2_RM, vaadd_vv_h, OP_SSS_H, H2, H2, H2, aadd32) +RVVCALL(OPIVV2_RM, vaadd_vv_w, OP_SSS_W, H4, H4, H4, aadd32) +RVVCALL(OPIVV2_RM, vaadd_vv_d, OP_SSS_D, H8, H8, H8, aadd64) +GEN_VEXT_VV_RM(vaadd_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vaadd_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vaadd_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vaadd_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2_RM, vaadd_vx_b, OP_SSS_B, H1, H1, aadd32) +RVVCALL(OPIVX2_RM, vaadd_vx_h, OP_SSS_H, H2, H2, aadd32) +RVVCALL(OPIVX2_RM, vaadd_vx_w, OP_SSS_W, H4, H4, aadd32) +RVVCALL(OPIVX2_RM, vaadd_vx_d, OP_SSS_D, H8, H8, aadd64) +GEN_VEXT_VX_RM(vaadd_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vaadd_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vaadd_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vaadd_vx_d, 8, 8, clearq) + +static inline int32_t asub32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +{ + int64_t res = (int64_t)a - b; + uint8_t round = get_round(vxrm, res, 1); + + return (res >> 1) + round; +} + +static inline int64_t asub64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +{ + int64_t res = (int64_t)a - b; + uint8_t round = get_round(vxrm, res, 1); + int64_t over = (res ^ a) & (a ^ b) & INT64_MIN; + + /* With signed overflow, bit 64 is inverse of bit 63. */ + return ((res >> 1) ^ over) + round; +} + +RVVCALL(OPIVV2_RM, vasub_vv_b, OP_SSS_B, H1, H1, H1, asub32) +RVVCALL(OPIVV2_RM, vasub_vv_h, OP_SSS_H, H2, H2, H2, asub32) +RVVCALL(OPIVV2_RM, vasub_vv_w, OP_SSS_W, H4, H4, H4, asub32) +RVVCALL(OPIVV2_RM, vasub_vv_d, OP_SSS_D, H8, H8, H8, asub64) +GEN_VEXT_VV_RM(vasub_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vasub_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vasub_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vasub_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2_RM, vasub_vx_b, OP_SSS_B, H1, H1, asub32) +RVVCALL(OPIVX2_RM, vasub_vx_h, OP_SSS_H, H2, H2, asub32) +RVVCALL(OPIVX2_RM, vasub_vx_w, OP_SSS_W, H4, H4, asub32) +RVVCALL(OPIVX2_RM, vasub_vx_d, OP_SSS_D, H8, H8, asub64) +GEN_VEXT_VX_RM(vasub_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vasub_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vasub_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vasub_vx_d, 8, 8, clearq) + +/* Vector Single-Width Fractional Multiply with Rounding and Saturation */ +static inline int8_t vsmul8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) +{ + uint8_t round; + int16_t res; + + res = (int16_t)a * (int16_t)b; + round = get_round(vxrm, res, 7); + res = (res >> 7) + round; + + if (res > INT8_MAX) { + env->vxsat = 0x1; + return INT8_MAX; + } else if (res < INT8_MIN) { + env->vxsat = 0x1; + return INT8_MIN; + } else { + return res; + } +} + +static int16_t vsmul16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) +{ + uint8_t round; + int32_t res; + + res = (int32_t)a * (int32_t)b; + round = get_round(vxrm, res, 15); + res = (res >> 15) + round; + + if (res > INT16_MAX) { + env->vxsat = 0x1; + return INT16_MAX; + } else if (res < INT16_MIN) { + env->vxsat = 0x1; + return INT16_MIN; + } else { + return res; + } +} + +static int32_t vsmul32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +{ + uint8_t round; + int64_t res; + + res = (int64_t)a * (int64_t)b; + round = get_round(vxrm, res, 31); + res = (res >> 31) + round; + + if (res > INT32_MAX) { + env->vxsat = 0x1; + return INT32_MAX; + } else if (res < INT32_MIN) { + env->vxsat = 0x1; + return INT32_MIN; + } else { + return res; + } +} + +static int64_t vsmul64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +{ + uint8_t round; + uint64_t hi_64, lo_64; + int64_t res; + + if (a == INT64_MIN && b == INT64_MIN) { + env->vxsat = 1; + return INT64_MAX; + } + + muls64(&lo_64, &hi_64, a, b); + round = get_round(vxrm, lo_64, 63); + /* + * Cannot overflow, as there are always + * 2 sign bits after multiply. + */ + res = (hi_64 << 1) | (lo_64 >> 63); + if (round) { + if (res == INT64_MAX) { + env->vxsat = 1; + } else { + res += 1; + } + } + return res; +} + +RVVCALL(OPIVV2_RM, vsmul_vv_b, OP_SSS_B, H1, H1, H1, vsmul8) +RVVCALL(OPIVV2_RM, vsmul_vv_h, OP_SSS_H, H2, H2, H2, vsmul16) +RVVCALL(OPIVV2_RM, vsmul_vv_w, OP_SSS_W, H4, H4, H4, vsmul32) +RVVCALL(OPIVV2_RM, vsmul_vv_d, OP_SSS_D, H8, H8, H8, vsmul64) +GEN_VEXT_VV_RM(vsmul_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vsmul_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vsmul_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vsmul_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2_RM, vsmul_vx_b, OP_SSS_B, H1, H1, vsmul8) +RVVCALL(OPIVX2_RM, vsmul_vx_h, OP_SSS_H, H2, H2, vsmul16) +RVVCALL(OPIVX2_RM, vsmul_vx_w, OP_SSS_W, H4, H4, vsmul32) +RVVCALL(OPIVX2_RM, vsmul_vx_d, OP_SSS_D, H8, H8, vsmul64) +GEN_VEXT_VX_RM(vsmul_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vsmul_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vsmul_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vsmul_vx_d, 8, 8, clearq) + +/* Vector Widening Saturating Scaled Multiply-Add */ +static inline uint16_t +vwsmaccu8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b, + uint16_t c) +{ + uint8_t round; + uint16_t res = (uint16_t)a * b; + + round = get_round(vxrm, res, 4); + res = (res >> 4) + round; + return saddu16(env, vxrm, c, res); +} + +static inline uint32_t +vwsmaccu16(CPURISCVState *env, int vxrm, uint16_t a, uint16_t b, + uint32_t c) +{ + uint8_t round; + uint32_t res = (uint32_t)a * b; + + round = get_round(vxrm, res, 8); + res = (res >> 8) + round; + return saddu32(env, vxrm, c, res); +} + +static inline uint64_t +vwsmaccu32(CPURISCVState *env, int vxrm, uint32_t a, uint32_t b, + uint64_t c) +{ + uint8_t round; + uint64_t res = (uint64_t)a * b; + + round = get_round(vxrm, res, 16); + res = (res >> 16) + round; + return saddu64(env, vxrm, c, res); +} + +#define OPIVV3_RM(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ +static inline void \ +do_##NAME(void *vd, void *vs1, void *vs2, int i, \ + CPURISCVState *env, int vxrm) \ +{ \ + TX1 s1 = *((T1 *)vs1 + HS1(i)); \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + TD d = *((TD *)vd + HD(i)); \ + *((TD *)vd + HD(i)) = OP(env, vxrm, s2, s1, d); \ +} + +RVVCALL(OPIVV3_RM, vwsmaccu_vv_b, WOP_UUU_B, H2, H1, H1, vwsmaccu8) +RVVCALL(OPIVV3_RM, vwsmaccu_vv_h, WOP_UUU_H, H4, H2, H2, vwsmaccu16) +RVVCALL(OPIVV3_RM, vwsmaccu_vv_w, WOP_UUU_W, H8, H4, H4, vwsmaccu32) +GEN_VEXT_VV_RM(vwsmaccu_vv_b, 1, 2, clearh) +GEN_VEXT_VV_RM(vwsmaccu_vv_h, 2, 4, clearl) +GEN_VEXT_VV_RM(vwsmaccu_vv_w, 4, 8, clearq) + +#define OPIVX3_RM(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ +static inline void \ +do_##NAME(void *vd, target_long s1, void *vs2, int i, \ + CPURISCVState *env, int vxrm) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + TD d = *((TD *)vd + HD(i)); \ + *((TD *)vd + HD(i)) = OP(env, vxrm, s2, (TX1)(T1)s1, d); \ +} + +RVVCALL(OPIVX3_RM, vwsmaccu_vx_b, WOP_UUU_B, H2, H1, vwsmaccu8) +RVVCALL(OPIVX3_RM, vwsmaccu_vx_h, WOP_UUU_H, H4, H2, vwsmaccu16) +RVVCALL(OPIVX3_RM, vwsmaccu_vx_w, WOP_UUU_W, H8, H4, vwsmaccu32) +GEN_VEXT_VX_RM(vwsmaccu_vx_b, 1, 2, clearh) +GEN_VEXT_VX_RM(vwsmaccu_vx_h, 2, 4, clearl) +GEN_VEXT_VX_RM(vwsmaccu_vx_w, 4, 8, clearq) + +static inline int16_t +vwsmacc8(CPURISCVState *env, int vxrm, int8_t a, int8_t b, int16_t c) +{ + uint8_t round; + int16_t res = (int16_t)a * b; + + round = get_round(vxrm, res, 4); + res = (res >> 4) + round; + return sadd16(env, vxrm, c, res); +} + +static inline int32_t +vwsmacc16(CPURISCVState *env, int vxrm, int16_t a, int16_t b, int32_t c) +{ + uint8_t round; + int32_t res = (int32_t)a * b; + + round = get_round(vxrm, res, 8); + res = (res >> 8) + round; + return sadd32(env, vxrm, c, res); + +} + +static inline int64_t +vwsmacc32(CPURISCVState *env, int vxrm, int32_t a, int32_t b, int64_t c) +{ + uint8_t round; + int64_t res = (int64_t)a * b; + + round = get_round(vxrm, res, 16); + res = (res >> 16) + round; + return sadd64(env, vxrm, c, res); +} + +RVVCALL(OPIVV3_RM, vwsmacc_vv_b, WOP_SSS_B, H2, H1, H1, vwsmacc8) +RVVCALL(OPIVV3_RM, vwsmacc_vv_h, WOP_SSS_H, H4, H2, H2, vwsmacc16) +RVVCALL(OPIVV3_RM, vwsmacc_vv_w, WOP_SSS_W, H8, H4, H4, vwsmacc32) +GEN_VEXT_VV_RM(vwsmacc_vv_b, 1, 2, clearh) +GEN_VEXT_VV_RM(vwsmacc_vv_h, 2, 4, clearl) +GEN_VEXT_VV_RM(vwsmacc_vv_w, 4, 8, clearq) +RVVCALL(OPIVX3_RM, vwsmacc_vx_b, WOP_SSS_B, H2, H1, vwsmacc8) +RVVCALL(OPIVX3_RM, vwsmacc_vx_h, WOP_SSS_H, H4, H2, vwsmacc16) +RVVCALL(OPIVX3_RM, vwsmacc_vx_w, WOP_SSS_W, H8, H4, vwsmacc32) +GEN_VEXT_VX_RM(vwsmacc_vx_b, 1, 2, clearh) +GEN_VEXT_VX_RM(vwsmacc_vx_h, 2, 4, clearl) +GEN_VEXT_VX_RM(vwsmacc_vx_w, 4, 8, clearq) + +static inline int16_t +vwsmaccsu8(CPURISCVState *env, int vxrm, uint8_t a, int8_t b, int16_t c) +{ + uint8_t round; + int16_t res = a * (int16_t)b; + + round = get_round(vxrm, res, 4); + res = (res >> 4) + round; + return ssub16(env, vxrm, c, res); +} + +static inline int32_t +vwsmaccsu16(CPURISCVState *env, int vxrm, uint16_t a, int16_t b, uint32_t c) +{ + uint8_t round; + int32_t res = a * (int32_t)b; + + round = get_round(vxrm, res, 8); + res = (res >> 8) + round; + return ssub32(env, vxrm, c, res); +} + +static inline int64_t +vwsmaccsu32(CPURISCVState *env, int vxrm, uint32_t a, int32_t b, int64_t c) +{ + uint8_t round; + int64_t res = a * (int64_t)b; + + round = get_round(vxrm, res, 16); + res = (res >> 16) + round; + return ssub64(env, vxrm, c, res); +} + +RVVCALL(OPIVV3_RM, vwsmaccsu_vv_b, WOP_SSU_B, H2, H1, H1, vwsmaccsu8) +RVVCALL(OPIVV3_RM, vwsmaccsu_vv_h, WOP_SSU_H, H4, H2, H2, vwsmaccsu16) +RVVCALL(OPIVV3_RM, vwsmaccsu_vv_w, WOP_SSU_W, H8, H4, H4, vwsmaccsu32) +GEN_VEXT_VV_RM(vwsmaccsu_vv_b, 1, 2, clearh) +GEN_VEXT_VV_RM(vwsmaccsu_vv_h, 2, 4, clearl) +GEN_VEXT_VV_RM(vwsmaccsu_vv_w, 4, 8, clearq) +RVVCALL(OPIVX3_RM, vwsmaccsu_vx_b, WOP_SSU_B, H2, H1, vwsmaccsu8) +RVVCALL(OPIVX3_RM, vwsmaccsu_vx_h, WOP_SSU_H, H4, H2, vwsmaccsu16) +RVVCALL(OPIVX3_RM, vwsmaccsu_vx_w, WOP_SSU_W, H8, H4, vwsmaccsu32) +GEN_VEXT_VX_RM(vwsmaccsu_vx_b, 1, 2, clearh) +GEN_VEXT_VX_RM(vwsmaccsu_vx_h, 2, 4, clearl) +GEN_VEXT_VX_RM(vwsmaccsu_vx_w, 4, 8, clearq) + +static inline int16_t +vwsmaccus8(CPURISCVState *env, int vxrm, int8_t a, uint8_t b, int16_t c) +{ + uint8_t round; + int16_t res = (int16_t)a * b; + + round = get_round(vxrm, res, 4); + res = (res >> 4) + round; + return ssub16(env, vxrm, c, res); +} + +static inline int32_t +vwsmaccus16(CPURISCVState *env, int vxrm, int16_t a, uint16_t b, int32_t c) +{ + uint8_t round; + int32_t res = (int32_t)a * b; + + round = get_round(vxrm, res, 8); + res = (res >> 8) + round; + return ssub32(env, vxrm, c, res); +} + +static inline int64_t +vwsmaccus32(CPURISCVState *env, int vxrm, int32_t a, uint32_t b, int64_t c) +{ + uint8_t round; + int64_t res = (int64_t)a * b; + + round = get_round(vxrm, res, 16); + res = (res >> 16) + round; + return ssub64(env, vxrm, c, res); +} + +RVVCALL(OPIVX3_RM, vwsmaccus_vx_b, WOP_SUS_B, H2, H1, vwsmaccus8) +RVVCALL(OPIVX3_RM, vwsmaccus_vx_h, WOP_SUS_H, H4, H2, vwsmaccus16) +RVVCALL(OPIVX3_RM, vwsmaccus_vx_w, WOP_SUS_W, H8, H4, vwsmaccus32) +GEN_VEXT_VX_RM(vwsmaccus_vx_b, 1, 2, clearh) +GEN_VEXT_VX_RM(vwsmaccus_vx_h, 2, 4, clearl) +GEN_VEXT_VX_RM(vwsmaccus_vx_w, 4, 8, clearq) + +/* Vector Single-Width Scaling Shift Instructions */ +static inline uint8_t +vssrl8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b) +{ + uint8_t round, shift = b & 0x7; + uint8_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + return res; +} +static inline uint16_t +vssrl16(CPURISCVState *env, int vxrm, uint16_t a, uint16_t b) +{ + uint8_t round, shift = b & 0xf; + uint16_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + return res; +} +static inline uint32_t +vssrl32(CPURISCVState *env, int vxrm, uint32_t a, uint32_t b) +{ + uint8_t round, shift = b & 0x1f; + uint32_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + return res; +} +static inline uint64_t +vssrl64(CPURISCVState *env, int vxrm, uint64_t a, uint64_t b) +{ + uint8_t round, shift = b & 0x3f; + uint64_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + return res; +} +RVVCALL(OPIVV2_RM, vssrl_vv_b, OP_UUU_B, H1, H1, H1, vssrl8) +RVVCALL(OPIVV2_RM, vssrl_vv_h, OP_UUU_H, H2, H2, H2, vssrl16) +RVVCALL(OPIVV2_RM, vssrl_vv_w, OP_UUU_W, H4, H4, H4, vssrl32) +RVVCALL(OPIVV2_RM, vssrl_vv_d, OP_UUU_D, H8, H8, H8, vssrl64) +GEN_VEXT_VV_RM(vssrl_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vssrl_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vssrl_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vssrl_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2_RM, vssrl_vx_b, OP_UUU_B, H1, H1, vssrl8) +RVVCALL(OPIVX2_RM, vssrl_vx_h, OP_UUU_H, H2, H2, vssrl16) +RVVCALL(OPIVX2_RM, vssrl_vx_w, OP_UUU_W, H4, H4, vssrl32) +RVVCALL(OPIVX2_RM, vssrl_vx_d, OP_UUU_D, H8, H8, vssrl64) +GEN_VEXT_VX_RM(vssrl_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vssrl_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vssrl_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vssrl_vx_d, 8, 8, clearq) + +static inline int8_t +vssra8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) +{ + uint8_t round, shift = b & 0x7; + int8_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + return res; +} +static inline int16_t +vssra16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) +{ + uint8_t round, shift = b & 0xf; + int16_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + return res; +} +static inline int32_t +vssra32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +{ + uint8_t round, shift = b & 0x1f; + int32_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + return res; +} +static inline int64_t +vssra64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +{ + uint8_t round, shift = b & 0x3f; + int64_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + return res; +} + +RVVCALL(OPIVV2_RM, vssra_vv_b, OP_SSS_B, H1, H1, H1, vssra8) +RVVCALL(OPIVV2_RM, vssra_vv_h, OP_SSS_H, H2, H2, H2, vssra16) +RVVCALL(OPIVV2_RM, vssra_vv_w, OP_SSS_W, H4, H4, H4, vssra32) +RVVCALL(OPIVV2_RM, vssra_vv_d, OP_SSS_D, H8, H8, H8, vssra64) +GEN_VEXT_VV_RM(vssra_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vssra_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vssra_vv_w, 4, 4, clearl) +GEN_VEXT_VV_RM(vssra_vv_d, 8, 8, clearq) + +RVVCALL(OPIVX2_RM, vssra_vx_b, OP_SSS_B, H1, H1, vssra8) +RVVCALL(OPIVX2_RM, vssra_vx_h, OP_SSS_H, H2, H2, vssra16) +RVVCALL(OPIVX2_RM, vssra_vx_w, OP_SSS_W, H4, H4, vssra32) +RVVCALL(OPIVX2_RM, vssra_vx_d, OP_SSS_D, H8, H8, vssra64) +GEN_VEXT_VX_RM(vssra_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vssra_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vssra_vx_w, 4, 4, clearl) +GEN_VEXT_VX_RM(vssra_vx_d, 8, 8, clearq) + +/* Vector Narrowing Fixed-Point Clip Instructions */ +static inline int8_t +vnclip8(CPURISCVState *env, int vxrm, int16_t a, int8_t b) +{ + uint8_t round, shift = b & 0xf; + int16_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + if (res > INT8_MAX) { + env->vxsat = 0x1; + return INT8_MAX; + } else if (res < INT8_MIN) { + env->vxsat = 0x1; + return INT8_MIN; + } else { + return res; + } +} + +static inline int16_t +vnclip16(CPURISCVState *env, int vxrm, int32_t a, int16_t b) +{ + uint8_t round, shift = b & 0x1f; + int32_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + if (res > INT16_MAX) { + env->vxsat = 0x1; + return INT16_MAX; + } else if (res < INT16_MIN) { + env->vxsat = 0x1; + return INT16_MIN; + } else { + return res; + } +} + +static inline int32_t +vnclip32(CPURISCVState *env, int vxrm, int64_t a, int32_t b) +{ + uint8_t round, shift = b & 0x3f; + int64_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + if (res > INT32_MAX) { + env->vxsat = 0x1; + return INT32_MAX; + } else if (res < INT32_MIN) { + env->vxsat = 0x1; + return INT32_MIN; + } else { + return res; + } +} + +RVVCALL(OPIVV2_RM, vnclip_vv_b, NOP_SSS_B, H1, H2, H1, vnclip8) +RVVCALL(OPIVV2_RM, vnclip_vv_h, NOP_SSS_H, H2, H4, H2, vnclip16) +RVVCALL(OPIVV2_RM, vnclip_vv_w, NOP_SSS_W, H4, H8, H4, vnclip32) +GEN_VEXT_VV_RM(vnclip_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vnclip_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vnclip_vv_w, 4, 4, clearl) + +RVVCALL(OPIVX2_RM, vnclip_vx_b, NOP_SSS_B, H1, H2, vnclip8) +RVVCALL(OPIVX2_RM, vnclip_vx_h, NOP_SSS_H, H2, H4, vnclip16) +RVVCALL(OPIVX2_RM, vnclip_vx_w, NOP_SSS_W, H4, H8, vnclip32) +GEN_VEXT_VX_RM(vnclip_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vnclip_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vnclip_vx_w, 4, 4, clearl) + +static inline uint8_t +vnclipu8(CPURISCVState *env, int vxrm, uint16_t a, uint8_t b) +{ + uint8_t round, shift = b & 0xf; + uint16_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + if (res > UINT8_MAX) { + env->vxsat = 0x1; + return UINT8_MAX; + } else { + return res; + } +} + +static inline uint16_t +vnclipu16(CPURISCVState *env, int vxrm, uint32_t a, uint16_t b) +{ + uint8_t round, shift = b & 0x1f; + uint32_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + if (res > UINT16_MAX) { + env->vxsat = 0x1; + return UINT16_MAX; + } else { + return res; + } +} + +static inline uint32_t +vnclipu32(CPURISCVState *env, int vxrm, uint64_t a, uint32_t b) +{ + uint8_t round, shift = b & 0x3f; + int64_t res; + + round = get_round(vxrm, a, shift); + res = (a >> shift) + round; + if (res > UINT32_MAX) { + env->vxsat = 0x1; + return UINT32_MAX; + } else { + return res; + } +} + +RVVCALL(OPIVV2_RM, vnclipu_vv_b, NOP_UUU_B, H1, H2, H1, vnclipu8) +RVVCALL(OPIVV2_RM, vnclipu_vv_h, NOP_UUU_H, H2, H4, H2, vnclipu16) +RVVCALL(OPIVV2_RM, vnclipu_vv_w, NOP_UUU_W, H4, H8, H4, vnclipu32) +GEN_VEXT_VV_RM(vnclipu_vv_b, 1, 1, clearb) +GEN_VEXT_VV_RM(vnclipu_vv_h, 2, 2, clearh) +GEN_VEXT_VV_RM(vnclipu_vv_w, 4, 4, clearl) + +RVVCALL(OPIVX2_RM, vnclipu_vx_b, NOP_UUU_B, H1, H2, vnclipu8) +RVVCALL(OPIVX2_RM, vnclipu_vx_h, NOP_UUU_H, H2, H4, vnclipu16) +RVVCALL(OPIVX2_RM, vnclipu_vx_w, NOP_UUU_W, H4, H8, vnclipu32) +GEN_VEXT_VX_RM(vnclipu_vx_b, 1, 1, clearb) +GEN_VEXT_VX_RM(vnclipu_vx_h, 2, 2, clearh) +GEN_VEXT_VX_RM(vnclipu_vx_w, 4, 4, clearl) + +/* + *** Vector Float Point Arithmetic Instructions + */ +/* Vector Single-Width Floating-Point Add/Subtract Instructions */ +#define OPFVV2(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ +static void do_##NAME(void *vd, void *vs1, void *vs2, int i, \ + CPURISCVState *env) \ +{ \ + TX1 s1 = *((T1 *)vs1 + HS1(i)); \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2, s1, &env->fp_status); \ +} + +#define GEN_VEXT_VV_ENV(NAME, ESZ, DSZ, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t vlmax = vext_maxsz(desc) / ESZ; \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + do_##NAME(vd, vs1, vs2, i, env); \ + } \ + CLEAR_FN(vd, vl, vl * DSZ, vlmax * DSZ); \ +} + +RVVCALL(OPFVV2, vfadd_vv_h, OP_UUU_H, H2, H2, H2, float16_add) +RVVCALL(OPFVV2, vfadd_vv_w, OP_UUU_W, H4, H4, H4, float32_add) +RVVCALL(OPFVV2, vfadd_vv_d, OP_UUU_D, H8, H8, H8, float64_add) +GEN_VEXT_VV_ENV(vfadd_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfadd_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfadd_vv_d, 8, 8, clearq) + +#define OPFVF2(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ +static void do_##NAME(void *vd, uint64_t s1, void *vs2, int i, \ + CPURISCVState *env) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2, (TX1)(T1)s1, &env->fp_status);\ +} + +#define GEN_VEXT_VF(NAME, ESZ, DSZ, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, uint64_t s1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t vlmax = vext_maxsz(desc) / ESZ; \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + do_##NAME(vd, s1, vs2, i, env); \ + } \ + CLEAR_FN(vd, vl, vl * DSZ, vlmax * DSZ); \ +} + +RVVCALL(OPFVF2, vfadd_vf_h, OP_UUU_H, H2, H2, float16_add) +RVVCALL(OPFVF2, vfadd_vf_w, OP_UUU_W, H4, H4, float32_add) +RVVCALL(OPFVF2, vfadd_vf_d, OP_UUU_D, H8, H8, float64_add) +GEN_VEXT_VF(vfadd_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfadd_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfadd_vf_d, 8, 8, clearq) + +RVVCALL(OPFVV2, vfsub_vv_h, OP_UUU_H, H2, H2, H2, float16_sub) +RVVCALL(OPFVV2, vfsub_vv_w, OP_UUU_W, H4, H4, H4, float32_sub) +RVVCALL(OPFVV2, vfsub_vv_d, OP_UUU_D, H8, H8, H8, float64_sub) +GEN_VEXT_VV_ENV(vfsub_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfsub_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfsub_vv_d, 8, 8, clearq) +RVVCALL(OPFVF2, vfsub_vf_h, OP_UUU_H, H2, H2, float16_sub) +RVVCALL(OPFVF2, vfsub_vf_w, OP_UUU_W, H4, H4, float32_sub) +RVVCALL(OPFVF2, vfsub_vf_d, OP_UUU_D, H8, H8, float64_sub) +GEN_VEXT_VF(vfsub_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfsub_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfsub_vf_d, 8, 8, clearq) + +static uint16_t float16_rsub(uint16_t a, uint16_t b, float_status *s) +{ + return float16_sub(b, a, s); +} + +static uint32_t float32_rsub(uint32_t a, uint32_t b, float_status *s) +{ + return float32_sub(b, a, s); +} + +static uint64_t float64_rsub(uint64_t a, uint64_t b, float_status *s) +{ + return float64_sub(b, a, s); +} + +RVVCALL(OPFVF2, vfrsub_vf_h, OP_UUU_H, H2, H2, float16_rsub) +RVVCALL(OPFVF2, vfrsub_vf_w, OP_UUU_W, H4, H4, float32_rsub) +RVVCALL(OPFVF2, vfrsub_vf_d, OP_UUU_D, H8, H8, float64_rsub) +GEN_VEXT_VF(vfrsub_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfrsub_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfrsub_vf_d, 8, 8, clearq) + +/* Vector Widening Floating-Point Add/Subtract Instructions */ +static uint32_t vfwadd16(uint16_t a, uint16_t b, float_status *s) +{ + return float32_add(float16_to_float32(a, true, s), + float16_to_float32(b, true, s), s); +} + +static uint64_t vfwadd32(uint32_t a, uint32_t b, float_status *s) +{ + return float64_add(float32_to_float64(a, s), + float32_to_float64(b, s), s); + +} + +RVVCALL(OPFVV2, vfwadd_vv_h, WOP_UUU_H, H4, H2, H2, vfwadd16) +RVVCALL(OPFVV2, vfwadd_vv_w, WOP_UUU_W, H8, H4, H4, vfwadd32) +GEN_VEXT_VV_ENV(vfwadd_vv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwadd_vv_w, 4, 8, clearq) +RVVCALL(OPFVF2, vfwadd_vf_h, WOP_UUU_H, H4, H2, vfwadd16) +RVVCALL(OPFVF2, vfwadd_vf_w, WOP_UUU_W, H8, H4, vfwadd32) +GEN_VEXT_VF(vfwadd_vf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwadd_vf_w, 4, 8, clearq) + +static uint32_t vfwsub16(uint16_t a, uint16_t b, float_status *s) +{ + return float32_sub(float16_to_float32(a, true, s), + float16_to_float32(b, true, s), s); +} + +static uint64_t vfwsub32(uint32_t a, uint32_t b, float_status *s) +{ + return float64_sub(float32_to_float64(a, s), + float32_to_float64(b, s), s); + +} + +RVVCALL(OPFVV2, vfwsub_vv_h, WOP_UUU_H, H4, H2, H2, vfwsub16) +RVVCALL(OPFVV2, vfwsub_vv_w, WOP_UUU_W, H8, H4, H4, vfwsub32) +GEN_VEXT_VV_ENV(vfwsub_vv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwsub_vv_w, 4, 8, clearq) +RVVCALL(OPFVF2, vfwsub_vf_h, WOP_UUU_H, H4, H2, vfwsub16) +RVVCALL(OPFVF2, vfwsub_vf_w, WOP_UUU_W, H8, H4, vfwsub32) +GEN_VEXT_VF(vfwsub_vf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwsub_vf_w, 4, 8, clearq) + +static uint32_t vfwaddw16(uint32_t a, uint16_t b, float_status *s) +{ + return float32_add(a, float16_to_float32(b, true, s), s); +} + +static uint64_t vfwaddw32(uint64_t a, uint32_t b, float_status *s) +{ + return float64_add(a, float32_to_float64(b, s), s); +} + +RVVCALL(OPFVV2, vfwadd_wv_h, WOP_WUUU_H, H4, H2, H2, vfwaddw16) +RVVCALL(OPFVV2, vfwadd_wv_w, WOP_WUUU_W, H8, H4, H4, vfwaddw32) +GEN_VEXT_VV_ENV(vfwadd_wv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwadd_wv_w, 4, 8, clearq) +RVVCALL(OPFVF2, vfwadd_wf_h, WOP_WUUU_H, H4, H2, vfwaddw16) +RVVCALL(OPFVF2, vfwadd_wf_w, WOP_WUUU_W, H8, H4, vfwaddw32) +GEN_VEXT_VF(vfwadd_wf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwadd_wf_w, 4, 8, clearq) + +static uint32_t vfwsubw16(uint32_t a, uint16_t b, float_status *s) +{ + return float32_sub(a, float16_to_float32(b, true, s), s); +} + +static uint64_t vfwsubw32(uint64_t a, uint32_t b, float_status *s) +{ + return float64_sub(a, float32_to_float64(b, s), s); +} + +RVVCALL(OPFVV2, vfwsub_wv_h, WOP_WUUU_H, H4, H2, H2, vfwsubw16) +RVVCALL(OPFVV2, vfwsub_wv_w, WOP_WUUU_W, H8, H4, H4, vfwsubw32) +GEN_VEXT_VV_ENV(vfwsub_wv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwsub_wv_w, 4, 8, clearq) +RVVCALL(OPFVF2, vfwsub_wf_h, WOP_WUUU_H, H4, H2, vfwsubw16) +RVVCALL(OPFVF2, vfwsub_wf_w, WOP_WUUU_W, H8, H4, vfwsubw32) +GEN_VEXT_VF(vfwsub_wf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwsub_wf_w, 4, 8, clearq) + +/* Vector Single-Width Floating-Point Multiply/Divide Instructions */ +RVVCALL(OPFVV2, vfmul_vv_h, OP_UUU_H, H2, H2, H2, float16_mul) +RVVCALL(OPFVV2, vfmul_vv_w, OP_UUU_W, H4, H4, H4, float32_mul) +RVVCALL(OPFVV2, vfmul_vv_d, OP_UUU_D, H8, H8, H8, float64_mul) +GEN_VEXT_VV_ENV(vfmul_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfmul_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfmul_vv_d, 8, 8, clearq) +RVVCALL(OPFVF2, vfmul_vf_h, OP_UUU_H, H2, H2, float16_mul) +RVVCALL(OPFVF2, vfmul_vf_w, OP_UUU_W, H4, H4, float32_mul) +RVVCALL(OPFVF2, vfmul_vf_d, OP_UUU_D, H8, H8, float64_mul) +GEN_VEXT_VF(vfmul_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfmul_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfmul_vf_d, 8, 8, clearq) + +RVVCALL(OPFVV2, vfdiv_vv_h, OP_UUU_H, H2, H2, H2, float16_div) +RVVCALL(OPFVV2, vfdiv_vv_w, OP_UUU_W, H4, H4, H4, float32_div) +RVVCALL(OPFVV2, vfdiv_vv_d, OP_UUU_D, H8, H8, H8, float64_div) +GEN_VEXT_VV_ENV(vfdiv_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfdiv_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfdiv_vv_d, 8, 8, clearq) +RVVCALL(OPFVF2, vfdiv_vf_h, OP_UUU_H, H2, H2, float16_div) +RVVCALL(OPFVF2, vfdiv_vf_w, OP_UUU_W, H4, H4, float32_div) +RVVCALL(OPFVF2, vfdiv_vf_d, OP_UUU_D, H8, H8, float64_div) +GEN_VEXT_VF(vfdiv_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfdiv_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfdiv_vf_d, 8, 8, clearq) + +static uint16_t float16_rdiv(uint16_t a, uint16_t b, float_status *s) +{ + return float16_div(b, a, s); +} + +static uint32_t float32_rdiv(uint32_t a, uint32_t b, float_status *s) +{ + return float32_div(b, a, s); +} + +static uint64_t float64_rdiv(uint64_t a, uint64_t b, float_status *s) +{ + return float64_div(b, a, s); +} + +RVVCALL(OPFVF2, vfrdiv_vf_h, OP_UUU_H, H2, H2, float16_rdiv) +RVVCALL(OPFVF2, vfrdiv_vf_w, OP_UUU_W, H4, H4, float32_rdiv) +RVVCALL(OPFVF2, vfrdiv_vf_d, OP_UUU_D, H8, H8, float64_rdiv) +GEN_VEXT_VF(vfrdiv_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfrdiv_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfrdiv_vf_d, 8, 8, clearq) + +/* Vector Widening Floating-Point Multiply */ +static uint32_t vfwmul16(uint16_t a, uint16_t b, float_status *s) +{ + return float32_mul(float16_to_float32(a, true, s), + float16_to_float32(b, true, s), s); +} + +static uint64_t vfwmul32(uint32_t a, uint32_t b, float_status *s) +{ + return float64_mul(float32_to_float64(a, s), + float32_to_float64(b, s), s); + +} +RVVCALL(OPFVV2, vfwmul_vv_h, WOP_UUU_H, H4, H2, H2, vfwmul16) +RVVCALL(OPFVV2, vfwmul_vv_w, WOP_UUU_W, H8, H4, H4, vfwmul32) +GEN_VEXT_VV_ENV(vfwmul_vv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwmul_vv_w, 4, 8, clearq) +RVVCALL(OPFVF2, vfwmul_vf_h, WOP_UUU_H, H4, H2, vfwmul16) +RVVCALL(OPFVF2, vfwmul_vf_w, WOP_UUU_W, H8, H4, vfwmul32) +GEN_VEXT_VF(vfwmul_vf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwmul_vf_w, 4, 8, clearq) + +/* Vector Single-Width Floating-Point Fused Multiply-Add Instructions */ +#define OPFVV3(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ +static void do_##NAME(void *vd, void *vs1, void *vs2, int i, \ + CPURISCVState *env) \ +{ \ + TX1 s1 = *((T1 *)vs1 + HS1(i)); \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + TD d = *((TD *)vd + HD(i)); \ + *((TD *)vd + HD(i)) = OP(s2, s1, d, &env->fp_status); \ +} + +static uint16_t fmacc16(uint16_t a, uint16_t b, uint16_t d, float_status *s) +{ + return float16_muladd(a, b, d, 0, s); +} + +static uint32_t fmacc32(uint32_t a, uint32_t b, uint32_t d, float_status *s) +{ + return float32_muladd(a, b, d, 0, s); +} + +static uint64_t fmacc64(uint64_t a, uint64_t b, uint64_t d, float_status *s) +{ + return float64_muladd(a, b, d, 0, s); +} + +RVVCALL(OPFVV3, vfmacc_vv_h, OP_UUU_H, H2, H2, H2, fmacc16) +RVVCALL(OPFVV3, vfmacc_vv_w, OP_UUU_W, H4, H4, H4, fmacc32) +RVVCALL(OPFVV3, vfmacc_vv_d, OP_UUU_D, H8, H8, H8, fmacc64) +GEN_VEXT_VV_ENV(vfmacc_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfmacc_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfmacc_vv_d, 8, 8, clearq) + +#define OPFVF3(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ +static void do_##NAME(void *vd, uint64_t s1, void *vs2, int i, \ + CPURISCVState *env) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + TD d = *((TD *)vd + HD(i)); \ + *((TD *)vd + HD(i)) = OP(s2, (TX1)(T1)s1, d, &env->fp_status);\ +} + +RVVCALL(OPFVF3, vfmacc_vf_h, OP_UUU_H, H2, H2, fmacc16) +RVVCALL(OPFVF3, vfmacc_vf_w, OP_UUU_W, H4, H4, fmacc32) +RVVCALL(OPFVF3, vfmacc_vf_d, OP_UUU_D, H8, H8, fmacc64) +GEN_VEXT_VF(vfmacc_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfmacc_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfmacc_vf_d, 8, 8, clearq) + +static uint16_t fnmacc16(uint16_t a, uint16_t b, uint16_t d, float_status *s) +{ + return float16_muladd(a, b, d, + float_muladd_negate_c | float_muladd_negate_product, s); +} + +static uint32_t fnmacc32(uint32_t a, uint32_t b, uint32_t d, float_status *s) +{ + return float32_muladd(a, b, d, + float_muladd_negate_c | float_muladd_negate_product, s); +} + +static uint64_t fnmacc64(uint64_t a, uint64_t b, uint64_t d, float_status *s) +{ + return float64_muladd(a, b, d, + float_muladd_negate_c | float_muladd_negate_product, s); +} + +RVVCALL(OPFVV3, vfnmacc_vv_h, OP_UUU_H, H2, H2, H2, fnmacc16) +RVVCALL(OPFVV3, vfnmacc_vv_w, OP_UUU_W, H4, H4, H4, fnmacc32) +RVVCALL(OPFVV3, vfnmacc_vv_d, OP_UUU_D, H8, H8, H8, fnmacc64) +GEN_VEXT_VV_ENV(vfnmacc_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfnmacc_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfnmacc_vv_d, 8, 8, clearq) +RVVCALL(OPFVF3, vfnmacc_vf_h, OP_UUU_H, H2, H2, fnmacc16) +RVVCALL(OPFVF3, vfnmacc_vf_w, OP_UUU_W, H4, H4, fnmacc32) +RVVCALL(OPFVF3, vfnmacc_vf_d, OP_UUU_D, H8, H8, fnmacc64) +GEN_VEXT_VF(vfnmacc_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfnmacc_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfnmacc_vf_d, 8, 8, clearq) + +static uint16_t fmsac16(uint16_t a, uint16_t b, uint16_t d, float_status *s) +{ + return float16_muladd(a, b, d, float_muladd_negate_c, s); +} + +static uint32_t fmsac32(uint32_t a, uint32_t b, uint32_t d, float_status *s) +{ + return float32_muladd(a, b, d, float_muladd_negate_c, s); +} + +static uint64_t fmsac64(uint64_t a, uint64_t b, uint64_t d, float_status *s) +{ + return float64_muladd(a, b, d, float_muladd_negate_c, s); +} + +RVVCALL(OPFVV3, vfmsac_vv_h, OP_UUU_H, H2, H2, H2, fmsac16) +RVVCALL(OPFVV3, vfmsac_vv_w, OP_UUU_W, H4, H4, H4, fmsac32) +RVVCALL(OPFVV3, vfmsac_vv_d, OP_UUU_D, H8, H8, H8, fmsac64) +GEN_VEXT_VV_ENV(vfmsac_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfmsac_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfmsac_vv_d, 8, 8, clearq) +RVVCALL(OPFVF3, vfmsac_vf_h, OP_UUU_H, H2, H2, fmsac16) +RVVCALL(OPFVF3, vfmsac_vf_w, OP_UUU_W, H4, H4, fmsac32) +RVVCALL(OPFVF3, vfmsac_vf_d, OP_UUU_D, H8, H8, fmsac64) +GEN_VEXT_VF(vfmsac_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfmsac_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfmsac_vf_d, 8, 8, clearq) + +static uint16_t fnmsac16(uint16_t a, uint16_t b, uint16_t d, float_status *s) +{ + return float16_muladd(a, b, d, float_muladd_negate_product, s); +} + +static uint32_t fnmsac32(uint32_t a, uint32_t b, uint32_t d, float_status *s) +{ + return float32_muladd(a, b, d, float_muladd_negate_product, s); +} + +static uint64_t fnmsac64(uint64_t a, uint64_t b, uint64_t d, float_status *s) +{ + return float64_muladd(a, b, d, float_muladd_negate_product, s); +} + +RVVCALL(OPFVV3, vfnmsac_vv_h, OP_UUU_H, H2, H2, H2, fnmsac16) +RVVCALL(OPFVV3, vfnmsac_vv_w, OP_UUU_W, H4, H4, H4, fnmsac32) +RVVCALL(OPFVV3, vfnmsac_vv_d, OP_UUU_D, H8, H8, H8, fnmsac64) +GEN_VEXT_VV_ENV(vfnmsac_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfnmsac_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfnmsac_vv_d, 8, 8, clearq) +RVVCALL(OPFVF3, vfnmsac_vf_h, OP_UUU_H, H2, H2, fnmsac16) +RVVCALL(OPFVF3, vfnmsac_vf_w, OP_UUU_W, H4, H4, fnmsac32) +RVVCALL(OPFVF3, vfnmsac_vf_d, OP_UUU_D, H8, H8, fnmsac64) +GEN_VEXT_VF(vfnmsac_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfnmsac_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfnmsac_vf_d, 8, 8, clearq) + +static uint16_t fmadd16(uint16_t a, uint16_t b, uint16_t d, float_status *s) +{ + return float16_muladd(d, b, a, 0, s); +} + +static uint32_t fmadd32(uint32_t a, uint32_t b, uint32_t d, float_status *s) +{ + return float32_muladd(d, b, a, 0, s); +} + +static uint64_t fmadd64(uint64_t a, uint64_t b, uint64_t d, float_status *s) +{ + return float64_muladd(d, b, a, 0, s); +} + +RVVCALL(OPFVV3, vfmadd_vv_h, OP_UUU_H, H2, H2, H2, fmadd16) +RVVCALL(OPFVV3, vfmadd_vv_w, OP_UUU_W, H4, H4, H4, fmadd32) +RVVCALL(OPFVV3, vfmadd_vv_d, OP_UUU_D, H8, H8, H8, fmadd64) +GEN_VEXT_VV_ENV(vfmadd_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfmadd_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfmadd_vv_d, 8, 8, clearq) +RVVCALL(OPFVF3, vfmadd_vf_h, OP_UUU_H, H2, H2, fmadd16) +RVVCALL(OPFVF3, vfmadd_vf_w, OP_UUU_W, H4, H4, fmadd32) +RVVCALL(OPFVF3, vfmadd_vf_d, OP_UUU_D, H8, H8, fmadd64) +GEN_VEXT_VF(vfmadd_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfmadd_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfmadd_vf_d, 8, 8, clearq) + +static uint16_t fnmadd16(uint16_t a, uint16_t b, uint16_t d, float_status *s) +{ + return float16_muladd(d, b, a, + float_muladd_negate_c | float_muladd_negate_product, s); +} + +static uint32_t fnmadd32(uint32_t a, uint32_t b, uint32_t d, float_status *s) +{ + return float32_muladd(d, b, a, + float_muladd_negate_c | float_muladd_negate_product, s); +} + +static uint64_t fnmadd64(uint64_t a, uint64_t b, uint64_t d, float_status *s) +{ + return float64_muladd(d, b, a, + float_muladd_negate_c | float_muladd_negate_product, s); +} + +RVVCALL(OPFVV3, vfnmadd_vv_h, OP_UUU_H, H2, H2, H2, fnmadd16) +RVVCALL(OPFVV3, vfnmadd_vv_w, OP_UUU_W, H4, H4, H4, fnmadd32) +RVVCALL(OPFVV3, vfnmadd_vv_d, OP_UUU_D, H8, H8, H8, fnmadd64) +GEN_VEXT_VV_ENV(vfnmadd_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfnmadd_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfnmadd_vv_d, 8, 8, clearq) +RVVCALL(OPFVF3, vfnmadd_vf_h, OP_UUU_H, H2, H2, fnmadd16) +RVVCALL(OPFVF3, vfnmadd_vf_w, OP_UUU_W, H4, H4, fnmadd32) +RVVCALL(OPFVF3, vfnmadd_vf_d, OP_UUU_D, H8, H8, fnmadd64) +GEN_VEXT_VF(vfnmadd_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfnmadd_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfnmadd_vf_d, 8, 8, clearq) + +static uint16_t fmsub16(uint16_t a, uint16_t b, uint16_t d, float_status *s) +{ + return float16_muladd(d, b, a, float_muladd_negate_c, s); +} + +static uint32_t fmsub32(uint32_t a, uint32_t b, uint32_t d, float_status *s) +{ + return float32_muladd(d, b, a, float_muladd_negate_c, s); +} + +static uint64_t fmsub64(uint64_t a, uint64_t b, uint64_t d, float_status *s) +{ + return float64_muladd(d, b, a, float_muladd_negate_c, s); +} + +RVVCALL(OPFVV3, vfmsub_vv_h, OP_UUU_H, H2, H2, H2, fmsub16) +RVVCALL(OPFVV3, vfmsub_vv_w, OP_UUU_W, H4, H4, H4, fmsub32) +RVVCALL(OPFVV3, vfmsub_vv_d, OP_UUU_D, H8, H8, H8, fmsub64) +GEN_VEXT_VV_ENV(vfmsub_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfmsub_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfmsub_vv_d, 8, 8, clearq) +RVVCALL(OPFVF3, vfmsub_vf_h, OP_UUU_H, H2, H2, fmsub16) +RVVCALL(OPFVF3, vfmsub_vf_w, OP_UUU_W, H4, H4, fmsub32) +RVVCALL(OPFVF3, vfmsub_vf_d, OP_UUU_D, H8, H8, fmsub64) +GEN_VEXT_VF(vfmsub_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfmsub_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfmsub_vf_d, 8, 8, clearq) + +static uint16_t fnmsub16(uint16_t a, uint16_t b, uint16_t d, float_status *s) +{ + return float16_muladd(d, b, a, float_muladd_negate_product, s); +} + +static uint32_t fnmsub32(uint32_t a, uint32_t b, uint32_t d, float_status *s) +{ + return float32_muladd(d, b, a, float_muladd_negate_product, s); +} + +static uint64_t fnmsub64(uint64_t a, uint64_t b, uint64_t d, float_status *s) +{ + return float64_muladd(d, b, a, float_muladd_negate_product, s); +} + +RVVCALL(OPFVV3, vfnmsub_vv_h, OP_UUU_H, H2, H2, H2, fnmsub16) +RVVCALL(OPFVV3, vfnmsub_vv_w, OP_UUU_W, H4, H4, H4, fnmsub32) +RVVCALL(OPFVV3, vfnmsub_vv_d, OP_UUU_D, H8, H8, H8, fnmsub64) +GEN_VEXT_VV_ENV(vfnmsub_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfnmsub_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfnmsub_vv_d, 8, 8, clearq) +RVVCALL(OPFVF3, vfnmsub_vf_h, OP_UUU_H, H2, H2, fnmsub16) +RVVCALL(OPFVF3, vfnmsub_vf_w, OP_UUU_W, H4, H4, fnmsub32) +RVVCALL(OPFVF3, vfnmsub_vf_d, OP_UUU_D, H8, H8, fnmsub64) +GEN_VEXT_VF(vfnmsub_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfnmsub_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfnmsub_vf_d, 8, 8, clearq) + +/* Vector Widening Floating-Point Fused Multiply-Add Instructions */ +static uint32_t fwmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s) +{ + return float32_muladd(float16_to_float32(a, true, s), + float16_to_float32(b, true, s), d, 0, s); +} + +static uint64_t fwmacc32(uint32_t a, uint32_t b, uint64_t d, float_status *s) +{ + return float64_muladd(float32_to_float64(a, s), + float32_to_float64(b, s), d, 0, s); +} + +RVVCALL(OPFVV3, vfwmacc_vv_h, WOP_UUU_H, H4, H2, H2, fwmacc16) +RVVCALL(OPFVV3, vfwmacc_vv_w, WOP_UUU_W, H8, H4, H4, fwmacc32) +GEN_VEXT_VV_ENV(vfwmacc_vv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwmacc_vv_w, 4, 8, clearq) +RVVCALL(OPFVF3, vfwmacc_vf_h, WOP_UUU_H, H4, H2, fwmacc16) +RVVCALL(OPFVF3, vfwmacc_vf_w, WOP_UUU_W, H8, H4, fwmacc32) +GEN_VEXT_VF(vfwmacc_vf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwmacc_vf_w, 4, 8, clearq) + +static uint32_t fwnmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s) +{ + return float32_muladd(float16_to_float32(a, true, s), + float16_to_float32(b, true, s), d, + float_muladd_negate_c | float_muladd_negate_product, s); +} + +static uint64_t fwnmacc32(uint32_t a, uint32_t b, uint64_t d, float_status *s) +{ + return float64_muladd(float32_to_float64(a, s), + float32_to_float64(b, s), d, + float_muladd_negate_c | float_muladd_negate_product, s); +} + +RVVCALL(OPFVV3, vfwnmacc_vv_h, WOP_UUU_H, H4, H2, H2, fwnmacc16) +RVVCALL(OPFVV3, vfwnmacc_vv_w, WOP_UUU_W, H8, H4, H4, fwnmacc32) +GEN_VEXT_VV_ENV(vfwnmacc_vv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwnmacc_vv_w, 4, 8, clearq) +RVVCALL(OPFVF3, vfwnmacc_vf_h, WOP_UUU_H, H4, H2, fwnmacc16) +RVVCALL(OPFVF3, vfwnmacc_vf_w, WOP_UUU_W, H8, H4, fwnmacc32) +GEN_VEXT_VF(vfwnmacc_vf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwnmacc_vf_w, 4, 8, clearq) + +static uint32_t fwmsac16(uint16_t a, uint16_t b, uint32_t d, float_status *s) +{ + return float32_muladd(float16_to_float32(a, true, s), + float16_to_float32(b, true, s), d, + float_muladd_negate_c, s); +} + +static uint64_t fwmsac32(uint32_t a, uint32_t b, uint64_t d, float_status *s) +{ + return float64_muladd(float32_to_float64(a, s), + float32_to_float64(b, s), d, + float_muladd_negate_c, s); +} + +RVVCALL(OPFVV3, vfwmsac_vv_h, WOP_UUU_H, H4, H2, H2, fwmsac16) +RVVCALL(OPFVV3, vfwmsac_vv_w, WOP_UUU_W, H8, H4, H4, fwmsac32) +GEN_VEXT_VV_ENV(vfwmsac_vv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwmsac_vv_w, 4, 8, clearq) +RVVCALL(OPFVF3, vfwmsac_vf_h, WOP_UUU_H, H4, H2, fwmsac16) +RVVCALL(OPFVF3, vfwmsac_vf_w, WOP_UUU_W, H8, H4, fwmsac32) +GEN_VEXT_VF(vfwmsac_vf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwmsac_vf_w, 4, 8, clearq) + +static uint32_t fwnmsac16(uint16_t a, uint16_t b, uint32_t d, float_status *s) +{ + return float32_muladd(float16_to_float32(a, true, s), + float16_to_float32(b, true, s), d, + float_muladd_negate_product, s); +} + +static uint64_t fwnmsac32(uint32_t a, uint32_t b, uint64_t d, float_status *s) +{ + return float64_muladd(float32_to_float64(a, s), + float32_to_float64(b, s), d, + float_muladd_negate_product, s); +} + +RVVCALL(OPFVV3, vfwnmsac_vv_h, WOP_UUU_H, H4, H2, H2, fwnmsac16) +RVVCALL(OPFVV3, vfwnmsac_vv_w, WOP_UUU_W, H8, H4, H4, fwnmsac32) +GEN_VEXT_VV_ENV(vfwnmsac_vv_h, 2, 4, clearl) +GEN_VEXT_VV_ENV(vfwnmsac_vv_w, 4, 8, clearq) +RVVCALL(OPFVF3, vfwnmsac_vf_h, WOP_UUU_H, H4, H2, fwnmsac16) +RVVCALL(OPFVF3, vfwnmsac_vf_w, WOP_UUU_W, H8, H4, fwnmsac32) +GEN_VEXT_VF(vfwnmsac_vf_h, 2, 4, clearl) +GEN_VEXT_VF(vfwnmsac_vf_w, 4, 8, clearq) + +/* Vector Floating-Point Square-Root Instruction */ +/* (TD, T2, TX2) */ +#define OP_UU_H uint16_t, uint16_t, uint16_t +#define OP_UU_W uint32_t, uint32_t, uint32_t +#define OP_UU_D uint64_t, uint64_t, uint64_t + +#define OPFVV1(NAME, TD, T2, TX2, HD, HS2, OP) \ +static void do_##NAME(void *vd, void *vs2, int i, \ + CPURISCVState *env) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2, &env->fp_status); \ +} + +#define GEN_VEXT_V_ENV(NAME, ESZ, DSZ, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t vlmax = vext_maxsz(desc) / ESZ; \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t i; \ + \ + if (vl == 0) { \ + return; \ + } \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + do_##NAME(vd, vs2, i, env); \ + } \ + CLEAR_FN(vd, vl, vl * DSZ, vlmax * DSZ); \ +} + +RVVCALL(OPFVV1, vfsqrt_v_h, OP_UU_H, H2, H2, float16_sqrt) +RVVCALL(OPFVV1, vfsqrt_v_w, OP_UU_W, H4, H4, float32_sqrt) +RVVCALL(OPFVV1, vfsqrt_v_d, OP_UU_D, H8, H8, float64_sqrt) +GEN_VEXT_V_ENV(vfsqrt_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfsqrt_v_w, 4, 4, clearl) +GEN_VEXT_V_ENV(vfsqrt_v_d, 8, 8, clearq) + +/* Vector Floating-Point MIN/MAX Instructions */ +RVVCALL(OPFVV2, vfmin_vv_h, OP_UUU_H, H2, H2, H2, float16_minnum) +RVVCALL(OPFVV2, vfmin_vv_w, OP_UUU_W, H4, H4, H4, float32_minnum) +RVVCALL(OPFVV2, vfmin_vv_d, OP_UUU_D, H8, H8, H8, float64_minnum) +GEN_VEXT_VV_ENV(vfmin_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfmin_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfmin_vv_d, 8, 8, clearq) +RVVCALL(OPFVF2, vfmin_vf_h, OP_UUU_H, H2, H2, float16_minnum) +RVVCALL(OPFVF2, vfmin_vf_w, OP_UUU_W, H4, H4, float32_minnum) +RVVCALL(OPFVF2, vfmin_vf_d, OP_UUU_D, H8, H8, float64_minnum) +GEN_VEXT_VF(vfmin_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfmin_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfmin_vf_d, 8, 8, clearq) + +RVVCALL(OPFVV2, vfmax_vv_h, OP_UUU_H, H2, H2, H2, float16_maxnum) +RVVCALL(OPFVV2, vfmax_vv_w, OP_UUU_W, H4, H4, H4, float32_maxnum) +RVVCALL(OPFVV2, vfmax_vv_d, OP_UUU_D, H8, H8, H8, float64_maxnum) +GEN_VEXT_VV_ENV(vfmax_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfmax_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfmax_vv_d, 8, 8, clearq) +RVVCALL(OPFVF2, vfmax_vf_h, OP_UUU_H, H2, H2, float16_maxnum) +RVVCALL(OPFVF2, vfmax_vf_w, OP_UUU_W, H4, H4, float32_maxnum) +RVVCALL(OPFVF2, vfmax_vf_d, OP_UUU_D, H8, H8, float64_maxnum) +GEN_VEXT_VF(vfmax_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfmax_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfmax_vf_d, 8, 8, clearq) + +/* Vector Floating-Point Sign-Injection Instructions */ +static uint16_t fsgnj16(uint16_t a, uint16_t b, float_status *s) +{ + return deposit64(b, 0, 15, a); +} + +static uint32_t fsgnj32(uint32_t a, uint32_t b, float_status *s) +{ + return deposit64(b, 0, 31, a); +} + +static uint64_t fsgnj64(uint64_t a, uint64_t b, float_status *s) +{ + return deposit64(b, 0, 63, a); +} + +RVVCALL(OPFVV2, vfsgnj_vv_h, OP_UUU_H, H2, H2, H2, fsgnj16) +RVVCALL(OPFVV2, vfsgnj_vv_w, OP_UUU_W, H4, H4, H4, fsgnj32) +RVVCALL(OPFVV2, vfsgnj_vv_d, OP_UUU_D, H8, H8, H8, fsgnj64) +GEN_VEXT_VV_ENV(vfsgnj_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfsgnj_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfsgnj_vv_d, 8, 8, clearq) +RVVCALL(OPFVF2, vfsgnj_vf_h, OP_UUU_H, H2, H2, fsgnj16) +RVVCALL(OPFVF2, vfsgnj_vf_w, OP_UUU_W, H4, H4, fsgnj32) +RVVCALL(OPFVF2, vfsgnj_vf_d, OP_UUU_D, H8, H8, fsgnj64) +GEN_VEXT_VF(vfsgnj_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfsgnj_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfsgnj_vf_d, 8, 8, clearq) + +static uint16_t fsgnjn16(uint16_t a, uint16_t b, float_status *s) +{ + return deposit64(~b, 0, 15, a); +} + +static uint32_t fsgnjn32(uint32_t a, uint32_t b, float_status *s) +{ + return deposit64(~b, 0, 31, a); +} + +static uint64_t fsgnjn64(uint64_t a, uint64_t b, float_status *s) +{ + return deposit64(~b, 0, 63, a); +} + +RVVCALL(OPFVV2, vfsgnjn_vv_h, OP_UUU_H, H2, H2, H2, fsgnjn16) +RVVCALL(OPFVV2, vfsgnjn_vv_w, OP_UUU_W, H4, H4, H4, fsgnjn32) +RVVCALL(OPFVV2, vfsgnjn_vv_d, OP_UUU_D, H8, H8, H8, fsgnjn64) +GEN_VEXT_VV_ENV(vfsgnjn_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfsgnjn_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfsgnjn_vv_d, 8, 8, clearq) +RVVCALL(OPFVF2, vfsgnjn_vf_h, OP_UUU_H, H2, H2, fsgnjn16) +RVVCALL(OPFVF2, vfsgnjn_vf_w, OP_UUU_W, H4, H4, fsgnjn32) +RVVCALL(OPFVF2, vfsgnjn_vf_d, OP_UUU_D, H8, H8, fsgnjn64) +GEN_VEXT_VF(vfsgnjn_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfsgnjn_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfsgnjn_vf_d, 8, 8, clearq) + +static uint16_t fsgnjx16(uint16_t a, uint16_t b, float_status *s) +{ + return deposit64(b ^ a, 0, 15, a); +} + +static uint32_t fsgnjx32(uint32_t a, uint32_t b, float_status *s) +{ + return deposit64(b ^ a, 0, 31, a); +} + +static uint64_t fsgnjx64(uint64_t a, uint64_t b, float_status *s) +{ + return deposit64(b ^ a, 0, 63, a); +} + +RVVCALL(OPFVV2, vfsgnjx_vv_h, OP_UUU_H, H2, H2, H2, fsgnjx16) +RVVCALL(OPFVV2, vfsgnjx_vv_w, OP_UUU_W, H4, H4, H4, fsgnjx32) +RVVCALL(OPFVV2, vfsgnjx_vv_d, OP_UUU_D, H8, H8, H8, fsgnjx64) +GEN_VEXT_VV_ENV(vfsgnjx_vv_h, 2, 2, clearh) +GEN_VEXT_VV_ENV(vfsgnjx_vv_w, 4, 4, clearl) +GEN_VEXT_VV_ENV(vfsgnjx_vv_d, 8, 8, clearq) +RVVCALL(OPFVF2, vfsgnjx_vf_h, OP_UUU_H, H2, H2, fsgnjx16) +RVVCALL(OPFVF2, vfsgnjx_vf_w, OP_UUU_W, H4, H4, fsgnjx32) +RVVCALL(OPFVF2, vfsgnjx_vf_d, OP_UUU_D, H8, H8, fsgnjx64) +GEN_VEXT_VF(vfsgnjx_vf_h, 2, 2, clearh) +GEN_VEXT_VF(vfsgnjx_vf_w, 4, 4, clearl) +GEN_VEXT_VF(vfsgnjx_vf_d, 8, 8, clearq) + +/* Vector Floating-Point Compare Instructions */ +#define GEN_VEXT_CMP_VV_ENV(NAME, ETYPE, H, DO_OP) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t vlmax = vext_maxsz(desc) / sizeof(ETYPE); \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + vext_set_elem_mask(vd, mlen, i, \ + DO_OP(s2, s1, &env->fp_status)); \ + } \ + for (; i < vlmax; i++) { \ + vext_set_elem_mask(vd, mlen, i, 0); \ + } \ +} + +static bool float16_eq_quiet(uint16_t a, uint16_t b, float_status *s) +{ + FloatRelation compare = float16_compare_quiet(a, b, s); + return compare == float_relation_equal; +} + +GEN_VEXT_CMP_VV_ENV(vmfeq_vv_h, uint16_t, H2, float16_eq_quiet) +GEN_VEXT_CMP_VV_ENV(vmfeq_vv_w, uint32_t, H4, float32_eq_quiet) +GEN_VEXT_CMP_VV_ENV(vmfeq_vv_d, uint64_t, H8, float64_eq_quiet) + +#define GEN_VEXT_CMP_VF(NAME, ETYPE, H, DO_OP) \ +void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t vlmax = vext_maxsz(desc) / sizeof(ETYPE); \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + vext_set_elem_mask(vd, mlen, i, \ + DO_OP(s2, (ETYPE)s1, &env->fp_status)); \ + } \ + for (; i < vlmax; i++) { \ + vext_set_elem_mask(vd, mlen, i, 0); \ + } \ +} + +GEN_VEXT_CMP_VF(vmfeq_vf_h, uint16_t, H2, float16_eq_quiet) +GEN_VEXT_CMP_VF(vmfeq_vf_w, uint32_t, H4, float32_eq_quiet) +GEN_VEXT_CMP_VF(vmfeq_vf_d, uint64_t, H8, float64_eq_quiet) + +static bool vmfne16(uint16_t a, uint16_t b, float_status *s) +{ + FloatRelation compare = float16_compare_quiet(a, b, s); + return compare != float_relation_equal; +} + +static bool vmfne32(uint32_t a, uint32_t b, float_status *s) +{ + FloatRelation compare = float32_compare_quiet(a, b, s); + return compare != float_relation_equal; +} + +static bool vmfne64(uint64_t a, uint64_t b, float_status *s) +{ + FloatRelation compare = float64_compare_quiet(a, b, s); + return compare != float_relation_equal; +} + +GEN_VEXT_CMP_VV_ENV(vmfne_vv_h, uint16_t, H2, vmfne16) +GEN_VEXT_CMP_VV_ENV(vmfne_vv_w, uint32_t, H4, vmfne32) +GEN_VEXT_CMP_VV_ENV(vmfne_vv_d, uint64_t, H8, vmfne64) +GEN_VEXT_CMP_VF(vmfne_vf_h, uint16_t, H2, vmfne16) +GEN_VEXT_CMP_VF(vmfne_vf_w, uint32_t, H4, vmfne32) +GEN_VEXT_CMP_VF(vmfne_vf_d, uint64_t, H8, vmfne64) + +static bool float16_lt(uint16_t a, uint16_t b, float_status *s) +{ + FloatRelation compare = float16_compare(a, b, s); + return compare == float_relation_less; +} + +GEN_VEXT_CMP_VV_ENV(vmflt_vv_h, uint16_t, H2, float16_lt) +GEN_VEXT_CMP_VV_ENV(vmflt_vv_w, uint32_t, H4, float32_lt) +GEN_VEXT_CMP_VV_ENV(vmflt_vv_d, uint64_t, H8, float64_lt) +GEN_VEXT_CMP_VF(vmflt_vf_h, uint16_t, H2, float16_lt) +GEN_VEXT_CMP_VF(vmflt_vf_w, uint32_t, H4, float32_lt) +GEN_VEXT_CMP_VF(vmflt_vf_d, uint64_t, H8, float64_lt) + +static bool float16_le(uint16_t a, uint16_t b, float_status *s) +{ + FloatRelation compare = float16_compare(a, b, s); + return compare == float_relation_less || + compare == float_relation_equal; +} + +GEN_VEXT_CMP_VV_ENV(vmfle_vv_h, uint16_t, H2, float16_le) +GEN_VEXT_CMP_VV_ENV(vmfle_vv_w, uint32_t, H4, float32_le) +GEN_VEXT_CMP_VV_ENV(vmfle_vv_d, uint64_t, H8, float64_le) +GEN_VEXT_CMP_VF(vmfle_vf_h, uint16_t, H2, float16_le) +GEN_VEXT_CMP_VF(vmfle_vf_w, uint32_t, H4, float32_le) +GEN_VEXT_CMP_VF(vmfle_vf_d, uint64_t, H8, float64_le) + +static bool vmfgt16(uint16_t a, uint16_t b, float_status *s) +{ + FloatRelation compare = float16_compare(a, b, s); + return compare == float_relation_greater; +} + +static bool vmfgt32(uint32_t a, uint32_t b, float_status *s) +{ + FloatRelation compare = float32_compare(a, b, s); + return compare == float_relation_greater; +} + +static bool vmfgt64(uint64_t a, uint64_t b, float_status *s) +{ + FloatRelation compare = float64_compare(a, b, s); + return compare == float_relation_greater; +} + +GEN_VEXT_CMP_VF(vmfgt_vf_h, uint16_t, H2, vmfgt16) +GEN_VEXT_CMP_VF(vmfgt_vf_w, uint32_t, H4, vmfgt32) +GEN_VEXT_CMP_VF(vmfgt_vf_d, uint64_t, H8, vmfgt64) + +static bool vmfge16(uint16_t a, uint16_t b, float_status *s) +{ + FloatRelation compare = float16_compare(a, b, s); + return compare == float_relation_greater || + compare == float_relation_equal; +} + +static bool vmfge32(uint32_t a, uint32_t b, float_status *s) +{ + FloatRelation compare = float32_compare(a, b, s); + return compare == float_relation_greater || + compare == float_relation_equal; +} + +static bool vmfge64(uint64_t a, uint64_t b, float_status *s) +{ + FloatRelation compare = float64_compare(a, b, s); + return compare == float_relation_greater || + compare == float_relation_equal; +} + +GEN_VEXT_CMP_VF(vmfge_vf_h, uint16_t, H2, vmfge16) +GEN_VEXT_CMP_VF(vmfge_vf_w, uint32_t, H4, vmfge32) +GEN_VEXT_CMP_VF(vmfge_vf_d, uint64_t, H8, vmfge64) + +static bool float16_unordered_quiet(uint16_t a, uint16_t b, float_status *s) +{ + FloatRelation compare = float16_compare_quiet(a, b, s); + return compare == float_relation_unordered; +} + +GEN_VEXT_CMP_VV_ENV(vmford_vv_h, uint16_t, H2, !float16_unordered_quiet) +GEN_VEXT_CMP_VV_ENV(vmford_vv_w, uint32_t, H4, !float32_unordered_quiet) +GEN_VEXT_CMP_VV_ENV(vmford_vv_d, uint64_t, H8, !float64_unordered_quiet) +GEN_VEXT_CMP_VF(vmford_vf_h, uint16_t, H2, !float16_unordered_quiet) +GEN_VEXT_CMP_VF(vmford_vf_w, uint32_t, H4, !float32_unordered_quiet) +GEN_VEXT_CMP_VF(vmford_vf_d, uint64_t, H8, !float64_unordered_quiet) + +/* Vector Floating-Point Classify Instruction */ +#define OPIVV1(NAME, TD, T2, TX2, HD, HS2, OP) \ +static void do_##NAME(void *vd, void *vs2, int i) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2); \ +} + +#define GEN_VEXT_V(NAME, ESZ, DSZ, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t vlmax = vext_maxsz(desc) / ESZ; \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + do_##NAME(vd, vs2, i); \ + } \ + CLEAR_FN(vd, vl, vl * DSZ, vlmax * DSZ); \ +} + +target_ulong fclass_h(uint64_t frs1) +{ + float16 f = frs1; + bool sign = float16_is_neg(f); + + if (float16_is_infinity(f)) { + return sign ? 1 << 0 : 1 << 7; + } else if (float16_is_zero(f)) { + return sign ? 1 << 3 : 1 << 4; + } else if (float16_is_zero_or_denormal(f)) { + return sign ? 1 << 2 : 1 << 5; + } else if (float16_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float16_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; + } else { + return sign ? 1 << 1 : 1 << 6; + } +} + +target_ulong fclass_s(uint64_t frs1) +{ + float32 f = frs1; + bool sign = float32_is_neg(f); + + if (float32_is_infinity(f)) { + return sign ? 1 << 0 : 1 << 7; + } else if (float32_is_zero(f)) { + return sign ? 1 << 3 : 1 << 4; + } else if (float32_is_zero_or_denormal(f)) { + return sign ? 1 << 2 : 1 << 5; + } else if (float32_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float32_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; + } else { + return sign ? 1 << 1 : 1 << 6; + } +} + +target_ulong fclass_d(uint64_t frs1) +{ + float64 f = frs1; + bool sign = float64_is_neg(f); + + if (float64_is_infinity(f)) { + return sign ? 1 << 0 : 1 << 7; + } else if (float64_is_zero(f)) { + return sign ? 1 << 3 : 1 << 4; + } else if (float64_is_zero_or_denormal(f)) { + return sign ? 1 << 2 : 1 << 5; + } else if (float64_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float64_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; + } else { + return sign ? 1 << 1 : 1 << 6; + } +} + +RVVCALL(OPIVV1, vfclass_v_h, OP_UU_H, H2, H2, fclass_h) +RVVCALL(OPIVV1, vfclass_v_w, OP_UU_W, H4, H4, fclass_s) +RVVCALL(OPIVV1, vfclass_v_d, OP_UU_D, H8, H8, fclass_d) +GEN_VEXT_V(vfclass_v_h, 2, 2, clearh) +GEN_VEXT_V(vfclass_v_w, 4, 4, clearl) +GEN_VEXT_V(vfclass_v_d, 8, 8, clearq) + +/* Vector Floating-Point Merge Instruction */ +#define GEN_VFMERGE_VF(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t vlmax = vext_maxsz(desc) / esz; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ + *((ETYPE *)vd + H(i)) \ + = (!vm && !vext_elem_mask(v0, mlen, i) ? s2 : s1); \ + } \ + CLEAR_FN(vd, vl, vl * esz, vlmax * esz); \ +} + +GEN_VFMERGE_VF(vfmerge_vfm_h, int16_t, H2, clearh) +GEN_VFMERGE_VF(vfmerge_vfm_w, int32_t, H4, clearl) +GEN_VFMERGE_VF(vfmerge_vfm_d, int64_t, H8, clearq) + +/* Single-Width Floating-Point/Integer Type-Convert Instructions */ +/* vfcvt.xu.f.v vd, vs2, vm # Convert float to unsigned integer. */ +RVVCALL(OPFVV1, vfcvt_xu_f_v_h, OP_UU_H, H2, H2, float16_to_uint16) +RVVCALL(OPFVV1, vfcvt_xu_f_v_w, OP_UU_W, H4, H4, float32_to_uint32) +RVVCALL(OPFVV1, vfcvt_xu_f_v_d, OP_UU_D, H8, H8, float64_to_uint64) +GEN_VEXT_V_ENV(vfcvt_xu_f_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfcvt_xu_f_v_w, 4, 4, clearl) +GEN_VEXT_V_ENV(vfcvt_xu_f_v_d, 8, 8, clearq) + +/* vfcvt.x.f.v vd, vs2, vm # Convert float to signed integer. */ +RVVCALL(OPFVV1, vfcvt_x_f_v_h, OP_UU_H, H2, H2, float16_to_int16) +RVVCALL(OPFVV1, vfcvt_x_f_v_w, OP_UU_W, H4, H4, float32_to_int32) +RVVCALL(OPFVV1, vfcvt_x_f_v_d, OP_UU_D, H8, H8, float64_to_int64) +GEN_VEXT_V_ENV(vfcvt_x_f_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfcvt_x_f_v_w, 4, 4, clearl) +GEN_VEXT_V_ENV(vfcvt_x_f_v_d, 8, 8, clearq) + +/* vfcvt.f.xu.v vd, vs2, vm # Convert unsigned integer to float. */ +RVVCALL(OPFVV1, vfcvt_f_xu_v_h, OP_UU_H, H2, H2, uint16_to_float16) +RVVCALL(OPFVV1, vfcvt_f_xu_v_w, OP_UU_W, H4, H4, uint32_to_float32) +RVVCALL(OPFVV1, vfcvt_f_xu_v_d, OP_UU_D, H8, H8, uint64_to_float64) +GEN_VEXT_V_ENV(vfcvt_f_xu_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfcvt_f_xu_v_w, 4, 4, clearl) +GEN_VEXT_V_ENV(vfcvt_f_xu_v_d, 8, 8, clearq) + +/* vfcvt.f.x.v vd, vs2, vm # Convert integer to float. */ +RVVCALL(OPFVV1, vfcvt_f_x_v_h, OP_UU_H, H2, H2, int16_to_float16) +RVVCALL(OPFVV1, vfcvt_f_x_v_w, OP_UU_W, H4, H4, int32_to_float32) +RVVCALL(OPFVV1, vfcvt_f_x_v_d, OP_UU_D, H8, H8, int64_to_float64) +GEN_VEXT_V_ENV(vfcvt_f_x_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfcvt_f_x_v_w, 4, 4, clearl) +GEN_VEXT_V_ENV(vfcvt_f_x_v_d, 8, 8, clearq) + +/* Widening Floating-Point/Integer Type-Convert Instructions */ +/* (TD, T2, TX2) */ +#define WOP_UU_H uint32_t, uint16_t, uint16_t +#define WOP_UU_W uint64_t, uint32_t, uint32_t +/* vfwcvt.xu.f.v vd, vs2, vm # Convert float to double-width unsigned integer.*/ +RVVCALL(OPFVV1, vfwcvt_xu_f_v_h, WOP_UU_H, H4, H2, float16_to_uint32) +RVVCALL(OPFVV1, vfwcvt_xu_f_v_w, WOP_UU_W, H8, H4, float32_to_uint64) +GEN_VEXT_V_ENV(vfwcvt_xu_f_v_h, 2, 4, clearl) +GEN_VEXT_V_ENV(vfwcvt_xu_f_v_w, 4, 8, clearq) + +/* vfwcvt.x.f.v vd, vs2, vm # Convert float to double-width signed integer. */ +RVVCALL(OPFVV1, vfwcvt_x_f_v_h, WOP_UU_H, H4, H2, float16_to_int32) +RVVCALL(OPFVV1, vfwcvt_x_f_v_w, WOP_UU_W, H8, H4, float32_to_int64) +GEN_VEXT_V_ENV(vfwcvt_x_f_v_h, 2, 4, clearl) +GEN_VEXT_V_ENV(vfwcvt_x_f_v_w, 4, 8, clearq) + +/* vfwcvt.f.xu.v vd, vs2, vm # Convert unsigned integer to double-width float */ +RVVCALL(OPFVV1, vfwcvt_f_xu_v_h, WOP_UU_H, H4, H2, uint16_to_float32) +RVVCALL(OPFVV1, vfwcvt_f_xu_v_w, WOP_UU_W, H8, H4, uint32_to_float64) +GEN_VEXT_V_ENV(vfwcvt_f_xu_v_h, 2, 4, clearl) +GEN_VEXT_V_ENV(vfwcvt_f_xu_v_w, 4, 8, clearq) + +/* vfwcvt.f.x.v vd, vs2, vm # Convert integer to double-width float. */ +RVVCALL(OPFVV1, vfwcvt_f_x_v_h, WOP_UU_H, H4, H2, int16_to_float32) +RVVCALL(OPFVV1, vfwcvt_f_x_v_w, WOP_UU_W, H8, H4, int32_to_float64) +GEN_VEXT_V_ENV(vfwcvt_f_x_v_h, 2, 4, clearl) +GEN_VEXT_V_ENV(vfwcvt_f_x_v_w, 4, 8, clearq) + +/* + * vfwcvt.f.f.v vd, vs2, vm # + * Convert single-width float to double-width float. + */ +static uint32_t vfwcvtffv16(uint16_t a, float_status *s) +{ + return float16_to_float32(a, true, s); +} + +RVVCALL(OPFVV1, vfwcvt_f_f_v_h, WOP_UU_H, H4, H2, vfwcvtffv16) +RVVCALL(OPFVV1, vfwcvt_f_f_v_w, WOP_UU_W, H8, H4, float32_to_float64) +GEN_VEXT_V_ENV(vfwcvt_f_f_v_h, 2, 4, clearl) +GEN_VEXT_V_ENV(vfwcvt_f_f_v_w, 4, 8, clearq) + +/* Narrowing Floating-Point/Integer Type-Convert Instructions */ +/* (TD, T2, TX2) */ +#define NOP_UU_H uint16_t, uint32_t, uint32_t +#define NOP_UU_W uint32_t, uint64_t, uint64_t +/* vfncvt.xu.f.v vd, vs2, vm # Convert float to unsigned integer. */ +RVVCALL(OPFVV1, vfncvt_xu_f_v_h, NOP_UU_H, H2, H4, float32_to_uint16) +RVVCALL(OPFVV1, vfncvt_xu_f_v_w, NOP_UU_W, H4, H8, float64_to_uint32) +GEN_VEXT_V_ENV(vfncvt_xu_f_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfncvt_xu_f_v_w, 4, 4, clearl) + +/* vfncvt.x.f.v vd, vs2, vm # Convert double-width float to signed integer. */ +RVVCALL(OPFVV1, vfncvt_x_f_v_h, NOP_UU_H, H2, H4, float32_to_int16) +RVVCALL(OPFVV1, vfncvt_x_f_v_w, NOP_UU_W, H4, H8, float64_to_int32) +GEN_VEXT_V_ENV(vfncvt_x_f_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfncvt_x_f_v_w, 4, 4, clearl) + +/* vfncvt.f.xu.v vd, vs2, vm # Convert double-width unsigned integer to float */ +RVVCALL(OPFVV1, vfncvt_f_xu_v_h, NOP_UU_H, H2, H4, uint32_to_float16) +RVVCALL(OPFVV1, vfncvt_f_xu_v_w, NOP_UU_W, H4, H8, uint64_to_float32) +GEN_VEXT_V_ENV(vfncvt_f_xu_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfncvt_f_xu_v_w, 4, 4, clearl) + +/* vfncvt.f.x.v vd, vs2, vm # Convert double-width integer to float. */ +RVVCALL(OPFVV1, vfncvt_f_x_v_h, NOP_UU_H, H2, H4, int32_to_float16) +RVVCALL(OPFVV1, vfncvt_f_x_v_w, NOP_UU_W, H4, H8, int64_to_float32) +GEN_VEXT_V_ENV(vfncvt_f_x_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfncvt_f_x_v_w, 4, 4, clearl) + +/* vfncvt.f.f.v vd, vs2, vm # Convert double float to single-width float. */ +static uint16_t vfncvtffv16(uint32_t a, float_status *s) +{ + return float32_to_float16(a, true, s); +} + +RVVCALL(OPFVV1, vfncvt_f_f_v_h, NOP_UU_H, H2, H4, vfncvtffv16) +RVVCALL(OPFVV1, vfncvt_f_f_v_w, NOP_UU_W, H4, H8, float64_to_float32) +GEN_VEXT_V_ENV(vfncvt_f_f_v_h, 2, 2, clearh) +GEN_VEXT_V_ENV(vfncvt_f_f_v_w, 4, 4, clearl) + +/* + *** Vector Reduction Operations + */ +/* Vector Single-Width Integer Reduction Instructions */ +#define GEN_VEXT_RED(NAME, TD, TS2, HD, HS2, OP, CLEAR_FN)\ +void HELPER(NAME)(void *vd, void *v0, void *vs1, \ + void *vs2, CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t i; \ + uint32_t tot = env_archcpu(env)->cfg.vlen / 8; \ + TD s1 = *((TD *)vs1 + HD(0)); \ + \ + for (i = 0; i < vl; i++) { \ + TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + s1 = OP(s1, (TD)s2); \ + } \ + *((TD *)vd + HD(0)) = s1; \ + CLEAR_FN(vd, 1, sizeof(TD), tot); \ +} + +/* vd[0] = sum(vs1[0], vs2[*]) */ +GEN_VEXT_RED(vredsum_vs_b, int8_t, int8_t, H1, H1, DO_ADD, clearb) +GEN_VEXT_RED(vredsum_vs_h, int16_t, int16_t, H2, H2, DO_ADD, clearh) +GEN_VEXT_RED(vredsum_vs_w, int32_t, int32_t, H4, H4, DO_ADD, clearl) +GEN_VEXT_RED(vredsum_vs_d, int64_t, int64_t, H8, H8, DO_ADD, clearq) + +/* vd[0] = maxu(vs1[0], vs2[*]) */ +GEN_VEXT_RED(vredmaxu_vs_b, uint8_t, uint8_t, H1, H1, DO_MAX, clearb) +GEN_VEXT_RED(vredmaxu_vs_h, uint16_t, uint16_t, H2, H2, DO_MAX, clearh) +GEN_VEXT_RED(vredmaxu_vs_w, uint32_t, uint32_t, H4, H4, DO_MAX, clearl) +GEN_VEXT_RED(vredmaxu_vs_d, uint64_t, uint64_t, H8, H8, DO_MAX, clearq) + +/* vd[0] = max(vs1[0], vs2[*]) */ +GEN_VEXT_RED(vredmax_vs_b, int8_t, int8_t, H1, H1, DO_MAX, clearb) +GEN_VEXT_RED(vredmax_vs_h, int16_t, int16_t, H2, H2, DO_MAX, clearh) +GEN_VEXT_RED(vredmax_vs_w, int32_t, int32_t, H4, H4, DO_MAX, clearl) +GEN_VEXT_RED(vredmax_vs_d, int64_t, int64_t, H8, H8, DO_MAX, clearq) + +/* vd[0] = minu(vs1[0], vs2[*]) */ +GEN_VEXT_RED(vredminu_vs_b, uint8_t, uint8_t, H1, H1, DO_MIN, clearb) +GEN_VEXT_RED(vredminu_vs_h, uint16_t, uint16_t, H2, H2, DO_MIN, clearh) +GEN_VEXT_RED(vredminu_vs_w, uint32_t, uint32_t, H4, H4, DO_MIN, clearl) +GEN_VEXT_RED(vredminu_vs_d, uint64_t, uint64_t, H8, H8, DO_MIN, clearq) + +/* vd[0] = min(vs1[0], vs2[*]) */ +GEN_VEXT_RED(vredmin_vs_b, int8_t, int8_t, H1, H1, DO_MIN, clearb) +GEN_VEXT_RED(vredmin_vs_h, int16_t, int16_t, H2, H2, DO_MIN, clearh) +GEN_VEXT_RED(vredmin_vs_w, int32_t, int32_t, H4, H4, DO_MIN, clearl) +GEN_VEXT_RED(vredmin_vs_d, int64_t, int64_t, H8, H8, DO_MIN, clearq) + +/* vd[0] = and(vs1[0], vs2[*]) */ +GEN_VEXT_RED(vredand_vs_b, int8_t, int8_t, H1, H1, DO_AND, clearb) +GEN_VEXT_RED(vredand_vs_h, int16_t, int16_t, H2, H2, DO_AND, clearh) +GEN_VEXT_RED(vredand_vs_w, int32_t, int32_t, H4, H4, DO_AND, clearl) +GEN_VEXT_RED(vredand_vs_d, int64_t, int64_t, H8, H8, DO_AND, clearq) + +/* vd[0] = or(vs1[0], vs2[*]) */ +GEN_VEXT_RED(vredor_vs_b, int8_t, int8_t, H1, H1, DO_OR, clearb) +GEN_VEXT_RED(vredor_vs_h, int16_t, int16_t, H2, H2, DO_OR, clearh) +GEN_VEXT_RED(vredor_vs_w, int32_t, int32_t, H4, H4, DO_OR, clearl) +GEN_VEXT_RED(vredor_vs_d, int64_t, int64_t, H8, H8, DO_OR, clearq) + +/* vd[0] = xor(vs1[0], vs2[*]) */ +GEN_VEXT_RED(vredxor_vs_b, int8_t, int8_t, H1, H1, DO_XOR, clearb) +GEN_VEXT_RED(vredxor_vs_h, int16_t, int16_t, H2, H2, DO_XOR, clearh) +GEN_VEXT_RED(vredxor_vs_w, int32_t, int32_t, H4, H4, DO_XOR, clearl) +GEN_VEXT_RED(vredxor_vs_d, int64_t, int64_t, H8, H8, DO_XOR, clearq) + +/* Vector Widening Integer Reduction Instructions */ +/* signed sum reduction into double-width accumulator */ +GEN_VEXT_RED(vwredsum_vs_b, int16_t, int8_t, H2, H1, DO_ADD, clearh) +GEN_VEXT_RED(vwredsum_vs_h, int32_t, int16_t, H4, H2, DO_ADD, clearl) +GEN_VEXT_RED(vwredsum_vs_w, int64_t, int32_t, H8, H4, DO_ADD, clearq) + +/* Unsigned sum reduction into double-width accumulator */ +GEN_VEXT_RED(vwredsumu_vs_b, uint16_t, uint8_t, H2, H1, DO_ADD, clearh) +GEN_VEXT_RED(vwredsumu_vs_h, uint32_t, uint16_t, H4, H2, DO_ADD, clearl) +GEN_VEXT_RED(vwredsumu_vs_w, uint64_t, uint32_t, H8, H4, DO_ADD, clearq) + +/* Vector Single-Width Floating-Point Reduction Instructions */ +#define GEN_VEXT_FRED(NAME, TD, TS2, HD, HS2, OP, CLEAR_FN)\ +void HELPER(NAME)(void *vd, void *v0, void *vs1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t i; \ + uint32_t tot = env_archcpu(env)->cfg.vlen / 8; \ + TD s1 = *((TD *)vs1 + HD(0)); \ + \ + for (i = 0; i < vl; i++) { \ + TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + s1 = OP(s1, (TD)s2, &env->fp_status); \ + } \ + *((TD *)vd + HD(0)) = s1; \ + CLEAR_FN(vd, 1, sizeof(TD), tot); \ +} + +/* Unordered sum */ +GEN_VEXT_FRED(vfredsum_vs_h, uint16_t, uint16_t, H2, H2, float16_add, clearh) +GEN_VEXT_FRED(vfredsum_vs_w, uint32_t, uint32_t, H4, H4, float32_add, clearl) +GEN_VEXT_FRED(vfredsum_vs_d, uint64_t, uint64_t, H8, H8, float64_add, clearq) + +/* Maximum value */ +GEN_VEXT_FRED(vfredmax_vs_h, uint16_t, uint16_t, H2, H2, float16_maxnum, clearh) +GEN_VEXT_FRED(vfredmax_vs_w, uint32_t, uint32_t, H4, H4, float32_maxnum, clearl) +GEN_VEXT_FRED(vfredmax_vs_d, uint64_t, uint64_t, H8, H8, float64_maxnum, clearq) + +/* Minimum value */ +GEN_VEXT_FRED(vfredmin_vs_h, uint16_t, uint16_t, H2, H2, float16_minnum, clearh) +GEN_VEXT_FRED(vfredmin_vs_w, uint32_t, uint32_t, H4, H4, float32_minnum, clearl) +GEN_VEXT_FRED(vfredmin_vs_d, uint64_t, uint64_t, H8, H8, float64_minnum, clearq) + +/* Vector Widening Floating-Point Reduction Instructions */ +/* Unordered reduce 2*SEW = 2*SEW + sum(promote(SEW)) */ +void HELPER(vfwredsum_vs_h)(void *vd, void *v0, void *vs1, + void *vs2, CPURISCVState *env, uint32_t desc) +{ + uint32_t mlen = vext_mlen(desc); + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + uint32_t i; + uint32_t tot = env_archcpu(env)->cfg.vlen / 8; + uint32_t s1 = *((uint32_t *)vs1 + H4(0)); + + for (i = 0; i < vl; i++) { + uint16_t s2 = *((uint16_t *)vs2 + H2(i)); + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + s1 = float32_add(s1, float16_to_float32(s2, true, &env->fp_status), + &env->fp_status); + } + *((uint32_t *)vd + H4(0)) = s1; + clearl(vd, 1, sizeof(uint32_t), tot); +} + +void HELPER(vfwredsum_vs_w)(void *vd, void *v0, void *vs1, + void *vs2, CPURISCVState *env, uint32_t desc) +{ + uint32_t mlen = vext_mlen(desc); + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + uint32_t i; + uint32_t tot = env_archcpu(env)->cfg.vlen / 8; + uint64_t s1 = *((uint64_t *)vs1); + + for (i = 0; i < vl; i++) { + uint32_t s2 = *((uint32_t *)vs2 + H4(i)); + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + s1 = float64_add(s1, float32_to_float64(s2, &env->fp_status), + &env->fp_status); + } + *((uint64_t *)vd) = s1; + clearq(vd, 1, sizeof(uint64_t), tot); +} + +/* + *** Vector Mask Operations + */ +/* Vector Mask-Register Logical Instructions */ +#define GEN_VEXT_MASK_VV(NAME, OP) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vl = env->vl; \ + uint32_t i; \ + int a, b; \ + \ + for (i = 0; i < vl; i++) { \ + a = vext_elem_mask(vs1, mlen, i); \ + b = vext_elem_mask(vs2, mlen, i); \ + vext_set_elem_mask(vd, mlen, i, OP(b, a)); \ + } \ + for (; i < vlmax; i++) { \ + vext_set_elem_mask(vd, mlen, i, 0); \ + } \ +} + +#define DO_NAND(N, M) (!(N & M)) +#define DO_ANDNOT(N, M) (N & !M) +#define DO_NOR(N, M) (!(N | M)) +#define DO_ORNOT(N, M) (N | !M) +#define DO_XNOR(N, M) (!(N ^ M)) + +GEN_VEXT_MASK_VV(vmand_mm, DO_AND) +GEN_VEXT_MASK_VV(vmnand_mm, DO_NAND) +GEN_VEXT_MASK_VV(vmandnot_mm, DO_ANDNOT) +GEN_VEXT_MASK_VV(vmxor_mm, DO_XOR) +GEN_VEXT_MASK_VV(vmor_mm, DO_OR) +GEN_VEXT_MASK_VV(vmnor_mm, DO_NOR) +GEN_VEXT_MASK_VV(vmornot_mm, DO_ORNOT) +GEN_VEXT_MASK_VV(vmxnor_mm, DO_XNOR) + +/* Vector mask population count vmpopc */ +target_ulong HELPER(vmpopc_m)(void *v0, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + target_ulong cnt = 0; + uint32_t mlen = vext_mlen(desc); + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + int i; + + for (i = 0; i < vl; i++) { + if (vm || vext_elem_mask(v0, mlen, i)) { + if (vext_elem_mask(vs2, mlen, i)) { + cnt++; + } + } + } + return cnt; +} + +/* vmfirst find-first-set mask bit*/ +target_ulong HELPER(vmfirst_m)(void *v0, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + uint32_t mlen = vext_mlen(desc); + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + int i; + + for (i = 0; i < vl; i++) { + if (vm || vext_elem_mask(v0, mlen, i)) { + if (vext_elem_mask(vs2, mlen, i)) { + return i; + } + } + } + return -1LL; +} + +enum set_mask_type { + ONLY_FIRST = 1, + INCLUDE_FIRST, + BEFORE_FIRST, +}; + +static void vmsetm(void *vd, void *v0, void *vs2, CPURISCVState *env, + uint32_t desc, enum set_mask_type type) +{ + uint32_t mlen = vext_mlen(desc); + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + int i; + bool first_mask_bit = false; + + for (i = 0; i < vl; i++) { + if (!vm && !vext_elem_mask(v0, mlen, i)) { + continue; + } + /* write a zero to all following active elements */ + if (first_mask_bit) { + vext_set_elem_mask(vd, mlen, i, 0); + continue; + } + if (vext_elem_mask(vs2, mlen, i)) { + first_mask_bit = true; + if (type == BEFORE_FIRST) { + vext_set_elem_mask(vd, mlen, i, 0); + } else { + vext_set_elem_mask(vd, mlen, i, 1); + } + } else { + if (type == ONLY_FIRST) { + vext_set_elem_mask(vd, mlen, i, 0); + } else { + vext_set_elem_mask(vd, mlen, i, 1); + } + } + } + for (; i < vlmax; i++) { + vext_set_elem_mask(vd, mlen, i, 0); + } +} + +void HELPER(vmsbf_m)(void *vd, void *v0, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + vmsetm(vd, v0, vs2, env, desc, BEFORE_FIRST); +} + +void HELPER(vmsif_m)(void *vd, void *v0, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + vmsetm(vd, v0, vs2, env, desc, INCLUDE_FIRST); +} + +void HELPER(vmsof_m)(void *vd, void *v0, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + vmsetm(vd, v0, vs2, env, desc, ONLY_FIRST); +} + +/* Vector Iota Instruction */ +#define GEN_VEXT_VIOTA_M(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t sum = 0; \ + int i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + *((ETYPE *)vd + H(i)) = sum; \ + if (vext_elem_mask(vs2, mlen, i)) { \ + sum++; \ + } \ + } \ + CLEAR_FN(vd, vl, vl * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +GEN_VEXT_VIOTA_M(viota_m_b, uint8_t, H1, clearb) +GEN_VEXT_VIOTA_M(viota_m_h, uint16_t, H2, clearh) +GEN_VEXT_VIOTA_M(viota_m_w, uint32_t, H4, clearl) +GEN_VEXT_VIOTA_M(viota_m_d, uint64_t, H8, clearq) + +/* Vector Element Index Instruction */ +#define GEN_VEXT_VID_V(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + int i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + *((ETYPE *)vd + H(i)) = i; \ + } \ + CLEAR_FN(vd, vl, vl * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +GEN_VEXT_VID_V(vid_v_b, uint8_t, H1, clearb) +GEN_VEXT_VID_V(vid_v_h, uint16_t, H2, clearh) +GEN_VEXT_VID_V(vid_v_w, uint32_t, H4, clearl) +GEN_VEXT_VID_V(vid_v_d, uint64_t, H8, clearq) + +/* + *** Vector Permutation Instructions + */ + +/* Vector Slide Instructions */ +#define GEN_VEXT_VSLIDEUP_VX(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + target_ulong offset = s1, i; \ + \ + for (i = offset; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i - offset)); \ + } \ + CLEAR_FN(vd, vl, vl * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +/* vslideup.vx vd, vs2, rs1, vm # vd[i+rs1] = vs2[i] */ +GEN_VEXT_VSLIDEUP_VX(vslideup_vx_b, uint8_t, H1, clearb) +GEN_VEXT_VSLIDEUP_VX(vslideup_vx_h, uint16_t, H2, clearh) +GEN_VEXT_VSLIDEUP_VX(vslideup_vx_w, uint32_t, H4, clearl) +GEN_VEXT_VSLIDEUP_VX(vslideup_vx_d, uint64_t, H8, clearq) + +#define GEN_VEXT_VSLIDEDOWN_VX(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + target_ulong offset = s1, i; \ + \ + for (i = 0; i < vl; ++i) { \ + target_ulong j = i + offset; \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + *((ETYPE *)vd + H(i)) = j >= vlmax ? 0 : *((ETYPE *)vs2 + H(j)); \ + } \ + CLEAR_FN(vd, vl, vl * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +/* vslidedown.vx vd, vs2, rs1, vm # vd[i] = vs2[i+rs1] */ +GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_b, uint8_t, H1, clearb) +GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_h, uint16_t, H2, clearh) +GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_w, uint32_t, H4, clearl) +GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_d, uint64_t, H8, clearq) + +#define GEN_VEXT_VSLIDE1UP_VX(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + if (i == 0) { \ + *((ETYPE *)vd + H(i)) = s1; \ + } else { \ + *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i - 1)); \ + } \ + } \ + CLEAR_FN(vd, vl, vl * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +/* vslide1up.vx vd, vs2, rs1, vm # vd[0]=x[rs1], vd[i+1] = vs2[i] */ +GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_b, uint8_t, H1, clearb) +GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_h, uint16_t, H2, clearh) +GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_w, uint32_t, H4, clearl) +GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_d, uint64_t, H8, clearq) + +#define GEN_VEXT_VSLIDE1DOWN_VX(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + if (i == vl - 1) { \ + *((ETYPE *)vd + H(i)) = s1; \ + } else { \ + *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i + 1)); \ + } \ + } \ + CLEAR_FN(vd, vl, vl * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +/* vslide1down.vx vd, vs2, rs1, vm # vd[i] = vs2[i+1], vd[vl-1]=x[rs1] */ +GEN_VEXT_VSLIDE1DOWN_VX(vslide1down_vx_b, uint8_t, H1, clearb) +GEN_VEXT_VSLIDE1DOWN_VX(vslide1down_vx_h, uint16_t, H2, clearh) +GEN_VEXT_VSLIDE1DOWN_VX(vslide1down_vx_w, uint32_t, H4, clearl) +GEN_VEXT_VSLIDE1DOWN_VX(vslide1down_vx_d, uint64_t, H8, clearq) + +/* Vector Register Gather Instruction */ +#define GEN_VEXT_VRGATHER_VV(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t index, i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + index = *((ETYPE *)vs1 + H(i)); \ + if (index >= vlmax) { \ + *((ETYPE *)vd + H(i)) = 0; \ + } else { \ + *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(index)); \ + } \ + } \ + CLEAR_FN(vd, vl, vl * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +/* vd[i] = (vs1[i] >= VLMAX) ? 0 : vs2[vs1[i]]; */ +GEN_VEXT_VRGATHER_VV(vrgather_vv_b, uint8_t, H1, clearb) +GEN_VEXT_VRGATHER_VV(vrgather_vv_h, uint16_t, H2, clearh) +GEN_VEXT_VRGATHER_VV(vrgather_vv_w, uint32_t, H4, clearl) +GEN_VEXT_VRGATHER_VV(vrgather_vv_d, uint64_t, H8, clearq) + +#define GEN_VEXT_VRGATHER_VX(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t index = s1, i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, mlen, i)) { \ + continue; \ + } \ + if (index >= vlmax) { \ + *((ETYPE *)vd + H(i)) = 0; \ + } else { \ + *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(index)); \ + } \ + } \ + CLEAR_FN(vd, vl, vl * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +/* vd[i] = (x[rs1] >= VLMAX) ? 0 : vs2[rs1] */ +GEN_VEXT_VRGATHER_VX(vrgather_vx_b, uint8_t, H1, clearb) +GEN_VEXT_VRGATHER_VX(vrgather_vx_h, uint16_t, H2, clearh) +GEN_VEXT_VRGATHER_VX(vrgather_vx_w, uint32_t, H4, clearl) +GEN_VEXT_VRGATHER_VX(vrgather_vx_d, uint64_t, H8, clearq) + +/* Vector Compress Instruction */ +#define GEN_VEXT_VCOMPRESS_VM(NAME, ETYPE, H, CLEAR_FN) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t mlen = vext_mlen(desc); \ + uint32_t vlmax = env_archcpu(env)->cfg.vlen / mlen; \ + uint32_t vl = env->vl; \ + uint32_t num = 0, i; \ + \ + for (i = 0; i < vl; i++) { \ + if (!vext_elem_mask(vs1, mlen, i)) { \ + continue; \ + } \ + *((ETYPE *)vd + H(num)) = *((ETYPE *)vs2 + H(i)); \ + num++; \ + } \ + CLEAR_FN(vd, num, num * sizeof(ETYPE), vlmax * sizeof(ETYPE)); \ +} + +/* Compress into vd elements of vs2 where vs1 is enabled */ +GEN_VEXT_VCOMPRESS_VM(vcompress_vm_b, uint8_t, H1, clearb) +GEN_VEXT_VCOMPRESS_VM(vcompress_vm_h, uint16_t, H2, clearh) +GEN_VEXT_VCOMPRESS_VM(vcompress_vm_w, uint32_t, H4, clearl) +GEN_VEXT_VCOMPRESS_VM(vcompress_vm_d, uint64_t, H8, clearq) diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index 91ddaedd84..d79ae9e3f1 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -798,7 +798,7 @@ /* SQUARE ROOT */ F(0xb314, SQEBR, RRE, Z, 0, e2, new, e1, sqeb, 0, IF_BFP) F(0xb315, SQDBR, RRE, Z, 0, f2, new, f1, sqdb, 0, IF_BFP) - F(0xb316, SQXBR, RRE, Z, x2h, x2l, new, x1, sqxb, 0, IF_BFP) + F(0xb316, SQXBR, RRE, Z, x2h, x2l, new_P, x1, sqxb, 0, IF_BFP) F(0xed14, SQEB, RXE, Z, 0, m2_32u, new, e1, sqeb, 0, IF_BFP) F(0xed15, SQDB, RXE, Z, 0, m2_64, new, f1, sqdb, 0, IF_BFP) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 9416a551cf..1a4efd4ed6 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -3663,6 +3663,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) #endif gen_store_gpr(dc, rd, cpu_tmp0); break; +#endif +#if defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY) } else if (xop == 0x2b) { /* rdtbr / V9 flushw */ #ifdef TARGET_SPARC64 gen_helper_flushw(cpu_env); diff --git a/tcg/ppc/tcg-target.inc.c b/tcg/ppc/tcg-target.inc.c index 7da67086c6..c8d1e765d9 100644 --- a/tcg/ppc/tcg-target.inc.c +++ b/tcg/ppc/tcg-target.inc.c @@ -2610,21 +2610,24 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, case INDEX_op_shl_i32: if (const_args[2]) { - tcg_out_shli32(s, args[0], args[1], args[2]); + /* Limit immediate shift count lest we create an illegal insn. */ + tcg_out_shli32(s, args[0], args[1], args[2] & 31); } else { tcg_out32(s, SLW | SAB(args[1], args[0], args[2])); } break; case INDEX_op_shr_i32: if (const_args[2]) { - tcg_out_shri32(s, args[0], args[1], args[2]); + /* Limit immediate shift count lest we create an illegal insn. */ + tcg_out_shri32(s, args[0], args[1], args[2] & 31); } else { tcg_out32(s, SRW | SAB(args[1], args[0], args[2])); } break; case INDEX_op_sar_i32: if (const_args[2]) { - tcg_out32(s, SRAWI | RS(args[1]) | RA(args[0]) | SH(args[2])); + /* Limit immediate shift count lest we create an illegal insn. */ + tcg_out32(s, SRAWI | RS(args[1]) | RA(args[0]) | SH(args[2] & 31)); } else { tcg_out32(s, SRAW | SAB(args[1], args[0], args[2])); } @@ -2696,14 +2699,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, case INDEX_op_shl_i64: if (const_args[2]) { - tcg_out_shli64(s, args[0], args[1], args[2]); + /* Limit immediate shift count lest we create an illegal insn. */ + tcg_out_shli64(s, args[0], args[1], args[2] & 63); } else { tcg_out32(s, SLD | SAB(args[1], args[0], args[2])); } break; case INDEX_op_shr_i64: if (const_args[2]) { - tcg_out_shri64(s, args[0], args[1], args[2]); + /* Limit immediate shift count lest we create an illegal insn. */ + tcg_out_shri64(s, args[0], args[1], args[2] & 63); } else { tcg_out32(s, SRD | SAB(args[1], args[0], args[2])); } diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index e60b74fb82..4b8a473fad 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -3189,8 +3189,9 @@ static void do_nonatomic_op_i32(TCGv_i32 ret, TCGv addr, TCGv_i32 val, memop = tcg_canonicalize_memop(memop, 0, 0); - tcg_gen_qemu_ld_i32(t1, addr, idx, memop & ~MO_SIGN); - gen(t2, t1, val); + tcg_gen_qemu_ld_i32(t1, addr, idx, memop); + tcg_gen_ext_i32(t2, val, memop); + gen(t2, t1, t2); tcg_gen_qemu_st_i32(t2, addr, idx, memop); tcg_gen_ext_i32(ret, (new_val ? t2 : t1), memop); @@ -3232,8 +3233,9 @@ static void do_nonatomic_op_i64(TCGv_i64 ret, TCGv addr, TCGv_i64 val, memop = tcg_canonicalize_memop(memop, 1, 0); - tcg_gen_qemu_ld_i64(t1, addr, idx, memop & ~MO_SIGN); - gen(t2, t1, val); + tcg_gen_qemu_ld_i64(t1, addr, idx, memop); + tcg_gen_ext_i64(t2, val, memop); + gen(t2, t1, t2); tcg_gen_qemu_st_i64(t2, addr, idx, memop); tcg_gen_ext_i64(ret, (new_val ? t2 : t1), memop); diff --git a/tests/acceptance/machine_sparc64_sun4u.py b/tests/acceptance/machine_sparc64_sun4u.py new file mode 100644 index 0000000000..458165500e --- /dev/null +++ b/tests/acceptance/machine_sparc64_sun4u.py @@ -0,0 +1,36 @@ +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2020 Red Hat, Inc. +# +# Author: +# Thomas Huth <thuth@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from avocado_qemu import wait_for_console_pattern +from avocado.utils import archive +from boot_linux_console import LinuxKernelTest + +class Sun4uMachine(LinuxKernelTest): + """Boots the Linux kernel and checks that the console is operational""" + + timeout = 90 + + def test_sparc64_sun4u(self): + """ + :avocado: tags=arch:sparc64 + :avocado: tags=machine:sun4u + """ + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day23.tar.xz') + tar_hash = '142db83cd974ffadc4f75c8a5cad5bcc5722c240' + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + archive.extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/day23/vmlinux', + '-append', self.KERNEL_COMMON_COMMAND_LINE) + self.vm.launch() + wait_for_console_pattern(self, 'Starting logging: OK') diff --git a/tests/data/acpi/disassemle-aml.sh b/tests/data/acpi/disassemle-aml.sh new file mode 100755 index 0000000000..1d8a4d0301 --- /dev/null +++ b/tests/data/acpi/disassemle-aml.sh @@ -0,0 +1,52 @@ +#!/usr/bin/bash + +outdir= +while getopts "o:" arg; do + case ${arg} in + o ) + outdir=$OPTARG + ;; + \? ) + echo "Usage: ./tests/data/acpi/disassemle-aml.sh [-o <output-directory>]" + exit 1 + ;; + + esac +done + +for machine in tests/data/acpi/* +do + if [[ ! -d "$machine" ]]; + then + continue + fi + + if [[ "${outdir}" ]]; + then + mkdir -p "${outdir}"/${machine} || exit $? + fi + for aml in $machine/* + do + if [[ "$aml" == $machine/*.dsl ]]; + then + continue + fi + if [[ "$aml" == $machine/SSDT*.* ]]; + then + dsdt=${aml/SSDT*./DSDT.} + extra="-e ${dsdt}" + elif [[ "$aml" == $machine/SSDT* ]]; + then + dsdt=${aml/SSDT*/DSDT}; + extra="-e ${dsdt}" + else + extra="" + fi + asl=${aml}.dsl + if [[ "${outdir}" ]]; + then + asl="${outdir}"/${machine}/${asl} + fi + iasl -d -p ${asl} ${extra} ${aml} + done +done diff --git a/tests/data/acpi/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh index 9cbaab1a4d..76cd797d1e 100755 --- a/tests/data/acpi/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -36,6 +36,7 @@ old_allowed_dif=`grep -v -e 'List of comma-separated changed AML files to ignore echo '/* List of comma-separated changed AML files to ignore */' > ${SRC_PATH}/tests/qtest/bios-tables-test-allowed-diff.h echo "The files were rebuilt and can be added to git." +echo "You can use ${SRC_PATH}/tests/data/acpi/disassemle-aml.sh to disassemble them to ASL." if [ -z "$old_allowed_dif" ]; then echo "Note! Please do not commit expected files with source changes" diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT Binary files differindex d6f5c61788..e669508d17 100644 --- a/tests/data/acpi/virt/DSDT +++ b/tests/data/acpi/virt/DSDT diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp Binary files differindex 730e95a46d..4cb81f692d 100644 --- a/tests/data/acpi/virt/DSDT.memhp +++ b/tests/data/acpi/virt/DSDT.memhp diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem Binary files differindex d6f5c61788..e669508d17 100644 --- a/tests/data/acpi/virt/DSDT.numamem +++ b/tests/data/acpi/virt/DSDT.numamem diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index c54ae21b86..e77966446b 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -4,90 +4,90 @@ QA output created by 049 == 1. Traditional size parameter == qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16 == 2. Specifying size via -o == qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16 == 3. Invalid sizes == @@ -129,84 +129,84 @@ qemu-img: TEST_DIR/t.qcow2: The image size must be specified only once == Check correct interpretation of suffixes for cluster size == qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1048576 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1048576 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024.0b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=512 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=512 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=512 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=512 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=524288 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=524288 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 == Check compat level option == qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.42 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.42 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=foobar cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=foobar lazy_refcounts=off refcount_bits=16 == Check preallocation option == qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=off lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 preallocation=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=metadata lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 preallocation=metadata compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=1234 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 preallocation=1234 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 == Check encryption option == qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 encryption=off cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 --object secret,id=sec0,data=123456 -o encryption=on,encrypt.key-secret=sec0 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on encrypt.key-secret=sec0 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 encryption=on encrypt.key-secret=sec0 cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 == Check lazy_refcounts option (only with v3) == qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=on refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=on refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=on refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=on refcount_bits=16 *** done diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 2f03cf045c..b0f8befe30 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -381,16 +381,20 @@ qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above ( qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) qemu-img: Unknown compatibility level 0.42 qemu-img: Invalid parameter 'foo' -qemu-img: Changing the cluster size is not supported -qemu-img: Changing the encryption flag is not supported -qemu-img: Cannot change preallocation mode +qemu-img: Invalid parameter 'cluster_size' +This option is only supported for image creation +qemu-img: Invalid parameter 'encryption' +This option is only supported for image creation +qemu-img: Invalid parameter 'preallocation' +This option is only supported for image creation === Testing correct handling of unset value === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Should work: Should not work: -qemu-img: Changing the cluster size is not supported +qemu-img: Invalid parameter 'cluster_size' +This option is only supported for image creation === Testing zero expansion on inactive clusters === diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index 529a1214e1..a4a2b69030 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -3,14 +3,14 @@ QA output created by 082 === create: Options specified more than once === Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128 MiB (134217728 bytes) cluster_size: 65536 Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=4096 lazy_refcounts=on refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=4096 compression_type=zlib size=134217728 lazy_refcounts=on refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128 MiB (134217728 bytes) @@ -23,7 +23,7 @@ Format specific information: corrupt: false Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=8192 lazy_refcounts=on refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=8192 compression_type=zlib size=134217728 lazy_refcounts=on refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128 MiB (134217728 bytes) @@ -36,7 +36,7 @@ Format specific information: corrupt: false Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=8192 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=8192 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128 MiB (134217728 bytes) @@ -237,10 +237,10 @@ Supported options: size=<size> - Virtual disk size Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2,,help cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2,,help lazy_refcounts=off refcount_bits=16 Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2,,? cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2,,? lazy_refcounts=off refcount_bits=16 Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2, @@ -290,7 +290,7 @@ qemu-img: Format driver 'bochs' does not support image creation === convert: Options specified more than once === Testing: create -f qcow2 TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base image: TEST_DIR/t.IMGFMT.base @@ -639,205 +639,133 @@ cluster_size: 65536 === amend: help for -o === Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2 -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2 @@ -856,30 +784,21 @@ Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/ qemu-img: Invalid option list: ,, Testing: amend -f qcow2 -o help -Creation options for 'qcow2': +Amend options for 'qcow2': backing_file=<str> - File name of a base image backing_fmt=<str> - Image format of the base image - cluster_size=<size> - qcow2 cluster size compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1]) - compression_type=<str> - Compression method used for image cluster compression data_file=<str> - File name of an external data file data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image - encrypt.cipher-alg=<str> - Name of encryption cipher algorithm - encrypt.cipher-mode=<str> - Name of encryption cipher mode - encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' - encrypt.hash-alg=<str> - Name of encryption hash algorithm encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds - encrypt.ivgen-alg=<str> - Name of IV generator algorithm - encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm - encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase - encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + encrypt.keyslot=<num> - Select a single keyslot to modify explicitly + encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase + encrypt.old-secret=<str> - Select all keyslots that match this password + encrypt.state=<str> - Select new state of affected keyslots (active/inactive) lazy_refcounts=<bool (on/off)> - Postpone refcount updates - preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits=<num> - Width of a reference count entry in bits size=<size> - Virtual disk size -Note that not all of these options may be amendable. - Testing: amend -o help qemu-img: Expecting one image file name diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out index a822ff4ef6..d68c06efdf 100644 --- a/tests/qemu-iotests/085.out +++ b/tests/qemu-iotests/085.out @@ -13,7 +13,7 @@ Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728 === Create a single snapshot on virtio0 === { 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'virtio0', 'snapshot-file':'TEST_DIR/1-snapshot-v0.IMGFMT', 'format': 'IMGFMT' } } -Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.1 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2.1 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} === Invalid command - missing device and nodename === @@ -30,40 +30,40 @@ Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file === Create several transactional group snapshots === { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/2-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/2-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/1-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/1-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2.2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/3-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/3-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/2-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/2-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/2-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/2-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/4-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/4-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/3-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/3-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/3-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/3-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/5-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/5-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/4-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/4-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/4-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/4-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/6-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/6-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/5-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/5-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/5-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/5-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/7-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/7-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/6-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/6-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/6-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/6-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/8-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/8-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/7-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/7-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/7-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/7-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/9-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/9-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/8-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/8-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/8-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/8-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/10-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/10-snapshot-v1.IMGFMT' } } ] } } -Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/9-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib -Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/9-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/9-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/9-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} === Create a couple of snapshots using blockdev-snapshot === diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index bdfdad3454..678e748c58 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +_require_working_luks do_run_qemu() { diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out index 2d92ea847b..b61ba638af 100644 --- a/tests/qemu-iotests/087.out +++ b/tests/qemu-iotests/087.out @@ -34,7 +34,7 @@ QMP_VERSION === Encrypted image QCow === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.key-secret=sec0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on Testing: QMP_VERSION {"return": {}} @@ -46,7 +46,7 @@ QMP_VERSION === Encrypted image LUKS === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encrypt.format=luks encrypt.key-secret=sec0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 Testing: QMP_VERSION {"return": {}} @@ -58,7 +58,7 @@ QMP_VERSION === Missing driver === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.key-secret=sec0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on Testing: -S QMP_VERSION {"return": {}} diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out index ae0318cabe..182655dbf6 100644 --- a/tests/qemu-iotests/112.out +++ b/tests/qemu-iotests/112.out @@ -5,7 +5,7 @@ QA output created by 112 qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount_bits=-1 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125 index d510984045..7cb1c19730 100755 --- a/tests/qemu-iotests/125 +++ b/tests/qemu-iotests/125 @@ -164,6 +164,30 @@ for GROWTH_SIZE in 16 48 80; do done done +# Test image resizing using preallocation and unaligned offsets +$QEMU_IMG create -f raw "$TEST_IMG.base" 128k | _filter_img_create +$QEMU_IO -c 'write -q -P 1 0 128k' -f raw "$TEST_IMG.base" +for orig_size in 31k 33k; do + echo "--- Resizing image from $orig_size to 96k ---" + _make_test_img -F raw -b "$TEST_IMG.base" -o cluster_size=64k "$orig_size" + $QEMU_IMG resize -f "$IMGFMT" --preallocation=full "$TEST_IMG" 96k + # The first part of the image should contain data from the backing file + $QEMU_IO -c "read -q -P 1 0 ${orig_size}" "$TEST_IMG" + # The resized part of the image should contain zeroes + $QEMU_IO -c "read -q -P 0 ${orig_size} 63k" "$TEST_IMG" + # If the image does not have an external data file we can also verify its + # actual size. The resized image should have 7 clusters: + # header, L1 table, L2 table, refcount table, refcount block, 2 data clusters + if ! _get_data_file "$TEST_IMG" > /dev/null; then + expected_file_length=$((65536 * 7)) + file_length=$(stat -c '%s' "$TEST_IMG_FILE") + if [ "$file_length" != "$expected_file_length" ]; then + echo "ERROR: file length $file_length (expected $expected_file_length)" + fi + fi + echo +done + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/125.out b/tests/qemu-iotests/125.out index 596905f533..7f76f7af20 100644 --- a/tests/qemu-iotests/125.out +++ b/tests/qemu-iotests/125.out @@ -767,4 +767,13 @@ wrote 2048000/2048000 bytes at offset 0 wrote 81920/81920 bytes at offset 2048000 80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=raw size=131072 +--- Resizing image from 31k to 96k --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=31744 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw +Image resized. + +--- Resizing image from 33k to 96k --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33792 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw +Image resized. + *** done diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out index 09d46f6b17..4abc5b5f7d 100644 --- a/tests/qemu-iotests/134.out +++ b/tests/qemu-iotests/134.out @@ -1,5 +1,5 @@ QA output created by 134 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.key-secret=sec0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on == reading whole image == read 134217728/134217728 bytes at offset 0 diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index 5192d256e3..6d1b7b0d4c 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -68,7 +68,7 @@ test_blockjob() _send_qemu_cmd $QEMU_HANDLE \ "$1" \ "$2" \ - | _filter_img_create | _filter_qmp_empty_return + | _filter_img_create_in_qmp | _filter_qmp_empty_return # We want this to return an error because the block job is still running _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out index 885a8874a5..a2172a1308 100644 --- a/tests/qemu-iotests/144.out +++ b/tests/qemu-iotests/144.out @@ -9,7 +9,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=536870912 { 'execute': 'qmp_capabilities' } {"return": {}} { 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'virtio0', 'snapshot-file':'TEST_DIR/tmp.IMGFMT', 'format': 'IMGFMT' } } -Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} === Performing block-commit on active layer === @@ -31,6 +31,6 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/ === Performing Live Snapshot 2 === { 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'virtio0', 'snapshot-file':'TEST_DIR/tmp2.IMGFMT', 'format': 'IMGFMT' } } -Formatting 'TEST_DIR/tmp2.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/tmp2.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} *** done diff --git a/tests/qemu-iotests/146 b/tests/qemu-iotests/146 index 2e43abddfc..ddc3c1fd80 100755 --- a/tests/qemu-iotests/146 +++ b/tests/qemu-iotests/146 @@ -51,19 +51,25 @@ echo === Testing VPC Autodetect === echo _use_sample_img virtualpc-dynamic.vhd.bz2 -${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Testing VPC with current_size force === echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=current_size,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Testing VPC with chs force === echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=chs,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map _cleanup_test_img @@ -72,19 +78,25 @@ echo === Testing Hyper-V Autodetect === echo _use_sample_img hyperv2012r2-dynamic.vhd.bz2 -${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Testing Hyper-V with current_size force === echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=current_size,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Testing Hyper-V with chs force === echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=chs,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map _cleanup_test_img @@ -93,19 +105,25 @@ echo === Testing d2v Autodetect === echo _use_sample_img d2v-zerofilled.vhd.bz2 -${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Testing d2v with current_size force === echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=current_size,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Testing d2v with chs force === echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=chs,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map _cleanup_test_img @@ -121,19 +139,25 @@ echo echo === Read created image, default opts ==== echo -${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Read created image, force_size_calc=chs ==== echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=chs,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Read created image, force_size_calc=current_size ==== echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=current_size,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Testing Image create, force_size === @@ -145,19 +169,25 @@ echo echo === Read created image, default opts ==== echo -${QEMU_IO} -c "open -o driver=vpc ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Read created image, force_size_calc=chs ==== echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=chs ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=chs,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo echo === Read created image, force_size_calc=current_size ==== echo -${QEMU_IO} -c "open -o driver=vpc,force_size_calc=current_size ${TEST_IMG}" -c 'map' +$QEMU_IMG map --output=json --image-opts \ + "driver=vpc,force_size_calc=current_size,file.filename=$TEST_IMG" \ + | _filter_qemu_img_map echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/146.out b/tests/qemu-iotests/146.out index 1332189d87..80513cdd06 100644 --- a/tests/qemu-iotests/146.out +++ b/tests/qemu-iotests/146.out @@ -2,39 +2,414 @@ QA output created by 146 === Testing VPC Autodetect === -126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false }] === Testing VPC with current_size force === -127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false }] === Testing VPC with chs force === -126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false }] === Testing Hyper-V Autodetect === -127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false }] === Testing Hyper-V with current_size force === -127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false }] === Testing Hyper-V with chs force === -126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false }] === Testing d2v Autodetect === -251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET }] === Testing d2v with current_size force === -251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET }] === Testing d2v with chs force === -251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET }, +{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET }] === Testing Image create, default === @@ -42,15 +417,15 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 === Read created image, default opts ==== -4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false }] === Read created image, force_size_calc=chs ==== -4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false }] === Read created image, force_size_calc=current_size ==== -4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false }] === Testing Image create, force_size === @@ -58,13 +433,13 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 === Read created image, default opts ==== -4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false }] === Read created image, force_size_calc=chs ==== -4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false }] === Read created image, force_size_calc=current_size ==== -4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0) +[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false }] *** done diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index cf961d3609..11e3d28841 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -167,11 +167,10 @@ done echo echo "== Creating ${TEST_IMG}.[abc] ==" | _filter_testdir -( - $QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}" - $QEMU_IMG create -f qcow2 "${TEST_IMG}.b" -b "${TEST_IMG}" - $QEMU_IMG create -f qcow2 "${TEST_IMG}.c" -b "${TEST_IMG}.b" -) | _filter_img_create +$QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}" | _filter_img_create +$QEMU_IMG create -f qcow2 "${TEST_IMG}.b" -b "${TEST_IMG}" | _filter_img_create +$QEMU_IMG create -f qcow2 "${TEST_IMG}.c" -b "${TEST_IMG}.b" \ + | _filter_img_create echo echo "== Two devices sharing the same file in backing chain ==" diff --git a/tests/qemu-iotests/158.out b/tests/qemu-iotests/158.out index 6def216e55..f28a17626b 100644 --- a/tests/qemu-iotests/158.out +++ b/tests/qemu-iotests/158.out @@ -1,6 +1,6 @@ QA output created by 158 == create base == -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 encryption=on encrypt.key-secret=sec0 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 encryption=on == writing whole image == wrote 134217728/134217728 bytes at offset 0 @@ -10,7 +10,7 @@ wrote 134217728/134217728 bytes at offset 0 read 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == create overlay == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base encryption=on encrypt.key-secret=sec0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base encryption=on == writing part of a cluster == wrote 1024/1024 bytes at offset 0 diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178 index 7cf0e27154..f09b27caac 100755 --- a/tests/qemu-iotests/178 +++ b/tests/qemu-iotests/178 @@ -41,6 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt raw qcow2 _supported_proto file _supported_os Linux +_require_working_luks echo "== Input validation ==" echo diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out index ae43654d32..29e9db3497 100644 --- a/tests/qemu-iotests/182.out +++ b/tests/qemu-iotests/182.out @@ -13,7 +13,7 @@ Is another process using the image [TEST_DIR/t.qcow2]? {'execute': 'blockdev-add', 'arguments': { 'node-name': 'node0', 'driver': 'file', 'filename': 'TEST_DIR/t.IMGFMT', 'locking': 'on' } } {"return": {}} {'execute': 'blockdev-snapshot-sync', 'arguments': { 'node-name': 'node0', 'snapshot-file': 'TEST_DIR/t.IMGFMT.overlay', 'snapshot-node-name': 'node1' } } -Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 size=197120 backing_file=TEST_DIR/t.qcow2 backing_fmt=file cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 cluster_size=65536 compression_type=zlib size=197120 backing_file=TEST_DIR/t.qcow2 backing_fmt=file lazy_refcounts=off refcount_bits=16 {"return": {}} {'execute': 'blockdev-add', 'arguments': { 'node-name': 'node1', 'driver': 'file', 'filename': 'TEST_DIR/t.IMGFMT', 'locking': 'on' } } {"return": {}} diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index ac5ab16bc8..62d1ab74d3 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -9,14 +9,14 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 === Creating backing chain === { 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'disk', 'snapshot-file': 'TEST_DIR/t.IMGFMT.mid', 'format': 'IMGFMT', 'mode': 'absolute-paths' } } -Formatting 'TEST_DIR/t.qcow2.mid', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.qcow2.base backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2.mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 backing_file=TEST_DIR/t.qcow2.base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} { 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'qemu-io disk "write 0 4M"' } } wrote 4194304/4194304 bytes at offset 0 4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} { 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'disk', 'snapshot-file': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'absolute-paths' } } -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.qcow2.mid backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 backing_file=TEST_DIR/t.qcow2.mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 {"return": {}} === Start commit job and exit qemu === @@ -48,7 +48,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q { 'execute': 'qmp_capabilities' } {"return": {}} { 'execute': 'drive-mirror', 'arguments': { 'device': 'disk', 'target': 'TEST_DIR/t.IMGFMT.copy', 'format': 'IMGFMT', 'sync': 'full', 'speed': 65536 } } -Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} @@ -62,7 +62,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l { 'execute': 'qmp_capabilities' } {"return": {}} { 'execute': 'drive-backup', 'arguments': { 'device': 'disk', 'target': 'TEST_DIR/t.IMGFMT.copy', 'format': 'IMGFMT', 'sync': 'full', 'speed': 65536 } } -Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} diff --git a/tests/qemu-iotests/188 b/tests/qemu-iotests/188 index 09b9b6083a..13b225fded 100755 --- a/tests/qemu-iotests/188 +++ b/tests/qemu-iotests/188 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +_require_working_luks size=16M diff --git a/tests/qemu-iotests/188.out b/tests/qemu-iotests/188.out index c568ef3701..5426861b18 100644 --- a/tests/qemu-iotests/188.out +++ b/tests/qemu-iotests/188.out @@ -1,5 +1,5 @@ QA output created by 188 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 == reading whole image == read 16777216/16777216 bytes at offset 0 diff --git a/tests/qemu-iotests/189 b/tests/qemu-iotests/189 index c9ce9d3bed..e6a84b8a3b 100755 --- a/tests/qemu-iotests/189 +++ b/tests/qemu-iotests/189 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +_require_working_luks size=16M diff --git a/tests/qemu-iotests/189.out b/tests/qemu-iotests/189.out index a0b7c9c24c..bc213cbe14 100644 --- a/tests/qemu-iotests/189.out +++ b/tests/qemu-iotests/189.out @@ -1,6 +1,6 @@ QA output created by 189 == create base == -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 == writing whole image == wrote 16777216/16777216 bytes at offset 0 @@ -10,7 +10,7 @@ wrote 16777216/16777216 bytes at offset 0 read 16777216/16777216 bytes at offset 0 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == create overlay == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base encrypt.format=luks encrypt.key-secret=sec1 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base == writing part of a cluster == wrote 1024/1024 bytes at offset 0 diff --git a/tests/qemu-iotests/198 b/tests/qemu-iotests/198 index fb0d5a29d3..aeb059d5ea 100755 --- a/tests/qemu-iotests/198 +++ b/tests/qemu-iotests/198 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +_require_working_luks size=16M diff --git a/tests/qemu-iotests/198.out b/tests/qemu-iotests/198.out index 6280ae6eed..4b800e70db 100644 --- a/tests/qemu-iotests/198.out +++ b/tests/qemu-iotests/198.out @@ -1,12 +1,12 @@ QA output created by 198 == create base == -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 == writing whole image base == wrote 16777216/16777216 bytes at offset 0 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == create overlay == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base encrypt.format=luks encrypt.key-secret=sec1 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base == writing whole image layer == wrote 16777216/16777216 bytes at offset 0 diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 index f42432a838..11bc51f256 100755 --- a/tests/qemu-iotests/206 +++ b/tests/qemu-iotests/206 @@ -24,6 +24,7 @@ import iotests from iotests import imgfmt iotests.script_initialize(supported_fmts=['qcow2']) +iotests.verify_working_luks() with iotests.FilePath('t.qcow2') as disk_path, \ iotests.FilePath('t.qcow2.base') as backing_path, \ diff --git a/tests/qemu-iotests/255.out b/tests/qemu-iotests/255.out index a3c99fd62e..d74903db99 100644 --- a/tests/qemu-iotests/255.out +++ b/tests/qemu-iotests/255.out @@ -3,9 +3,9 @@ Finishing a commit job with background reads === Create backing chain and start VM === -Formatting 'TEST_DIR/PID-t.qcow2.mid', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-t.qcow2.mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-t.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 === Start background read requests === @@ -23,9 +23,9 @@ Closing the VM while a job is being cancelled === Create images and start VM === -Formatting 'TEST_DIR/PID-src.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-src.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-dst.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-dst.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/263 b/tests/qemu-iotests/263 index d2c030fae9..f598a12899 100755 --- a/tests/qemu-iotests/263 +++ b/tests/qemu-iotests/263 @@ -40,6 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +_require_working_luks size=1M diff --git a/tests/qemu-iotests/263.out b/tests/qemu-iotests/263.out index 0c982c55cb..54bfbeeff8 100644 --- a/tests/qemu-iotests/263.out +++ b/tests/qemu-iotests/263.out @@ -2,7 +2,7 @@ QA output created by 263 testing LUKS qcow2 encryption -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 == reading the whole image == read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -21,7 +21,7 @@ read 982528/982528 bytes at offset 66048 testing legacy AES qcow2 encryption -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=aes encrypt.key-secret=sec0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 == reading the whole image == read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out index d24ff681af..d248a1e21b 100644 --- a/tests/qemu-iotests/274.out +++ b/tests/qemu-iotests/274.out @@ -1,9 +1,9 @@ == Commit tests == -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16 wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -64,11 +64,11 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing HMP commit (top -> mid) === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16 wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -94,11 +94,11 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing QMP active commit (top -> mid) === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16 wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -131,9 +131,9 @@ read 1048576/1048576 bytes at offset 1048576 == Resize tests == === preallocation=off === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=6442450944 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=6442450944 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=1073741824 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 5368709120 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -150,9 +150,9 @@ read 65536/65536 bytes at offset 5368709120 { "start": 1073741824, "length": 7516192768, "depth": 0, "zero": true, "data": false}] === preallocation=metadata === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=34359738368 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=34359738368 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=32212254720 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=32212254720 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 33285996544 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -174,9 +174,9 @@ read 65536/65536 bytes at offset 33285996544 { "start": 34896609280, "length": 536870912, "depth": 0, "zero": true, "data": false, "offset": 2685075456}] === preallocation=falloc === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=10485760 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=10485760 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=5242880 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=5242880 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 9437184 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -193,9 +193,9 @@ read 65536/65536 bytes at offset 9437184 { "start": 5242880, "length": 10485760, "depth": 0, "zero": false, "data": true, "offset": 327680}] === preallocation=full === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=16777216 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=16777216 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=8388608 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=8388608 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 11534336 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -212,9 +212,9 @@ read 65536/65536 bytes at offset 11534336 { "start": 8388608, "length": 4194304, "depth": 0, "zero": false, "data": true, "offset": 327680}] === preallocation=off === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=393216 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=393216 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=259072 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=259072 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 259072 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -232,9 +232,9 @@ read 65536/65536 bytes at offset 259072 { "start": 262144, "length": 262144, "depth": 0, "zero": true, "data": false}] === preallocation=off === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=409600 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=409600 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=262144 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 344064 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -251,9 +251,9 @@ read 65536/65536 bytes at offset 344064 { "start": 262144, "length": 262144, "depth": 0, "zero": true, "data": false}] === preallocation=off === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=524288 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=524288 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=262144 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 446464 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out index 92e4d14079..fc59b9bc5c 100644 --- a/tests/qemu-iotests/280.out +++ b/tests/qemu-iotests/280.out @@ -1,4 +1,4 @@ -Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 === Launch VM === Enabling migration QMP events on VM... diff --git a/tests/qemu-iotests/284 b/tests/qemu-iotests/284 index 071e89b33e..9f6c29a79c 100755 --- a/tests/qemu-iotests/284 +++ b/tests/qemu-iotests/284 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +_require_working_luks size=1M diff --git a/tests/qemu-iotests/284.out b/tests/qemu-iotests/284.out index 48216f5742..a929239302 100644 --- a/tests/qemu-iotests/284.out +++ b/tests/qemu-iotests/284.out @@ -2,7 +2,7 @@ QA output created by 284 testing LUKS qcow2 encryption -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 == cluster size 512 == checking image refcounts == @@ -21,7 +21,7 @@ wrote 1/1 bytes at offset 512 == rechecking image refcounts == No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 == cluster size 2048 == checking image refcounts == @@ -40,7 +40,7 @@ wrote 1/1 bytes at offset 2048 == rechecking image refcounts == No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 == cluster size 32768 == checking image refcounts == diff --git a/tests/qemu-iotests/293 b/tests/qemu-iotests/293 new file mode 100755 index 0000000000..f86fe3b413 --- /dev/null +++ b/tests/qemu-iotests/293 @@ -0,0 +1,208 @@ +#!/usr/bin/env bash +# +# Test encryption key management with luks +# Based on 134 +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mlevitsk@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 luks +_supported_proto file #TODO +_require_working_luks + +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT + +if [ "$IMGFMT" = "qcow2" ] ; then + PR="encrypt." + EXTRA_IMG_ARGS="-o encrypt.format=luks" +fi + + +# secrets: you are supposed to see the password as *******, see :-) +S0="--object secret,id=sec0,data=hunter0" +S1="--object secret,id=sec1,data=hunter1" +S2="--object secret,id=sec2,data=hunter2" +S3="--object secret,id=sec3,data=hunter3" +S4="--object secret,id=sec4,data=hunter4" +SECRETS="$S0 $S1 $S2 $S3 $S4" + +# image with given secret +IMGS0="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec0" +IMGS1="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec1" +IMGS2="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec2" +IMGS3="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec3" +IMGS4="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec4" + + +echo "== creating a test image ==" +_make_test_img $S0 $EXTRA_IMG_ARGS -o ${PR}key-secret=sec0,${PR}iter-time=10 32M + +echo +echo "== test that key 0 opens the image ==" +$QEMU_IO $S0 -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir + +echo +echo "== adding a password to slot 4 ==" +$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=active,${PR}new-secret=sec4,${PR}iter-time=10,${PR}keyslot=4 +echo "== adding a password to slot 1 ==" +$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=active,${PR}new-secret=sec1,${PR}iter-time=10 +echo "== adding a password to slot 3 ==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=active,${PR}new-secret=sec3,${PR}iter-time=10,${PR}keyslot=3 + +echo "== adding a password to slot 2 ==" +$QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec2,${PR}iter-time=10 + + +echo "== erase slot 4 ==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=4 | _filter_img_create + + +echo +echo "== all secrets should work ==" +for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do + $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir +done + +echo +echo "== erase slot 0 and try it ==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec0 | _filter_img_create +$QEMU_IO $SECRETS -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir + +echo +echo "== erase slot 2 and try it ==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=2 | _filter_img_create +$QEMU_IO $SECRETS -c "read 0 4096" $IMGS2 | _filter_qemu_io | _filter_testdir + + +# at this point slots 1 and 3 should be active + +echo +echo "== filling 4 slots with secret 2 ==" +for ((i = 0; i < 4; i++)); do + $QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec2,${PR}iter-time=10 +done + +echo +echo "== adding secret 0 ==" + $QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec0,${PR}iter-time=10 + +echo +echo "== adding secret 3 (last slot) ==" + $QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec3,${PR}iter-time=10 + +echo +echo "== trying to add another slot (should fail) ==" +$QEMU_IMG amend $SECRETS $IMGS2 -o ${PR}state=active,${PR}new-secret=sec3,${PR}iter-time=10 + +echo +echo "== all secrets should work again ==" +for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do + $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir +done + + +echo + +echo "== erase all keys of secret 2==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec2 + +echo "== erase all keys of secret 1==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec1 + +echo "== erase all keys of secret 0==" +$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=inactive,${PR}old-secret=sec0 + +echo "== erasing secret3 will fail now since it is the only secret (in 3 slots) ==" +$QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=inactive,${PR}old-secret=sec3 + +echo +echo "== only secret3 should work now ==" +for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do + $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir +done + +echo +echo "== add secret0 ==" +$QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec0,${PR}iter-time=10 + +echo "== erase secret3 ==" +$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=inactive,${PR}old-secret=sec3 + +echo +echo "== only secret0 should work now ==" +for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do + $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir +done + +echo +echo "== replace secret0 with secret1 (should fail) ==" +$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=active,${PR}new-secret=sec1,${PR}keyslot=0 + +echo +echo "== replace secret0 with secret1 with force (should work) ==" +$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=active,${PR}new-secret=sec1,${PR}iter-time=10,${PR}keyslot=0 --force + +echo +echo "== only secret1 should work now ==" +for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do + $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir +done + + +echo +echo "== erase last secret (should fail) ==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=0 +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec1 + + +echo "== erase non existing secrets (should fail) ==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec5 --force +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec0 --force +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=1 --force + +echo +echo "== erase last secret with force by slot (should work) ==" +$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=0 --force + +echo +echo "== we have no secrets now, data is lost forever ==" +for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do + $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 + diff --git a/tests/qemu-iotests/293.out b/tests/qemu-iotests/293.out new file mode 100644 index 0000000000..7260783126 --- /dev/null +++ b/tests/qemu-iotests/293.out @@ -0,0 +1,99 @@ +QA output created by 293 +== creating a test image == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 + +== test that key 0 opens the image == +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== adding a password to slot 4 == +== adding a password to slot 1 == +== adding a password to slot 3 == +== adding a password to slot 2 == +== erase slot 4 == + +== all secrets should work == +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== erase slot 0 and try it == +qemu-io: can't open: Invalid password, cannot unlock any keyslot + +== erase slot 2 and try it == +qemu-io: can't open: Invalid password, cannot unlock any keyslot + +== filling 4 slots with secret 2 == + +== adding secret 0 == + +== adding secret 3 (last slot) == + +== trying to add another slot (should fail) == +qemu-img: Can't add a keyslot - all keyslots are in use + +== all secrets should work again == +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== erase all keys of secret 2== +== erase all keys of secret 1== +== erase all keys of secret 0== +== erasing secret3 will fail now since it is the only secret (in 3 slots) == +qemu-img: All the active keyslots match the (old) password that was given and erasing them will erase all the data in the image irreversibly - refusing operation + +== only secret3 should work now == +qemu-io: can't open: Invalid password, cannot unlock any keyslot +qemu-io: can't open: Invalid password, cannot unlock any keyslot +qemu-io: can't open: Invalid password, cannot unlock any keyslot +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== add secret0 == +== erase secret3 == + +== only secret0 should work now == +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io: can't open: Invalid password, cannot unlock any keyslot +qemu-io: can't open: Invalid password, cannot unlock any keyslot +qemu-io: can't open: Invalid password, cannot unlock any keyslot + +== replace secret0 with secret1 (should fail) == +qemu-img: Refusing to overwrite active keyslot 0 - please erase it first + +== replace secret0 with secret1 with force (should work) == + +== only secret1 should work now == +qemu-io: can't open: Invalid password, cannot unlock any keyslot +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io: can't open: Invalid password, cannot unlock any keyslot +qemu-io: can't open: Invalid password, cannot unlock any keyslot + +== erase last secret (should fail) == +qemu-img: Attempt to erase the only active keyslot 0 which will erase all the data in the image irreversibly - refusing operation +qemu-img: All the active keyslots match the (old) password that was given and erasing them will erase all the data in the image irreversibly - refusing operation +== erase non existing secrets (should fail) == +qemu-img: No secret with id 'sec5' +qemu-img: No keyslots match given (old) password for erase operation + +== erase last secret with force by slot (should work) == + +== we have no secrets now, data is lost forever == +qemu-io: can't open: Invalid password, cannot unlock any keyslot +qemu-io: can't open: Invalid password, cannot unlock any keyslot +qemu-io: can't open: Invalid password, cannot unlock any keyslot +qemu-io: can't open: Invalid password, cannot unlock any keyslot +*** done diff --git a/tests/qemu-iotests/294 b/tests/qemu-iotests/294 new file mode 100755 index 0000000000..9c95ed8c9a --- /dev/null +++ b/tests/qemu-iotests/294 @@ -0,0 +1,90 @@ +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mlevitsk@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt luks +_supported_proto file #TODO + +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT + +# you are supposed to see the password as *******, see :-) +S0="--object secret,id=sec0,data=hunter0" +S1="--object secret,id=sec1,data=hunter1" +SECRETS="$S0 $S1" + + +IMGS0="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,key-secret=sec0" +IMGS1="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,key-secret=sec1" + +echo "== creating a test image ==" +_make_test_img $S0 -o "key-secret=sec0,iter-time=10" 32M + +echo +echo "== test that key 0 opens the image ==" +$QEMU_IO $S0 -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir + +echo +echo "== adding a password to slot 1 ==" +$QEMU_IMG amend $SECRETS $IMGS0 -o state=active,new-secret=sec1,keyslot=1,iter-time=10 + +echo +echo "== 'backup' the image header ==" +dd if=$TEST_IMG_FILE of=${TEST_IMG_FILE}.bk bs=4K skip=0 count=1 + +echo +echo "== erase slot 0 ==" +$QEMU_IMG amend $SECRETS $IMGS1 -o state=inactive,keyslot=0 | _filter_img_create + +echo +echo "== test that key 0 doesn't open the image ==" +$QEMU_IO $S0 -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir + +echo +echo "== 'restore' the image header ==" +dd if=${TEST_IMG_FILE}.bk of=${TEST_IMG_FILE} bs=4K skip=0 count=1 conv=notrunc + +echo +echo "== test that key 0 still doesn't open the image (key material is erased) ==" +$QEMU_IO $SECRETS -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir + +echo +echo "== test that key 1 still works ==" +$QEMU_IO $SECRETS -c "read 0 4096" $IMGS1 | _filter_qemu_io | _filter_testdir + +echo "*** done" +rm -f $seq.full +status=0 + + +exit 0 diff --git a/tests/qemu-iotests/294.out b/tests/qemu-iotests/294.out new file mode 100644 index 0000000000..994ae87308 --- /dev/null +++ b/tests/qemu-iotests/294.out @@ -0,0 +1,30 @@ +QA output created by 294 +== creating a test image == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 + +== test that key 0 opens the image == +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== adding a password to slot 1 == + +== 'backup' the image header == +1+0 records in +1+0 records out + +== erase slot 0 == + +== test that key 0 doesn't open the image == +qemu-io: can't open: Invalid password, cannot unlock any keyslot + +== 'restore' the image header == +1+0 records in +1+0 records out + +== test that key 0 still doesn't open the image (key material is erased) == +qemu-io: can't open: Invalid password, cannot unlock any keyslot + +== test that key 1 still works == +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/295 b/tests/qemu-iotests/295 new file mode 100755 index 0000000000..59e674fa85 --- /dev/null +++ b/tests/qemu-iotests/295 @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 +# +# Test case QMP's encrypted key management +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import iotests +import os +import time +import json + +test_img = os.path.join(iotests.test_dir, 'test.img') + +class Secret: + def __init__(self, index): + self._id = "keysec" + str(index) + # you are not supposed to see the password... + self._secret = "hunter" + str(index) + + def id(self): + return self._id + + def secret(self): + return self._secret + + def to_cmdline_object(self): + return [ "secret,id=" + self._id + ",data=" + self._secret] + + def to_qmp_object(self): + return { "qom_type" : "secret", "id": self.id(), + "props": { "data": self.secret() } } + +################################################################################ +class EncryptionSetupTestCase(iotests.QMPTestCase): + + # test case startup + def setUp(self): + # start the VM + self.vm = iotests.VM() + self.vm.launch() + + # create the secrets and load 'em into the VM + self.secrets = [ Secret(i) for i in range(0, 6) ] + for secret in self.secrets: + result = self.vm.qmp("object-add", **secret.to_qmp_object()) + self.assert_qmp(result, 'return', {}) + + if iotests.imgfmt == "qcow2": + self.pfx = "encrypt." + self.img_opts = [ '-o', "encrypt.format=luks" ] + else: + self.pfx = "" + self.img_opts = [] + + # test case shutdown + def tearDown(self): + # stop the VM + self.vm.shutdown() + + ########################################################################### + # create the encrypted block device + def createImg(self, file, secret): + + iotests.qemu_img( + 'create', + '--object', *secret.to_cmdline_object(), + '-f', iotests.imgfmt, + '-o', self.pfx + 'key-secret=' + secret.id(), + '-o', self.pfx + 'iter-time=10', + *self.img_opts, + file, + '1M') + + ########################################################################### + # open an encrypted block device + def openImageQmp(self, id, file, secret, read_only = False): + + encrypt_options = { + 'key-secret' : secret.id() + } + + if iotests.imgfmt == "qcow2": + encrypt_options = { + 'encrypt': { + 'format':'luks', + **encrypt_options + } + } + + result = self.vm.qmp('blockdev-add', ** + { + 'driver': iotests.imgfmt, + 'node-name': id, + 'read-only': read_only, + + **encrypt_options, + + 'file': { + 'driver': 'file', + 'filename': test_img, + } + } + ) + self.assert_qmp(result, 'return', {}) + + # close the encrypted block device + def closeImageQmp(self, id): + result = self.vm.qmp('blockdev-del', **{ 'node-name': id }) + self.assert_qmp(result, 'return', {}) + + ########################################################################### + # add a key to an encrypted block device + def addKeyQmp(self, id, new_secret, secret = None, + slot = None, force = False): + + crypt_options = { + 'state' : 'active', + 'new-secret' : new_secret.id(), + 'iter-time' : 10 + } + + if slot != None: + crypt_options['keyslot'] = slot + + + if secret != None: + crypt_options['secret'] = secret.id() + + if iotests.imgfmt == "qcow2": + crypt_options['format'] = 'luks' + crypt_options = { + 'encrypt': crypt_options + } + + args = { + 'node-name': id, + 'job-id' : 'job_add_key', + 'options' : { + 'driver' : iotests.imgfmt, + **crypt_options + }, + } + + if force == True: + args['force'] = True + + #TODO: check what jobs return + result = self.vm.qmp('x-blockdev-amend', **args) + assert result['return'] == {} + self.vm.run_job('job_add_key') + + # erase a key from an encrypted block device + def eraseKeyQmp(self, id, old_secret = None, slot = None, force = False): + + crypt_options = { + 'state' : 'inactive', + } + + if slot != None: + crypt_options['keyslot'] = slot + if old_secret != None: + crypt_options['old-secret'] = old_secret.id() + + if iotests.imgfmt == "qcow2": + crypt_options['format'] = 'luks' + crypt_options = { + 'encrypt': crypt_options + } + + args = { + 'node-name': id, + 'job-id' : 'job_erase_key', + 'options' : { + 'driver' : iotests.imgfmt, + **crypt_options + }, + } + + if force == True: + args['force'] = True + + result = self.vm.qmp('x-blockdev-amend', **args) + assert result['return'] == {} + self.vm.run_job('job_erase_key') + + ########################################################################### + # create image, and change its key + def testChangeKey(self): + + # create the image with secret0 and open it + self.createImg(test_img, self.secrets[0]); + self.openImageQmp("testdev", test_img, self.secrets[0]) + + # add key to slot 1 + self.addKeyQmp("testdev", new_secret = self.secrets[1]) + + # add key to slot 5 + self.addKeyQmp("testdev", new_secret = self.secrets[2], slot=5) + + # erase key from slot 0 + self.eraseKeyQmp("testdev", old_secret = self.secrets[0]) + + #reopen the image with secret1 + self.closeImageQmp("testdev") + self.openImageQmp("testdev", test_img, self.secrets[1]) + + # close and erase the image for good + self.closeImageQmp("testdev") + os.remove(test_img) + + # test that if we erase the old password, + # we can still change the encryption keys using 'old-secret' + def testOldPassword(self): + + # create the image with secret0 and open it + self.createImg(test_img, self.secrets[0]); + self.openImageQmp("testdev", test_img, self.secrets[0]) + + # add key to slot 1 + self.addKeyQmp("testdev", new_secret = self.secrets[1]) + + # erase key from slot 0 + self.eraseKeyQmp("testdev", old_secret = self.secrets[0]) + + # this will fail as the old password is no longer valid + self.addKeyQmp("testdev", new_secret = self.secrets[2]) + + # this will work + self.addKeyQmp("testdev", new_secret = self.secrets[2], secret = self.secrets[1]) + + # close and erase the image for good + self.closeImageQmp("testdev") + os.remove(test_img) + + def testUseForceLuke(self): + + self.createImg(test_img, self.secrets[0]); + self.openImageQmp("testdev", test_img, self.secrets[0]) + + # Add bunch of secrets + self.addKeyQmp("testdev", new_secret = self.secrets[1], slot=4) + self.addKeyQmp("testdev", new_secret = self.secrets[4], slot=2) + + # overwrite an active secret + self.addKeyQmp("testdev", new_secret = self.secrets[5], slot=2) + self.addKeyQmp("testdev", new_secret = self.secrets[5], slot=2, force=True) + + self.addKeyQmp("testdev", new_secret = self.secrets[0]) + + # Now erase all the secrets + self.eraseKeyQmp("testdev", old_secret = self.secrets[5]) + self.eraseKeyQmp("testdev", slot=4) + + # erase last keyslot + self.eraseKeyQmp("testdev", old_secret = self.secrets[0]) + self.eraseKeyQmp("testdev", old_secret = self.secrets[0], force=True) + + self.closeImageQmp("testdev") + os.remove(test_img) + + +if __name__ == '__main__': + iotests.verify_working_luks() + # Encrypted formats support + iotests.activate_logging() + iotests.main(supported_fmts = ['qcow2', 'luks']) diff --git a/tests/qemu-iotests/295.out b/tests/qemu-iotests/295.out new file mode 100644 index 0000000000..ad34b2ca2c --- /dev/null +++ b/tests/qemu-iotests/295.out @@ -0,0 +1,40 @@ +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} +{"return": {}} +Job failed: Invalid password, cannot unlock any keyslot +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +Job failed: Refusing to overwrite active keyslot 2 - please erase it first +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} +{"return": {}} +Job failed: All the active keyslots match the (old) password that was given and erasing them will erase all the data in the image irreversibly - refusing operation +{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} +{"return": {}} +... +---------------------------------------------------------------------- +Ran 3 tests + +OK diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296 new file mode 100755 index 0000000000..ec69ec8974 --- /dev/null +++ b/tests/qemu-iotests/296 @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +# +# Test case for encryption key management versus image sharing +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import iotests +import os +import time +import json + +test_img = os.path.join(iotests.test_dir, 'test.img') + +class Secret: + def __init__(self, index): + self._id = "keysec" + str(index) + # you are not supposed to see the password... + self._secret = "hunter" + str(index) + + def id(self): + return self._id + + def secret(self): + return self._secret + + def to_cmdline_object(self): + return [ "secret,id=" + self._id + ",data=" + self._secret] + + def to_qmp_object(self): + return { "qom_type" : "secret", "id": self.id(), + "props": { "data": self.secret() } } + +################################################################################ + +class EncryptionSetupTestCase(iotests.QMPTestCase): + + # test case startup + def setUp(self): + + # start the VMs + self.vm1 = iotests.VM(path_suffix = 'VM1') + self.vm2 = iotests.VM(path_suffix = 'VM2') + self.vm1.launch() + self.vm2.launch() + + # create the secrets and load 'em into the VMs + self.secrets = [ Secret(i) for i in range(0, 4) ] + for secret in self.secrets: + result = self.vm1.qmp("object-add", **secret.to_qmp_object()) + self.assert_qmp(result, 'return', {}) + result = self.vm2.qmp("object-add", **secret.to_qmp_object()) + self.assert_qmp(result, 'return', {}) + + # test case shutdown + def tearDown(self): + # stop the VM + self.vm1.shutdown() + self.vm2.shutdown() + + ########################################################################### + # create the encrypted block device using qemu-img + def createImg(self, file, secret): + + output = iotests.qemu_img_pipe( + 'create', + '--object', *secret.to_cmdline_object(), + '-f', iotests.imgfmt, + '-o', 'key-secret=' + secret.id(), + '-o', 'iter-time=10', + file, + '1M') + + iotests.log(output, filters=[iotests.filter_test_dir]) + + # attempts to add a key using qemu-img + def addKey(self, file, secret, new_secret): + + image_options = { + 'key-secret' : secret.id(), + 'driver' : iotests.imgfmt, + 'file' : { + 'driver':'file', + 'filename': file, + } + } + + output = iotests.qemu_img_pipe( + 'amend', + '--object', *secret.to_cmdline_object(), + '--object', *new_secret.to_cmdline_object(), + + '-o', 'state=active', + '-o', 'new-secret=' + new_secret.id(), + '-o', 'iter-time=10', + + "json:" + json.dumps(image_options) + ) + + iotests.log(output, filters=[iotests.filter_test_dir]) + + ########################################################################### + # open an encrypted block device + def openImageQmp(self, vm, id, file, secret, + readOnly = False, reOpen = False): + + command = 'x-blockdev-reopen' if reOpen else 'blockdev-add' + + result = vm.qmp(command, ** + { + 'driver': iotests.imgfmt, + 'node-name': id, + 'read-only': readOnly, + 'key-secret' : secret.id(), + 'file': { + 'driver': 'file', + 'filename': test_img, + } + } + ) + self.assert_qmp(result, 'return', {}) + + # close the encrypted block device + def closeImageQmp(self, vm, id): + result = vm.qmp('blockdev-del', **{ 'node-name': id }) + self.assert_qmp(result, 'return', {}) + + ########################################################################### + + # add a key to an encrypted block device + def addKeyQmp(self, vm, id, new_secret): + + args = { + 'node-name': id, + 'job-id' : 'job0', + 'options' : { + 'state' : 'active', + 'driver' : iotests.imgfmt, + 'new-secret': new_secret.id(), + 'iter-time' : 10 + }, + } + + result = vm.qmp('x-blockdev-amend', **args) + assert result['return'] == {} + vm.run_job('job0') + + # test that when the image opened by two qemu processes, + # neither of them can update the image + def test1(self): + self.createImg(test_img, self.secrets[0]); + + # VM1 opens the image and adds a key + self.openImageQmp(self.vm1, "testdev", test_img, self.secrets[0]) + self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[1]) + + + # VM2 opens the image + self.openImageQmp(self.vm2, "testdev", test_img, self.secrets[0]) + + + # neither VMs now should be able to add a key + self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[2]) + self.addKeyQmp(self.vm2, "testdev", new_secret = self.secrets[2]) + + + # VM 1 closes the image + self.closeImageQmp(self.vm1, "testdev") + + + # now VM2 can add the key + self.addKeyQmp(self.vm2, "testdev", new_secret = self.secrets[2]) + + + # qemu-img should also not be able to add a key + self.addKey(test_img, self.secrets[0], self.secrets[2]) + + # cleanup + self.closeImageQmp(self.vm2, "testdev") + os.remove(test_img) + + + def test2(self): + self.createImg(test_img, self.secrets[0]); + + # VM1 opens the image readonly + self.openImageQmp(self.vm1, "testdev", test_img, self.secrets[0], + readOnly = True) + + # VM2 opens the image + self.openImageQmp(self.vm2, "testdev", test_img, self.secrets[0]) + + # VM1 can't add a key since image is readonly + self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[2]) + + # VM2 can't add a key since VM is has the image opened + self.addKeyQmp(self.vm2, "testdev", new_secret = self.secrets[2]) + + + #VM1 reopens the image read-write + self.openImageQmp(self.vm1, "testdev", test_img, self.secrets[0], + reOpen = True, readOnly = False) + + # VM1 still can't add the key + self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[2]) + + # VM2 gets away + self.closeImageQmp(self.vm2, "testdev") + + # VM1 now can add the key + self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[2]) + + self.closeImageQmp(self.vm1, "testdev") + os.remove(test_img) + + +if __name__ == '__main__': + # support only raw luks since luks encrypted qcow2 is a proper + # format driver which doesn't allow any sharing + iotests.activate_logging() + iotests.main(supported_fmts = ['luks']) diff --git a/tests/qemu-iotests/296.out b/tests/qemu-iotests/296.out new file mode 100644 index 0000000000..afb6d2d09d --- /dev/null +++ b/tests/qemu-iotests/296.out @@ -0,0 +1,33 @@ +Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 + +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +Job failed: Failed to get shared "consistent read" lock +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +Job failed: Failed to get shared "consistent read" lock +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +qemu-img: Failed to get shared "consistent read" lock +Is another process using the image [TEST_DIR/test.img]? + +Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 + +Job failed: Block node is read-only +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +Job failed: Failed to get shared "consistent read" lock +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +Job failed: Failed to get shared "consistent read" lock +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 03e4f71808..d967adc59a 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -122,38 +122,96 @@ _filter_actual_image_size() # replace driver-specific options in the "Formatting..." line _filter_img_create() { - data_file_filter=() - if data_file=$(_get_data_file "$TEST_IMG"); then - data_file_filter=(-e "s# data_file=$data_file##") + # Split the line into the pre-options part ($filename_part, which + # precedes ", fmt=") and the options part ($options, which starts + # with "fmt=") + # (And just echo everything before the first "^Formatting") + readarray formatting_line < <($SED -e 's/, fmt=/\n/') + + filename_part='' + options='' + lines=${#formatting_line[@]} + for ((i = 0; i < $lines; i++)); do + line=${formatting_line[i]} + unset formatting_line[i] + + filename_part="$filename_part$line" + + if echo "$line" | grep -q '^Formatting'; then + next_i=$((i + 1)) + if [ -n "${formatting_line[next_i]}" ]; then + options="fmt=${formatting_line[@]}" + fi + break + fi + done + + # Set grep_data_file to '\|data_file' to keep it; make it empty + # to drop it. + # We want to drop it if it is part of the global $IMGOPTS, and we + # want to keep it otherwise (if the test specifically wants to + # test data files). + grep_data_file=(-e data_file) + if _get_data_file "$TEST_IMG" > /dev/null; then + grep_data_file=() fi - $SED "${data_file_filter[@]}" \ + filename_filters=( -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd:127.0.0.1:[0-9]\\+#TEST_DIR/t.IMGFMT#g' \ - -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' \ - -e "s# encryption=off##g" \ - -e "s# cluster_size=[0-9]\\+##g" \ - -e "s# table_size=[0-9]\\+##g" \ - -e "s# compat=[^ ]*##g" \ - -e "s# compat6=\\(on\\|off\\)##g" \ - -e "s# static=\\(on\\|off\\)##g" \ - -e "s# zeroed_grain=\\(on\\|off\\)##g" \ - -e "s# subformat=[^ ]*##g" \ - -e "s# adapter_type=[^ ]*##g" \ - -e "s# hwversion=[^ ]*##g" \ - -e "s# lazy_refcounts=\\(on\\|off\\)##g" \ - -e "s# block_size=[0-9]\\+##g" \ - -e "s# block_state_zero=\\(on\\|off\\)##g" \ - -e "s# log_size=[0-9]\\+##g" \ - -e "s# refcount_bits=[0-9]\\+##g" \ - -e "s# key-secret=[a-zA-Z0-9]\\+##g" \ - -e "s# iter-time=[0-9]\\+##g" \ - -e "s# force_size=\\(on\\|off\\)##g" \ - -e "s# compression_type=[a-zA-Z0-9]\\+##g" + -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' + ) + + filename_part=$(echo "$filename_part" | $SED "${filename_filters[@]}") + + # Break the option line before each option (preserving pre-existing + # line breaks by replacing them by \0 and restoring them at the end), + # then filter out the options we want to keep and sort them according + # to some order that all block drivers used at the time of writing + # this function. + options=$( + echo "$options" \ + | tr '\n' '\0' \ + | $SED -e 's/ \([a-z0-9_.-]*\)=/\n\1=/g' \ + | grep -a -e '^fmt' -e '^size' -e '^backing' -e '^preallocation' \ + -e '^encryption' "${grep_data_file[@]}" \ + | $SED "${filename_filters[@]}" \ + -e 's/^\(fmt\)/0-\1/' \ + -e 's/^\(size\)/1-\1/' \ + -e 's/^\(backing\)/2-\1/' \ + -e 's/^\(data_file\)/3-\1/' \ + -e 's/^\(encryption\)/4-\1/' \ + -e 's/^\(preallocation\)/8-\1/' \ + | sort \ + | $SED -e 's/^[0-9]-//' \ + | tr '\n\0' ' \n' \ + | $SED -e 's/^ *$//' -e 's/ *$//' + ) + + if [ -n "$options" ]; then + echo "$filename_part, $options" + elif [ -n "$filename_part" ]; then + echo "$filename_part" + fi +} + +# Filter the "Formatting..." line in QMP output (leaving the QMP output +# untouched) +# (In contrast to _filter_img_create(), this function does not support +# multi-line Formatting output) +_filter_img_create_in_qmp() +{ + while read -r line; do + if echo "$line" | grep -q '^Formatting'; then + echo "$line" | _filter_img_create + else + echo "$line" + fi + done } _filter_img_create_size() diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index ba912555ca..7ac46edc1f 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -605,6 +605,9 @@ _supported_fmt() # setting IMGFMT_GENERIC to false. for f; do if [ "$f" = "$IMGFMT" -o "$f" = "generic" -a "$IMGFMT_GENERIC" = "true" ]; then + if [ "$IMGFMT" = "luks" ]; then + _require_working_luks + fi return fi done @@ -740,6 +743,33 @@ _unsupported_imgopts() done } +# Caution: Overwrites $TEST_DIR/t.luks +_require_working_luks() +{ + file="$TEST_DIR/t.luks" + + output=$( + $QEMU_IMG create -f luks \ + --object secret,id=sec0,data=hunter0 \ + -o key-secret=sec0 \ + -o iter-time=10 \ + "$file" \ + 1M \ + 2>&1 + ) + status=$? + + IMGFMT='luks' _rm_test_img "$file" + + if [ $status != 0 ]; then + reason=$(echo "$output" | grep "$file:" | $SED -e "s#.*$file: *##") + if [ -z "$reason" ]; then + reason="Failed to create a LUKS image" + fi + _notrun "$reason" + fi +} + # this test requires that a specified command (executable) exists # _require_command() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index d886fa0cb3..9b07a7ed03 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -301,4 +301,8 @@ 290 rw auto quick 291 rw quick 292 rw auto quick +293 rw +294 rw quick +295 rw +296 rw 297 meta diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 5ea4c4df8b..f1e0733dda 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -29,7 +29,7 @@ import struct import subprocess import sys from typing import (Any, Callable, Dict, Iterable, - List, Optional, Sequence, TypeVar) + List, Optional, Sequence, Tuple, TypeVar) import unittest # pylint: disable=import-error, wrong-import-position @@ -90,15 +90,24 @@ luks_default_secret_object = 'secret,id=keysec0,data=' + \ luks_default_key_secret_opt = 'key-secret=keysec0' -def qemu_img(*args): - '''Run qemu-img and return the exit code''' - devnull = open('/dev/null', 'r+') - exitcode = subprocess.call(qemu_img_args + list(args), - stdin=devnull, stdout=devnull) - if exitcode < 0: +def qemu_img_pipe_and_status(*args: str) -> Tuple[str, int]: + """ + Run qemu-img and return both its output and its exit code + """ + subp = subprocess.Popen(qemu_img_args + list(args), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + output = subp.communicate()[0] + if subp.returncode < 0: sys.stderr.write('qemu-img received signal %i: %s\n' - % (-exitcode, ' '.join(qemu_img_args + list(args)))) - return exitcode + % (-subp.returncode, + ' '.join(qemu_img_args + list(args)))) + return (output, subp.returncode) + +def qemu_img(*args: str) -> int: + '''Run qemu-img and return the exit code''' + return qemu_img_pipe_and_status(*args)[1] def ordered_qmp(qmsg, conv_keys=True): # Dictionaries are not ordered prior to 3.6, therefore: @@ -140,17 +149,9 @@ def qemu_img_verbose(*args): % (-exitcode, ' '.join(qemu_img_args + list(args)))) return exitcode -def qemu_img_pipe(*args): +def qemu_img_pipe(*args: str) -> str: '''Run qemu-img and return its output''' - subp = subprocess.Popen(qemu_img_args + list(args), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - exitcode = subp.wait() - if exitcode < 0: - sys.stderr.write('qemu-img received signal %i: %s\n' - % (-exitcode, ' '.join(qemu_img_args + list(args)))) - return subp.communicate()[0] + return qemu_img_pipe_and_status(*args)[0] def qemu_img_log(*args): result = qemu_img_pipe(*args) @@ -177,11 +178,11 @@ def qemu_io(*args): subp = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - exitcode = subp.wait() - if exitcode < 0: + output = subp.communicate()[0] + if subp.returncode < 0: sys.stderr.write('qemu-io received signal %i: %s\n' - % (-exitcode, ' '.join(args))) - return subp.communicate()[0] + % (-subp.returncode, ' '.join(args))) + return output def qemu_io_log(*args): result = qemu_io(*args) @@ -257,15 +258,14 @@ def qemu_nbd_early_pipe(*args): and its output in case of an error''' subp = subprocess.Popen(qemu_nbd_args + ['--fork'] + list(args), stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, universal_newlines=True) - exitcode = subp.wait() - if exitcode < 0: + output = subp.communicate()[0] + if subp.returncode < 0: sys.stderr.write('qemu-nbd received signal %i: %s\n' % - (-exitcode, + (-subp.returncode, ' '.join(qemu_nbd_args + ['--fork'] + list(args)))) - return exitcode, subp.communicate()[0] if exitcode else '' + return subp.returncode, output if subp.returncode else '' def qemu_nbd_popen(*args): '''Run qemu-nbd in daemon mode and return the parent's exit code''' @@ -1010,12 +1010,17 @@ def _verify_image_format(supported_fmts: Sequence[str] = (), # similar to # _supported_fmt generic # for bash tests + if imgfmt == 'luks': + verify_working_luks() return not_sup = supported_fmts and (imgfmt not in supported_fmts) if not_sup or (imgfmt in unsupported_fmts): notrun('not suitable for this image format: %s' % imgfmt) + if imgfmt == 'luks': + verify_working_luks() + def _verify_protocol(supported: Sequence[str] = (), unsupported: Sequence[str] = ()) -> None: assert not (supported and unsupported) @@ -1052,6 +1057,45 @@ def verify_quorum(): if not supports_quorum(): notrun('quorum support missing') +def has_working_luks() -> Tuple[bool, str]: + """ + Check whether our LUKS driver can actually create images + (this extends to LUKS encryption for qcow2). + + If not, return the reason why. + """ + + img_file = f'{test_dir}/luks-test.luks' + (output, status) = \ + qemu_img_pipe_and_status('create', '-f', 'luks', + '--object', luks_default_secret_object, + '-o', luks_default_key_secret_opt, + '-o', 'iter-time=10', + img_file, '1G') + try: + os.remove(img_file) + except OSError: + pass + + if status != 0: + reason = output + for line in output.splitlines(): + if img_file + ':' in line: + reason = line.split(img_file + ':', 1)[1].strip() + break + + return (False, reason) + else: + return (True, '') + +def verify_working_luks(): + """ + Skip test suite if LUKS does not work + """ + (working, reason) = has_working_luks() + if not working: + notrun(reason) + def qemu_pipe(*args): """ Run qemu with an option to print something and exit (e.g. a help option). @@ -1062,11 +1106,11 @@ def qemu_pipe(*args): subp = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - exitcode = subp.wait() - if exitcode < 0: + output = subp.communicate()[0] + if subp.returncode < 0: sys.stderr.write('qemu received signal %i: %s\n' % - (-exitcode, ' '.join(args))) - return subp.communicate()[0] + (-subp.returncode, ' '.join(args))) + return output def supported_formats(read_only=False): '''Set 'read_only' to True to check ro-whitelist diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 8992f1f12b..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,19 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/pc/DSDT", -"tests/data/acpi/pc/DSDT.acpihmat", -"tests/data/acpi/pc/DSDT.bridge", -"tests/data/acpi/pc/DSDT.cphp", -"tests/data/acpi/pc/DSDT.dimmpxm", -"tests/data/acpi/pc/DSDT.ipmikcs", -"tests/data/acpi/pc/DSDT.memhp", -"tests/data/acpi/pc/DSDT.numamem", -"tests/data/acpi/q35/DSDT", -"tests/data/acpi/q35/DSDT.acpihmat", -"tests/data/acpi/q35/DSDT.bridge", -"tests/data/acpi/q35/DSDT.cphp", -"tests/data/acpi/q35/DSDT.dimmpxm", -"tests/data/acpi/q35/DSDT.ipmibt", -"tests/data/acpi/q35/DSDT.memhp", -"tests/data/acpi/q35/DSDT.mmio64", -"tests/data/acpi/q35/DSDT.numamem", -"tests/data/acpi/q35/DSDT.tis", diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c index f2c1576cae..9abb5ec889 100644 --- a/tests/qtest/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -287,11 +287,6 @@ static void add_machine_test_case(const char *mname) { char *path, *args; - /* Ignore blacklisted machines */ - if (!memcmp("xenfv", mname, 5) || g_str_equal("xenpv", mname)) { - return; - } - path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); args = g_strdup_printf("-M %s", mname); qtest_add_data_func(path, args, test_device_intro_concrete); diff --git a/tests/qtest/fuzz/fork_fuzz.c b/tests/qtest/fuzz/fork_fuzz.c index 2bd0851903..6ffb2a7937 100644 --- a/tests/qtest/fuzz/fork_fuzz.c +++ b/tests/qtest/fuzz/fork_fuzz.c @@ -17,39 +17,25 @@ void counter_shm_init(void) { - char *shm_path = g_strdup_printf("/qemu-fuzz-cntrs.%d", getpid()); - int fd = shm_open(shm_path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); - g_free(shm_path); - - if (fd == -1) { - perror("Error: "); - exit(1); - } - if (ftruncate(fd, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START) == -1) { - perror("Error: "); - exit(1); - } - /* Copy what's in the counter region to the shm.. */ - void *rptr = mmap(NULL , - &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - memcpy(rptr, + /* Copy what's in the counter region to a temporary buffer.. */ + void *copy = malloc(&__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); + memcpy(copy, &__FUZZ_COUNTERS_START, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); - munmap(rptr, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); - - /* And map the shm over the counter region */ - rptr = mmap(&__FUZZ_COUNTERS_START, - &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); - - close(fd); - - if (!rptr) { + /* Map a shared region over the counter region */ + if (mmap(&__FUZZ_COUNTERS_START, + &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, + 0, 0) == MAP_FAILED) { perror("Error: "); exit(1); } + + /* Copy the original data back to the counter-region */ + memcpy(&__FUZZ_COUNTERS_START, copy, + &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); + free(copy); } diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index a44fe479db..a36d9038e0 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -211,5 +211,8 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) qemu_init(result.we_wordc, result.we_wordv, NULL); + /* re-enable the rcu atfork, which was previously disabled in qemu_init */ + rcu_enable_atfork(); + return 0; } diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 49075b55a1..fd4680590d 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1232,6 +1232,10 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine), qstr = qobject_to(QString, qobj); g_assert(qstr); mname = qstring_get_str(qstr); + /* Ignore machines that cannot be used for qtests */ + if (!memcmp("xenfv", mname, 5) || g_str_equal("xenpv", mname)) { + continue; + } if (!skip_old_versioned || !qtest_is_old_versioned_machine(mname)) { cb(mname); } diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index dc3490c9fa..21ea5ba1d2 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1211,7 +1211,7 @@ static void test_migrate_auto_converge(void) * without throttling. */ migrate_set_parameter_int(from, "downtime-limit", 1); - migrate_set_parameter_int(from, "max-bandwidth", 1000000); /* ~1Mb/s */ + migrate_set_parameter_int(from, "max-bandwidth", 100000000); /* ~100Mb/s */ /* To check remaining size after precopy */ migrate_set_capability(from, "pause-before-switchover", true); diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index e338a41194..1acf0d7369 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -81,11 +81,6 @@ static void add_machine_test_case(const char *mname) { char *path; - /* Ignore blacklisted machines that have known problems */ - if (!memcmp("xenfv", mname, 5) || g_str_equal("xenpv", mname)) { - return; - } - path = g_strdup_printf("qom/%s", mname); qtest_add_data_func(path, g_strdup(mname), test_machine); g_free(path); diff --git a/tests/qtest/test-hmp.c b/tests/qtest/test-hmp.c index b8b1271b9e..d5e7ebd176 100644 --- a/tests/qtest/test-hmp.c +++ b/tests/qtest/test-hmp.c @@ -143,11 +143,6 @@ static void add_machine_test_case(const char *mname) { char *path; - /* Ignore blacklisted machines that have known problems */ - if (!memcmp("xenfv", mname, 5) || g_str_equal("xenpv", mname)) { - return; - } - path = g_strdup_printf("hmp/%s", mname); qtest_add_data_func(path, g_strdup(mname), test_machine); g_free(path); @@ -271,6 +271,160 @@ const argtype *thunk_convert(void *dst, const void *src, return type_ptr; } +const argtype *thunk_print(void *arg, const argtype *type_ptr) +{ + int type; + + type = *type_ptr++; + + switch (type) { + case TYPE_CHAR: + qemu_log("%c", *(uint8_t *)arg); + break; + case TYPE_SHORT: + qemu_log("%" PRId16, tswap16(*(uint16_t *)arg)); + break; + case TYPE_INT: + qemu_log("%" PRId32, tswap32(*(uint32_t *)arg)); + break; + case TYPE_LONGLONG: + qemu_log("%" PRId64, tswap64(*(uint64_t *)arg)); + break; + case TYPE_ULONGLONG: + qemu_log("%" PRIu64, tswap64(*(uint64_t *)arg)); + break; +#if HOST_LONG_BITS == 32 && TARGET_ABI_BITS == 32 + case TYPE_PTRVOID: + qemu_log("0x%" PRIx32, tswap32(*(uint32_t *)arg)); + break; + case TYPE_LONG: + qemu_log("%" PRId32, tswap32(*(uint32_t *)arg)); + break; + case TYPE_ULONG: + qemu_log("%" PRIu32, tswap32(*(uint32_t *)arg)); + break; +#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 32 + case TYPE_PTRVOID: + qemu_log("0x%" PRIx32, tswap32(*(uint64_t *)arg & 0xffffffff)); + break; + case TYPE_LONG: + qemu_log("%" PRId32, tswap32(*(uint64_t *)arg & 0xffffffff)); + break; + case TYPE_ULONG: + qemu_log("%" PRIu32, tswap32(*(uint64_t *)arg & 0xffffffff)); + break; +#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64 + case TYPE_PTRVOID: + qemu_log("0x%" PRIx64, tswap64(*(uint64_t *)arg)); + break; + case TYPE_LONG: + qemu_log("%" PRId64, tswap64(*(uint64_t *)arg)); + break; + case TYPE_ULONG: + qemu_log("%" PRIu64, tswap64(*(uint64_t *)arg)); + break; +#else + case TYPE_PTRVOID: + qemu_log("0x%" PRIx64, tswap64(*(uint64_t *)arg)); + break; + case TYPE_LONG: + qemu_log("%" PRId64, tswap64(*(uint64_t *)arg)); + break; + case TYPE_ULONG: + qemu_log("%" PRIu64, tswap64(*(uint64_t *)arg)); + break; +#endif + case TYPE_OLDDEVT: + { + uint64_t val = 0; + switch (thunk_type_size(type_ptr - 1, 1)) { + case 2: + val = *(uint16_t *)arg; + break; + case 4: + val = *(uint32_t *)arg; + break; + case 8: + val = *(uint64_t *)arg; + break; + } + switch (thunk_type_size(type_ptr - 1, 0)) { + case 2: + qemu_log("%" PRIu16, tswap16(val)); + break; + case 4: + qemu_log("%" PRIu32, tswap32(val)); + break; + case 8: + qemu_log("%" PRIu64, tswap64(val)); + break; + } + } + break; + case TYPE_ARRAY: + { + int i, array_length, arg_size; + uint8_t *a; + int is_string = 0; + + array_length = *type_ptr++; + arg_size = thunk_type_size(type_ptr, 0); + a = arg; + + if (*type_ptr == TYPE_CHAR) { + qemu_log("\""); + is_string = 1; + } else { + qemu_log("["); + } + + for (i = 0; i < array_length; i++) { + if (i > 0 && !is_string) { + qemu_log(","); + } + thunk_print(a, type_ptr); + a += arg_size; + } + + if (is_string) { + qemu_log("\""); + } else { + qemu_log("]"); + } + + type_ptr = thunk_type_next(type_ptr); + } + break; + case TYPE_STRUCT: + { + int i; + const StructEntry *se; + uint8_t *a; + const argtype *field_types; + const int *arg_offsets; + + se = struct_entries + *type_ptr++; + a = arg; + + field_types = se->field_types; + arg_offsets = se->field_offsets[0]; + + qemu_log("{"); + for (i = 0; i < se->nb_fields; i++) { + if (i > 0) { + qemu_log(","); + } + field_types = thunk_print(a + arg_offsets[i], field_types); + } + qemu_log("}"); + } + break; + default: + g_assert_not_reached(); + } + return type_ptr; +} + /* from em86 */ /* Utility function: Table-driven functions to translate bitmasks diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c index 00a1ef666a..3105b6c23a 100644 --- a/tools/virtiofsd/helper.c +++ b/tools/virtiofsd/helper.c @@ -174,6 +174,8 @@ void fuse_cmdline_help(void) " default: no_writeback\n" " -o xattr|no_xattr enable/disable xattr\n" " default: no_xattr\n" + " -o modcaps=CAPLIST Modify the list of capabilities\n" + " e.g. -o modcaps=+sys_admin:-chown\n" " --rlimit-nofile=<num> set maximum number of file descriptors\n" " (0 leaves rlimit unchanged)\n" " default: min(1000000, fs.file-max - 16384)\n" diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 2ce7c96085..94e0de2d2b 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -145,6 +145,7 @@ struct lo_data { int posix_lock; int xattr; char *source; + char *modcaps; double timeout; int cache; int timeout_set; @@ -170,6 +171,7 @@ static const struct fuse_opt lo_opts[] = { { "no_posix_lock", offsetof(struct lo_data, posix_lock), 0 }, { "xattr", offsetof(struct lo_data, xattr), 1 }, { "no_xattr", offsetof(struct lo_data, xattr), 0 }, + { "modcaps=%s", offsetof(struct lo_data, modcaps), 0 }, { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, { "timeout=", offsetof(struct lo_data, timeout_set), 1 }, { "cache=none", offsetof(struct lo_data, cache), CACHE_NONE }, @@ -2570,9 +2572,11 @@ static void setup_mounts(const char *source) /* * Only keep whitelisted capabilities that are needed for file system operation + * The (possibly NULL) modcaps_in string passed in is free'd before exit. */ -static void setup_capabilities(void) +static void setup_capabilities(char *modcaps_in) { + char *modcaps = modcaps_in; pthread_mutex_lock(&cap.mutex); capng_restore_state(&cap.saved); @@ -2589,7 +2593,7 @@ static void setup_capabilities(void) */ capng_setpid(syscall(SYS_gettid)); capng_clear(CAPNG_SELECT_BOTH); - capng_updatev(CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, + if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, @@ -2598,10 +2602,67 @@ static void setup_capabilities(void) CAP_SETGID, CAP_SETUID, CAP_MKNOD, - CAP_SETFCAP); - capng_apply(CAPNG_SELECT_BOTH); + CAP_SETFCAP, + -1)) { + fuse_log(FUSE_LOG_ERR, "%s: capng_updatev failed\n", __func__); + exit(1); + } + + /* + * The modcaps option is a colon separated list of caps, + * each preceded by either + or -. + */ + while (modcaps) { + capng_act_t action; + int cap; + + char *next = strchr(modcaps, ':'); + if (next) { + *next = '\0'; + next++; + } + + switch (modcaps[0]) { + case '+': + action = CAPNG_ADD; + break; + + case '-': + action = CAPNG_DROP; + break; + + default: + fuse_log(FUSE_LOG_ERR, + "%s: Expecting '+'/'-' in modcaps but found '%c'\n", + __func__, modcaps[0]); + exit(1); + } + cap = capng_name_to_capability(modcaps + 1); + if (cap < 0) { + fuse_log(FUSE_LOG_ERR, "%s: Unknown capability '%s'\n", __func__, + modcaps); + exit(1); + } + if (capng_update(action, CAPNG_PERMITTED | CAPNG_EFFECTIVE, cap)) { + fuse_log(FUSE_LOG_ERR, "%s: capng_update failed for '%s'\n", + __func__, modcaps); + exit(1); + } + + modcaps = next; + } + g_free(modcaps_in); + + if (capng_apply(CAPNG_SELECT_BOTH)) { + fuse_log(FUSE_LOG_ERR, "%s: capng_apply failed\n", __func__); + exit(1); + } cap.saved = capng_save_state(); + if (!cap.saved) { + fuse_log(FUSE_LOG_ERR, "%s: capng_save_state failed\n", __func__); + exit(1); + } pthread_mutex_unlock(&cap.mutex); } @@ -2615,7 +2676,7 @@ static void setup_sandbox(struct lo_data *lo, struct fuse_session *se, setup_namespaces(lo, se); setup_mounts(lo->source); setup_seccomp(enable_syslog); - setup_capabilities(); + setup_capabilities(g_strdup(lo->modcaps)); } /* Set the maximum number of open file descriptors */ diff --git a/util/module.c b/util/module.c index e48d9aacc0..32b0547b82 100644 --- a/util/module.c +++ b/util/module.c @@ -245,3 +245,70 @@ bool module_load_one(const char *prefix, const char *lib_name) #endif return success; } + +/* + * Building devices and other qom objects modular is mostly useful in + * case they have dependencies to external shared libraries, so we can + * cut down the core qemu library dependencies. Which is the case for + * only a very few devices & objects. + * + * So with the expectation that this will be rather the exception than + * to rule and the list will not gain that many entries go with a + * simple manually maintained list for now. + */ +static struct { + const char *type; + const char *prefix; + const char *module; +} const qom_modules[] = { + { "ccid-card-passthru", "hw-", "usb-smartcard" }, + { "ccid-card-emulated", "hw-", "usb-smartcard" }, + { "usb-redir", "hw-", "usb-redirect" }, + { "qxl-vga", "hw-", "display-qxl" }, + { "qxl", "hw-", "display-qxl" }, + { "virtio-gpu-device", "hw-", "display-virtio-gpu" }, + { "virtio-gpu-pci", "hw-", "display-virtio-gpu" }, + { "virtio-vga", "hw-", "display-virtio-gpu" }, + { "vhost-user-gpu-device", "hw-", "display-virtio-gpu" }, + { "vhost-user-gpu-pci", "hw-", "display-virtio-gpu" }, + { "vhost-user-vga", "hw-", "display-virtio-gpu" }, + { "chardev-braille", "chardev-", "baum" }, +}; + +static bool module_loaded_qom_all; + +void module_load_qom_one(const char *type) +{ + int i; + + if (module_loaded_qom_all) { + return; + } + for (i = 0; i < ARRAY_SIZE(qom_modules); i++) { + if (strcmp(qom_modules[i].type, type) == 0) { + module_load_one(qom_modules[i].prefix, + qom_modules[i].module); + return; + } + } +} + +void module_load_qom_all(void) +{ + int i; + + if (module_loaded_qom_all) { + return; + } + for (i = 0; i < ARRAY_SIZE(qom_modules); i++) { + if (i > 0 && (strcmp(qom_modules[i - 1].module, + qom_modules[i].module) == 0 && + strcmp(qom_modules[i - 1].prefix, + qom_modules[i].prefix) == 0)) { + /* one module implementing multiple types -> load only once */ + continue; + } + module_load_one(qom_modules[i].prefix, qom_modules[i].module); + } + module_loaded_qom_all = true; +} diff --git a/util/qemu-openpty.c b/util/qemu-openpty.c index 2e8b43bdf5..4b8df96f38 100644 --- a/util/qemu-openpty.c +++ b/util/qemu-openpty.c @@ -52,7 +52,9 @@ #endif #ifdef __sun__ -/* Once Solaris has openpty(), this is going to be removed. */ + +#if !defined(HAVE_OPENPTY) +/* Once illumos has openpty(), this is going to be removed. */ static int openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) { @@ -93,6 +95,7 @@ err: close(mfd); return -1; } +#endif static void cfmakeraw (struct termios *termios_p) { |